drk.rs 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504
  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 std::{
  19. collections::BTreeMap,
  20. io::Cursor,
  21. sync::{Arc, OnceLock, Weak},
  22. };
  23. use parking_lot::Mutex as SyncMutex;
  24. use smol::lock::RwLock;
  25. use url::Url;
  26. use darkfi::{
  27. blockchain::BlockInfo,
  28. rpc::{
  29. client::RpcClient,
  30. jsonrpc::{ErrorCode, JsonError, JsonRequest, JsonResult},
  31. util::JsonValue,
  32. },
  33. system::{sleep, Publisher, StoppableTask, StoppableTaskPtr},
  34. tx::Transaction,
  35. util::encoding::base64,
  36. Error as DarkFiError, Result as DarkFiResult,
  37. };
  38. use darkfi_money_contract::{
  39. model::TokenId, MONEY_CONTRACT_COIN_MERKLE_TREE, MONEY_CONTRACT_INFO_TREE,
  40. MONEY_CONTRACT_LATEST_COIN_ROOT,
  41. };
  42. use darkfi_sdk::crypto::{
  43. contract_id::MONEY_CONTRACT_ID,
  44. keypair::{Address, Network, PublicKey, StandardAddress},
  45. MerkleNode, MerkleTree,
  46. };
  47. use darkfi_serial::{deserialize, deserialize_async, serialize, Decodable, Encodable};
  48. use drk::{money::KVDB_MERKLE_TREES_MONEY, rpc::DarkfidRpcClient, Drk};
  49. use kvdb_overlay::Batch;
  50. use crate::{
  51. error::{Error, Result},
  52. prop::{PropertyEnum, Role},
  53. scene::{MethodCallSub, Pimpl, SceneNodePtr, SceneNodeWeak},
  54. ExecutorPtr,
  55. };
  56. const DARKFID_RETRY_TIME: u64 = 20;
  57. const BLOCK_BATCHES_BUFFER: usize = 3;
  58. const NEW_WALLET_INIT_RETRY_TIME: u64 = 5;
  59. #[cfg(target_os = "android")]
  60. mod paths {
  61. use crate::android::{get_appdata_path, get_external_storage_path};
  62. use std::path::PathBuf;
  63. pub fn get_cache_path() -> PathBuf {
  64. get_external_storage_path().join("drk/cache")
  65. }
  66. pub fn get_wallet_path() -> PathBuf {
  67. get_external_storage_path().join("drk/wallet.db")
  68. }
  69. pub fn get_use_tor_filename() -> PathBuf {
  70. get_external_storage_path().join("use_tor.txt")
  71. }
  72. }
  73. #[cfg(not(target_os = "android"))]
  74. mod paths {
  75. use std::path::PathBuf;
  76. pub fn get_cache_path() -> PathBuf {
  77. dirs::data_local_dir().unwrap().join("darkfi/app/drk/cache")
  78. }
  79. pub fn get_wallet_path() -> PathBuf {
  80. dirs::data_local_dir().unwrap().join("darkfi/app/drk/wallet.db")
  81. }
  82. pub fn get_use_tor_filename() -> PathBuf {
  83. dirs::data_local_dir().unwrap().join("darkfi/app/drk/use_tor.txt")
  84. }
  85. }
  86. use paths::*;
  87. macro_rules! t { ($($arg:tt)*) => { trace!(target: "plugin::drk", $($arg)*); } }
  88. macro_rules! d { ($($arg:tt)*) => { debug!(target: "plugin::drk", $($arg)*); } }
  89. macro_rules! i { ($($arg:tt)*) => { info!(target: "plugin::drk", $($arg)*); } }
  90. macro_rules! e { ($($arg:tt)*) => { error!(target: "plugin::drk", $($arg)*); } }
  91. #[derive(Debug, Clone)]
  92. struct TxState {
  93. id: Option<String>,
  94. confirmed: bool,
  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. net_transport: PropertyEnum,
  111. subscribe_task: SyncMutex<Option<StoppableTaskPtr>>,
  112. drk: Arc<RwLock<Drk>>,
  113. build_tx_channel: smol::channel::Sender<BuildTxRequest>,
  114. last_balances: SyncMutex<Option<Vec<(String, TokenId, u64)>>>,
  115. ex: ExecutorPtr,
  116. }
  117. impl DrkPlugin {
  118. pub async fn new(node: SceneNodeWeak, sg_root: SceneNodePtr, ex: ExecutorPtr) -> Result<Pimpl> {
  119. let setting_node = sg_root.lookup_node("/setting").unwrap();
  120. let net_transport =
  121. PropertyEnum::wrap(&setting_node, Role::Internal, "net.transport", 0).unwrap();
  122. let endpoint = Self::endpoint(&sg_root, &net_transport);
  123. i!("Using {endpoint} transport for darkfid connection");
  124. let drk = match Drk::new(
  125. Network::Testnet,
  126. get_cache_path().to_string_lossy().to_string(),
  127. get_wallet_path().to_string_lossy().to_string(),
  128. "changeme".to_string(),
  129. Some(endpoint.clone()),
  130. &ex,
  131. false,
  132. )
  133. .await
  134. {
  135. Ok(wallet) => wallet,
  136. Err(e) => {
  137. eprintln!("Error initializing wallet: {e}");
  138. return Err(Error::ServiceFailed); // TODO: make a better error
  139. }
  140. };
  141. if let Err(e) = drk.initialize_wallet().await {
  142. e!("Error initializing wallet: {e}");
  143. }
  144. let mut output = vec![];
  145. if let Err(e) = drk.initialize_money(&mut output).await {
  146. e!("Failed to initialize Money: {e}");
  147. }
  148. if let Err(e) = drk.initialize_dao().await {
  149. e!("Failed to initialize DAO: {e}");
  150. }
  151. if let Err(e) = drk.initialize_deployooor().await {
  152. e!("Failed to initialize Deployooor: {e}");
  153. }
  154. // Generate a default address if needed
  155. match drk.default_address().await {
  156. Ok(_) => {
  157. i!("Default address already exists");
  158. }
  159. Err(e) => {
  160. i!("No default address found ({}), generating one...", e);
  161. if let Err(e) = drk.money_keygen(&mut output).await {
  162. e!("Failed to generate keypair: {e}");
  163. } else {
  164. i!("Generated default address");
  165. match drk.addresses().await {
  166. Ok(addrs) => {
  167. if let Some((key_id, _, _, _)) = addrs.last() {
  168. i!("Setting address with key_id {} as default", key_id);
  169. if let Err(e) = drk.set_default_address(*key_id as u16).await {
  170. e!("Failed to set default address: {e}");
  171. }
  172. }
  173. }
  174. Err(e) => {
  175. e!("Failed to get addresses: {e}");
  176. }
  177. }
  178. }
  179. }
  180. }
  181. // Create channel for build_tx requests
  182. let (build_tx_tx, build_tx_rx) = smol::channel::unbounded();
  183. let self_ = Arc::new(Self {
  184. node: node.clone(),
  185. sg_root,
  186. tasks: OnceLock::new(),
  187. drk: drk.into_ptr(),
  188. build_tx_channel: build_tx_tx,
  189. net_transport,
  190. subscribe_task: SyncMutex::new(None),
  191. last_balances: SyncMutex::new(None),
  192. ex: ex.clone(),
  193. });
  194. // Start background task to process build_tx requests from channel
  195. let me3 = Arc::downgrade(&self_);
  196. let build_tx_processor = ex.spawn(async move {
  197. while let Ok(request) = build_tx_rx.recv().await {
  198. if let Some(self_) = me3.upgrade() {
  199. match self_.build_tx_request(request).await {
  200. Ok((tx, token_symbol, recipient, amount)) => {
  201. self_.emit_tx_built(amount, token_symbol, recipient, tx).await;
  202. }
  203. Err(e) => {
  204. e!("Failed to build transaction: {e}");
  205. self_.emit_tx_built_error(e.to_string()).await;
  206. }
  207. }
  208. }
  209. }
  210. });
  211. let node_ref = node.upgrade().unwrap();
  212. let me2 = Arc::downgrade(&self_);
  213. let method_sub = node_ref.subscribe_method_call("get_default_address").unwrap();
  214. let get_address_task =
  215. ex.spawn(
  216. async move { while Self::process_get_default_address(&me2, &method_sub).await {} },
  217. );
  218. let node_ref = node.upgrade().unwrap();
  219. let me2 = Arc::downgrade(&self_);
  220. let method_sub_balances = node_ref.subscribe_method_call("get_balances").unwrap();
  221. let get_balances_task = ex.spawn(async move {
  222. while Self::process_get_balances(&me2, &method_sub_balances).await {}
  223. });
  224. let node_ref = node.upgrade().unwrap();
  225. let me2 = Arc::downgrade(&self_);
  226. let method_sub_tx_status = node_ref.subscribe_method_call("get_tx_status").unwrap();
  227. let get_tx_status_task = ex.spawn(async move {
  228. while Self::process_get_tx_status(&me2, &method_sub_tx_status).await {}
  229. });
  230. let node_ref = node.upgrade().unwrap();
  231. let me2 = Arc::downgrade(&self_);
  232. let method_sub_build_tx = node_ref.subscribe_method_call("build_tx").unwrap();
  233. let build_tx_task =
  234. ex.spawn(
  235. async move { while Self::process_build_tx(&me2, &method_sub_build_tx).await {} },
  236. );
  237. let node_ref = node.upgrade().unwrap();
  238. let me2 = Arc::downgrade(&self_);
  239. let method_sub_broadcast_tx = node_ref.subscribe_method_call("broadcast_tx").unwrap();
  240. let broadcast_tx_task = ex.spawn(async move {
  241. while Self::process_broadcast_tx(&me2, &method_sub_broadcast_tx).await {}
  242. });
  243. let tasks = vec![
  244. get_address_task,
  245. get_balances_task,
  246. get_tx_status_task,
  247. build_tx_task,
  248. broadcast_tx_task,
  249. build_tx_processor,
  250. ];
  251. self_.clone().start(tasks).await;
  252. Ok(Pimpl::Drk(self_))
  253. }
  254. /// Endpoint for the darkfid daemon connection, derived from the
  255. /// `net.transport` and `wallet.*.rpc` settings
  256. fn endpoint(sg_root: &SceneNodePtr, net_transport: &PropertyEnum) -> Url {
  257. let setting_node = sg_root.lookup_node("/setting").unwrap();
  258. let prop = match net_transport.get().as_str() {
  259. "tor" => "wallet.tor.rpc",
  260. "tcp" => "wallet.tcp.rpc",
  261. unhandled => panic!("Unhandled net.transport value: {unhandled}"),
  262. };
  263. let endpoint = setting_node.get_property_str(prop).unwrap();
  264. Url::parse(&endpoint).unwrap()
  265. }
  266. pub async fn get_default_address(&self) -> Result<String> {
  267. let drk = self.drk.read().await;
  268. let pubkey = drk.default_address().await.map_err(|e| {
  269. e!("Failed to get default address: {e}");
  270. Error::ServiceFailed
  271. })?;
  272. let network = drk.network;
  273. let address: darkfi_sdk::crypto::keypair::Address =
  274. StandardAddress::from_public(network, pubkey).into();
  275. Ok(address.to_string())
  276. }
  277. pub async fn get_balances(&self) -> Result<Vec<(String, TokenId, u64)>> {
  278. let drk = self.drk.read().await;
  279. let balances = drk.money_balance().await.map_err(|e| {
  280. e!("Failed to get money balance: {e}");
  281. Error::ServiceFailed
  282. })?;
  283. let aliases = drk.get_aliases_mapped_by_token().await.map_err(|e| {
  284. e!("Failed to get aliases: {e}");
  285. Error::ServiceFailed
  286. })?;
  287. let mut result: Vec<(String, TokenId, u64)> = Vec::new();
  288. for (token_id_str, balance) in balances {
  289. let alias = aliases.get(&token_id_str).cloned().unwrap_or_else(|| "UNKN".to_string());
  290. let token_id = token_id_str.parse::<TokenId>().unwrap();
  291. result.push((alias, token_id, balance));
  292. }
  293. // Sort by balance
  294. result.sort_by(|a, b| b.2.partial_cmp(&a.2).unwrap_or(std::cmp::Ordering::Equal));
  295. Ok(result)
  296. }
  297. /// Fetch the Money contract's `info` state tree records from darkfid.
  298. async fn fetch_money_info(drk: &Drk) -> DarkFiResult<BTreeMap<Vec<u8>, Vec<u8>>> {
  299. let params = JsonValue::Array(vec![
  300. JsonValue::String(MONEY_CONTRACT_ID.to_string()),
  301. JsonValue::String(MONEY_CONTRACT_INFO_TREE.to_string()),
  302. ]);
  303. let rep = drk.darkfid_daemon_request("blockchain.get_contract_state", &params).await?;
  304. let rep_str = rep.get::<String>().ok_or_else(|| {
  305. DarkFiError::Custom(
  306. "Malformed reply: contract state result is not a string".to_string(),
  307. )
  308. })?;
  309. let bytes = base64::decode(rep_str).ok_or_else(|| {
  310. DarkFiError::Custom("Malformed reply: contract state is not valid base64".to_string())
  311. })?;
  312. let records: BTreeMap<Vec<u8>, Vec<u8>> = deserialize_async(&bytes).await?;
  313. Ok(records)
  314. }
  315. /// Initialize a brand-new wallet at the chain tip.
  316. async fn new_wallet_init(&self) -> DarkFiResult<()> {
  317. let drk = self.drk.read().await;
  318. // Only an empty wallet may be initialized here
  319. let (last_height, last_hash) = drk
  320. .get_last_scanned_block()
  321. .map_err(|e| DarkFiError::Custom(format!("Could not get last scanned block: {e}")))?;
  322. if last_height != 0 || last_hash != "-" || !drk.get_coins(true).await?.is_empty() {
  323. return Err(DarkFiError::Custom(
  324. "new_wallet_init called on a non-empty wallet".to_string(),
  325. ))
  326. }
  327. let (tip_height, tip_hash) = drk.get_last_confirmed_block().await?;
  328. if tip_height == 0 {
  329. // Genesis-only chain
  330. return Ok(())
  331. }
  332. // Tree and root come from one consistent state snapshot
  333. let records = Self::fetch_money_info(&drk).await?;
  334. // Decode the Money coin tree
  335. let tree_bytes = records.get(MONEY_CONTRACT_COIN_MERKLE_TREE).ok_or_else(|| {
  336. DarkFiError::Custom("Coin tree not found in contract state".to_string())
  337. })?;
  338. let (_, rest) = tree_bytes.split_at_checked(4).ok_or_else(|| {
  339. DarkFiError::Custom("Malformed coin tree record: missing set size prefix".to_string())
  340. })?;
  341. let tree: MerkleTree = deserialize(rest)?;
  342. let mut tree = MerkleTree::from_parts(
  343. tree.prior_bridges().to_vec(),
  344. tree.current_bridge().clone(),
  345. tree.marked_indices().clone(),
  346. tree.checkpoints().clone(),
  347. u32::MAX as usize,
  348. )
  349. .map_err(|_| DarkFiError::Custom("Invalid coin tree record".to_string()))?;
  350. let root_bytes = records.get(MONEY_CONTRACT_LATEST_COIN_ROOT).ok_or_else(|| {
  351. DarkFiError::Custom("Latest coin root not found in contract state".to_string())
  352. })?;
  353. let latest_root: MerkleNode = deserialize(root_bytes)?;
  354. if tree.root(0) != Some(latest_root) {
  355. return Err(DarkFiError::Custom(
  356. "Fetched coin tree root does not match the chain's latest coin root".to_string(),
  357. ))
  358. }
  359. // Re-read the tip in case the chain advanced between reads
  360. let (tip_height2, tip_hash2) = drk.get_last_confirmed_block().await?;
  361. if tip_height != tip_height2 || tip_hash != tip_hash2 {
  362. return Err(DarkFiError::Custom(format!(
  363. "Tip moved during sync ({tip_height} -> {tip_height2})"
  364. )))
  365. }
  366. // Make the snapshot point rewindable by `reset_to_height`
  367. tree.checkpoint(tip_height as usize);
  368. let mut merkle_batch = Batch::new();
  369. merkle_batch.insert(KVDB_MERKLE_TREES_MONEY, &serialize(&tree));
  370. let mut scanned_batch = Batch::new();
  371. scanned_batch
  372. .insert(&tip_height.to_be_bytes(), &serialize(&(tip_hash.clone(), "-".to_string())));
  373. drk.cache.kvdb.atomic_write(&[
  374. (&drk.cache.merkle_trees, &merkle_batch),
  375. (&drk.cache.scanned_blocks, &scanned_batch),
  376. ])?;
  377. drk.cache.kvdb.flush_default_mode()?;
  378. i!(
  379. "New wallet state initialized at height {tip_height}, scanning starts from {}",
  380. tip_height + 1
  381. );
  382. Ok(())
  383. }
  384. /// Decode a single base64-encoded serialized block entry from darkfid.
  385. async fn decode_block_param(param: &JsonValue) -> DarkFiResult<BlockInfo> {
  386. let param_str = param.get::<String>().ok_or_else(|| {
  387. DarkFiError::Custom("Malformed reply: block entry is not a string".to_string())
  388. })?;
  389. let bytes = base64::decode(param_str).ok_or_else(|| {
  390. DarkFiError::Custom("Malformed reply: block entry is not valid base64".to_string())
  391. })?;
  392. let block = deserialize_async(&bytes).await?;
  393. Ok(block)
  394. }
  395. /// Get a batch of blocks from darkfid starting at `height` using the provided client.
  396. async fn fetch_blocks(rpc_client: &RpcClient, height: u32) -> DarkFiResult<Vec<BlockInfo>> {
  397. let req = JsonRequest::new(
  398. "blockchain.get_blocks",
  399. JsonValue::Array(vec![JsonValue::Number(height as f64)]),
  400. );
  401. let rep = rpc_client.request(req).await?;
  402. let params_array = rep.get::<Vec<JsonValue>>().ok_or_else(|| {
  403. DarkFiError::Custom("Malformed reply: get_blocks result is not an array".to_string())
  404. })?;
  405. let mut blocks = Vec::with_capacity(params_array.len());
  406. for param in params_array {
  407. blocks.push(Self::decode_block_param(param).await?);
  408. }
  409. Ok(blocks)
  410. }
  411. /// Fetch and scan blocks starting from the last scanned block.
  412. async fn scan_blocks(&self) -> DarkFiResult<()> {
  413. let drk = self.drk.read().await;
  414. // Grab last scanned block height
  415. let (mut height, mut hash) = drk
  416. .get_last_scanned_block()
  417. .map_err(|e| DarkFiError::Custom(format!("Could not get last scanned block: {e}")))?;
  418. // Never resume past a height that has no tree checkpoint
  419. let anchor =
  420. drk.get_money_tree().await?.checkpoints().back().map(|c| *c.id() as u32).unwrap_or(0);
  421. if height > anchor {
  422. let mut batch = Batch::new();
  423. for h in anchor + 1..=height {
  424. batch.remove(&h.to_be_bytes());
  425. }
  426. drk.cache.kvdb.atomic_write(&[
  427. (&drk.cache.scanned_blocks, &batch),
  428. (&drk.cache.state_inverse_diff, &batch),
  429. ])?;
  430. height = anchor;
  431. hash = drk
  432. .get_scanned_block(&height)
  433. .map_err(|e| DarkFiError::Custom(format!("Could not get scanned block: {e}")))?
  434. .0;
  435. }
  436. // Grab our last scanned block from darkfid
  437. let block = match drk.get_block_by_height(height).await {
  438. Ok(b) => Some(b),
  439. Err(darkfi::Error::JsonRpcError((-32121, _))) => None,
  440. Err(e) => return Err(e),
  441. };
  442. // Check if a reorg has happened
  443. if !block.as_ref().is_some_and(|b| b.hash().to_string() == hash) {
  444. match self
  445. .handle_reorg(&drk, height)
  446. .await
  447. .map_err(|e| DarkFiError::Custom(format!("Could not find common ancestor: {e}")))?
  448. {
  449. // The wallet was reset and re-initialized
  450. None => return Ok(()),
  451. Some(h) => height = h,
  452. }
  453. }
  454. // If last scanned block is genesis(0) we reset,
  455. // otherwise continue with the next block height.
  456. if height == 0 {
  457. drk.reset(&mut vec![])
  458. .await
  459. .map_err(|e| DarkFiError::Custom(format!("Wallet state reset failed: {e}")))?;
  460. self.emit_balances_updated().await;
  461. } else {
  462. height += 1;
  463. }
  464. // The expected previous block hash
  465. let mut expected_prev = if height > 0 {
  466. Some(
  467. drk.get_scanned_block(&(height - 1))
  468. .map_err(|e| DarkFiError::Custom(format!("Could not get scanned block: {e}")))?
  469. .0,
  470. )
  471. } else {
  472. None
  473. };
  474. // Grab last confirmed block height
  475. let (mut last_height, _) = drk.get_last_confirmed_block().await?;
  476. drop(drk);
  477. // Already scanned last confirmed block
  478. if height > last_height {
  479. return Ok(())
  480. }
  481. // Generate a new scan cache
  482. let mut cache = self.drk.read().await.scan_cache(false).await?;
  483. // Save starting height to report progress
  484. let start_height = height;
  485. // Create RPC client for block fetching
  486. let endpoint = Self::endpoint(&self.sg_root, &self.net_transport);
  487. let rpc_client = match RpcClient::new(endpoint, self.ex.clone()).await {
  488. Ok(client) => client,
  489. Err(e) => return Err(DarkFiError::Custom(format!("Failed to create RPC client: {e}"))),
  490. };
  491. let rpc_client = Arc::new(rpc_client);
  492. let rpc_client_ = rpc_client.clone();
  493. // Bounded channel limits block fetches
  494. let (block_tx, block_rx) = smol::channel::bounded(BLOCK_BATCHES_BUFFER);
  495. // Fetcher task: fetches blocks continuously, blocks when channel is full
  496. let fetcher_task = StoppableTask::new();
  497. let fetcher_task_ = fetcher_task.clone();
  498. fetcher_task.start(
  499. async move {
  500. let mut fetch_height = height;
  501. while fetch_height <= last_height {
  502. match Self::fetch_blocks(&rpc_client_, fetch_height).await {
  503. Ok(blocks) => {
  504. let len = blocks.len() as u32;
  505. if block_tx.send(blocks).await.is_err() {
  506. // Channel closed, receiver is done
  507. break;
  508. }
  509. fetch_height += len;
  510. }
  511. Err(e) => {
  512. e!("Error fetching blocks: {e}");
  513. break;
  514. }
  515. }
  516. }
  517. Ok(())
  518. },
  519. |_result| async move {
  520. // Cleanup: stop the RPC client
  521. rpc_client.stop().await;
  522. },
  523. DarkFiError::DetachedTaskStopped,
  524. self.ex.clone(),
  525. );
  526. // Update scan progress
  527. self.emit_progress_update(height - start_height, last_height - start_height).await;
  528. // Process blocks from the channel until it closes
  529. let original_last_height = last_height;
  530. let mut processing_error = None;
  531. loop {
  532. let blocks = match block_rx.recv().await {
  533. Ok(b) => b,
  534. Err(e) => {
  535. // Channel closed: the fetcher finished or died early
  536. if height < original_last_height && processing_error.is_none() {
  537. processing_error = Some(DarkFiError::Custom(format!(
  538. "Block fetcher ended early at height {height} (expected up to {original_last_height}): {e}"
  539. )));
  540. }
  541. break;
  542. }
  543. };
  544. if blocks.is_empty() {
  545. processing_error = Some(DarkFiError::Custom(
  546. "Received empty block batch from fetcher".to_string(),
  547. ));
  548. break;
  549. }
  550. let old_nullifiers = cache.owncoins_nullifiers.clone();
  551. // Scan each block, verifying the batch chains to our scan state
  552. for block in blocks {
  553. let block_height = block.header.height;
  554. if let Some(prev) = &expected_prev {
  555. if &block.header.previous.to_string() != prev {
  556. processing_error = Some(DarkFiError::Custom(format!(
  557. "Non-contiguous block batch: block {block_height} does not chain to {prev}"
  558. )));
  559. break;
  560. }
  561. }
  562. expected_prev = Some(block.header.hash().to_string());
  563. let drk = self.drk.read().await;
  564. if let Err(e) = drk.scan_block(&mut cache, &block).await {
  565. processing_error = Some(e);
  566. break;
  567. }
  568. height += 1;
  569. }
  570. if height > last_height {
  571. last_height = height;
  572. }
  573. // Update balances if they changed
  574. if cache.owncoins_nullifiers != old_nullifiers {
  575. self.emit_balances_updated().await;
  576. }
  577. // Update scan progress
  578. self.emit_progress_update(height - start_height, last_height - start_height).await;
  579. // Break on processing error
  580. if processing_error.is_some() {
  581. break;
  582. }
  583. }
  584. // Stop the fetcher gracefully before dropping the receiver
  585. fetcher_task_.stop().await;
  586. // Drop the receiver (now that fetcher is stopped)
  587. drop(block_rx);
  588. // Return any processing error that occurred
  589. if let Some(e) = processing_error {
  590. return Err(e);
  591. }
  592. Ok(())
  593. }
  594. /// Handle a subscribed block. Returns `Ok(false)` when a below-floor
  595. /// reorg reset the wallet: the caller must abort the subscription.
  596. async fn handle_subscribed_block(
  597. &self,
  598. last_scanned_height: &mut u32,
  599. block: BlockInfo,
  600. ) -> DarkFiResult<bool> {
  601. i!("Received block {}", block.header.height);
  602. let drk = self.drk.read().await;
  603. if block.header.height <= *last_scanned_height {
  604. let reset_height = block.header.height.saturating_sub(1);
  605. // Can't handle a reorg to below the earliest wallet state
  606. if self.scanned_floor(&drk)?.is_some_and(|floor| reset_height < floor) {
  607. drop(drk);
  608. self.handle_full_reset().await;
  609. return Ok(false)
  610. }
  611. if let Err(e) = drk.reset_to_height(reset_height, &mut vec![]).await {
  612. return Err(DarkFiError::Custom(format!("Wallet state reset failed: {e}")))
  613. }
  614. // Scan genesis again if needed
  615. if reset_height == 0 {
  616. let genesis = match drk.get_block_by_height(reset_height).await {
  617. Ok(b) => b,
  618. Err(e) => {
  619. return Err(DarkFiError::Custom(format!("RPC client request failed: {e}")))
  620. }
  621. };
  622. let mut scan_cache = drk.scan_cache(false).await?;
  623. if let Err(e) = drk.scan_block(&mut scan_cache, &genesis).await {
  624. return Err(DarkFiError::Custom(format!("Scanning block failed: {e}")))
  625. };
  626. }
  627. }
  628. // The subscribed block must chain to our scanned state
  629. if block.header.height > 0 {
  630. let (prev_scanned_hash, _) =
  631. drk.get_scanned_block(&(block.header.height - 1)).map_err(|e| {
  632. DarkFiError::Custom(format!(
  633. "Missing scanned block record at height {}: {e}",
  634. block.header.height - 1
  635. ))
  636. })?;
  637. if prev_scanned_hash != block.header.previous.to_string() {
  638. return Err(DarkFiError::Custom(format!(
  639. "Subscribed block {} does not chain to scanned block {}",
  640. block.header.height,
  641. block.header.height - 1
  642. )))
  643. }
  644. }
  645. let mut scan_cache = drk.scan_cache(false).await?;
  646. let old_nullifiers = scan_cache.owncoins_nullifiers.clone();
  647. if let Err(e) = drk.scan_block(&mut scan_cache, &block).await {
  648. return Err(DarkFiError::Custom(format!("Scanning block failed: {e}")))
  649. }
  650. if scan_cache.owncoins_nullifiers != old_nullifiers {
  651. self.emit_balances_updated().await;
  652. }
  653. // Set new last scanned block height
  654. *last_scanned_height = block.header.height;
  655. Ok(true)
  656. }
  657. /// Scan until the scanned tip matches darkfid's confirmed tip.
  658. /// Returns the scanned tip, used as the subscription baseline.
  659. async fn catch_up_to_tip(&self) -> DarkFiResult<u32> {
  660. loop {
  661. // First we do a clean scan
  662. if let Err(e) = self.scan_blocks().await {
  663. return Err(DarkFiError::Custom(format!("Failed during scanning: {e}")))
  664. }
  665. // Grab last confirmed block
  666. let drk = self.drk.read().await;
  667. let (last_confirmed_height, last_confirmed_hash) =
  668. drk.get_last_confirmed_block().await?;
  669. // Grab last scanned block
  670. let (last_scanned_height, last_scanned_hash) = match drk.get_last_scanned_block() {
  671. Ok(last) => last,
  672. Err(e) => {
  673. return Err(DarkFiError::Custom(format!(
  674. "Retrieving last scanned block failed: {e}"
  675. )))
  676. }
  677. };
  678. drop(drk);
  679. // The wallet was fully reset
  680. if last_scanned_height == 0 && last_scanned_hash == "-" {
  681. return Err(DarkFiError::Custom("Wallet was reset during catch-up".to_string()))
  682. }
  683. // Rescan if other blocks have been created while we were scanning
  684. if last_confirmed_height != last_scanned_height ||
  685. last_confirmed_hash != last_scanned_hash
  686. {
  687. continue
  688. }
  689. return Ok(last_scanned_height)
  690. }
  691. }
  692. /// Call catch_up_to_tip() then subscribe to blocks from darkfid.
  693. async fn subscribe_blocks(&self, rpc_task: StoppableTaskPtr) -> DarkFiResult<()> {
  694. // Brand-new wallets (nothing scanned, no coins) are inited by
  695. // fetching the current money tree from darkfid.
  696. let drk = self.drk.read().await;
  697. let (last_height, last_hash) = drk
  698. .get_last_scanned_block()
  699. .map_err(|e| DarkFiError::Custom(format!("Could not get last scanned block: {e}")))?;
  700. let is_new_wallet =
  701. last_height == 0 && last_hash == "-" && drk.get_coins(true).await?.is_empty();
  702. drop(drk);
  703. if is_new_wallet {
  704. i!("New wallet detected, initializing state at the chain tip...");
  705. loop {
  706. if let Err(e) = self.new_wallet_init().await {
  707. e!("New wallet init attempt failed: {e}");
  708. i!("Retrying new wallet init in {NEW_WALLET_INIT_RETRY_TIME} seconds...");
  709. sleep(NEW_WALLET_INIT_RETRY_TIME).await;
  710. continue
  711. }
  712. break
  713. }
  714. }
  715. let mut last_scanned_height = self.catch_up_to_tip().await?;
  716. let publisher = Publisher::new();
  717. let subscription = publisher.clone().subscribe().await;
  718. let _publisher = publisher.clone();
  719. let rpc_client = Arc::new(
  720. RpcClient::new(Self::endpoint(&self.sg_root, &self.net_transport), self.ex.clone())
  721. .await?,
  722. );
  723. let rpc_client_ = rpc_client.clone();
  724. rpc_task.clone().start(
  725. async move {
  726. let req = JsonRequest::new("blockchain.subscribe_blocks", JsonValue::Array(vec![]));
  727. rpc_client_.subscribe(req, _publisher).await
  728. },
  729. |res| async move {
  730. rpc_client.stop().await;
  731. match res {
  732. Ok(()) |
  733. Err(DarkFiError::DetachedTaskStopped) |
  734. Err(DarkFiError::RpcServerStopped) => { /* Do nothing */ }
  735. Err(e) => {
  736. e!("JSON-RPC server error: {e}");
  737. publisher
  738. .notify(JsonResult::Error(JsonError::new(
  739. ErrorCode::InternalError,
  740. None,
  741. 0,
  742. )))
  743. .await;
  744. }
  745. }
  746. },
  747. DarkFiError::RpcServerStopped,
  748. self.ex.clone(),
  749. );
  750. // Set the blockchain indicator to fully scanned status and remove the progress text
  751. if let Some(node) = self.node.upgrade() {
  752. let _ = node.trigger("connect", serialize(&(3u8, String::new()))).await;
  753. }
  754. // Wait for blocks from darkfid
  755. i!("Blockchain is fully scanned, waiting for blocks...");
  756. let e = 'outer: loop {
  757. match subscription.receive().await {
  758. JsonResult::Notification(n) => {
  759. if n.method != "blockchain.subscribe_blocks" {
  760. break DarkFiError::UnexpectedJsonRpc(format!(
  761. "Got foreign notification from darkfid: {}",
  762. n.method
  763. ))
  764. }
  765. // Verify parameters
  766. if !n.params.is_array() {
  767. break DarkFiError::UnexpectedJsonRpc(
  768. "Received notification params are not an array".to_string(),
  769. )
  770. }
  771. let Some(params) = n.params.get::<Vec<JsonValue>>() else {
  772. break DarkFiError::UnexpectedJsonRpc(
  773. "Notification parameters are not a JSON array".to_string(),
  774. )
  775. };
  776. if params.is_empty() {
  777. break DarkFiError::UnexpectedJsonRpc(
  778. "Notification parameters are empty".to_string(),
  779. )
  780. }
  781. for param in params {
  782. let block = match Self::decode_block_param(param).await {
  783. Ok(b) => b,
  784. Err(e) => break 'outer e,
  785. };
  786. match self.handle_subscribed_block(&mut last_scanned_height, block).await {
  787. Ok(true) => continue,
  788. // Below-floor reorg reset the wallet
  789. Ok(false) => {
  790. rpc_task.stop().await;
  791. return Ok(())
  792. }
  793. Err(e) => break 'outer e,
  794. }
  795. }
  796. }
  797. JsonResult::Error(e) => {
  798. // Some error happened in the transmission
  799. break DarkFiError::UnexpectedJsonRpc(format!("Got error from JSON-RPC: {e:?}"))
  800. }
  801. x => {
  802. // And this is weird
  803. break DarkFiError::UnexpectedJsonRpc(format!(
  804. "Got unexpected data from JSON-RPC: {x:?}"
  805. ))
  806. }
  807. }
  808. };
  809. rpc_task.stop().await;
  810. Err(e)
  811. }
  812. /// Lowest height whose wallet state we possess: the earliest scanned
  813. /// block record. `None` when nothing was ever scanned.
  814. fn scanned_floor(&self, drk: &Drk) -> DarkFiResult<Option<u32>> {
  815. let records = drk.get_scanned_block_records().map_err(|e| {
  816. DarkFiError::Custom(format!("Could not get scanned block records: {e}"))
  817. })?;
  818. Ok(records.iter().map(|(height, _, _)| *height).min())
  819. }
  820. /// Full reset after a reorg below the earliest wallet state: wipe
  821. /// everything and re-initialize at the current tip.
  822. async fn handle_full_reset(&self) {
  823. e!("Reorg reached below the earliest wallet state, resetting and re-initializing");
  824. loop {
  825. let drk = self.drk.read().await;
  826. let result = drk.reset(&mut vec![]).await;
  827. drop(drk);
  828. self.emit_balances_updated().await;
  829. match result {
  830. Ok(()) => break,
  831. Err(e) => {
  832. e!("Wallet reset attempt failed: {e}");
  833. i!("Retrying wallet reset in {NEW_WALLET_INIT_RETRY_TIME} seconds...");
  834. sleep(NEW_WALLET_INIT_RETRY_TIME).await;
  835. }
  836. }
  837. }
  838. loop {
  839. if let Err(e) = self.new_wallet_init().await {
  840. e!("New wallet init attempt failed: {e}");
  841. i!("Retrying new wallet init in {NEW_WALLET_INIT_RETRY_TIME} seconds...");
  842. sleep(NEW_WALLET_INIT_RETRY_TIME).await;
  843. continue
  844. }
  845. break
  846. }
  847. }
  848. /// Find the exact block height the reorg happened. If it happened below
  849. /// the earliest wallet state, the wallet is fully reset and re-initialized
  850. /// at the current tip, and `Ok(None)` is returned.
  851. async fn handle_reorg(&self, drk: &Drk, mut height: u32) -> DarkFiResult<Option<u32>> {
  852. i!("Reorg detected, finding common ancestor...");
  853. let floor = self.scanned_floor(drk)?;
  854. let start_height = height;
  855. height = height.saturating_sub(1);
  856. while height != 0 {
  857. // Below the earliest wallet state
  858. if floor.is_some_and(|floor| height < floor) {
  859. self.handle_full_reset().await;
  860. return Ok(None)
  861. }
  862. // Grab our scanned block hash for that height
  863. let (scanned_block_hash, _) =
  864. drk.get_scanned_block(&height).map_err(|e| DarkFiError::Custom(e.to_string()))?;
  865. // Grab the block from darkfid for that height
  866. let block = match drk.get_block_by_height(height).await {
  867. Ok(b) => Some(b),
  868. // Check if block was found
  869. Err(DarkFiError::JsonRpcError((-32121, _))) => None,
  870. Err(e) => {
  871. e!("Error getting block {height} while finding common ancestor: {e}");
  872. return Err(DarkFiError::Custom(e.to_string()))
  873. }
  874. };
  875. // Continue to previous one if they don't match
  876. if !block.as_ref().is_some_and(|b| b.hash().to_string() == scanned_block_hash) {
  877. height = height.saturating_sub(1);
  878. continue
  879. }
  880. // Reset to its height
  881. drk.reset_to_height(height, &mut vec![])
  882. .await
  883. .map_err(|e| DarkFiError::Custom(e.to_string()))?;
  884. self.emit_balances_updated().await;
  885. break
  886. }
  887. i!("Found common ancestor: {height} ({}-block reorg)", start_height - height);
  888. Ok(Some(height))
  889. }
  890. /// Emit balances_updated signal with the balances encoded in the payload.
  891. /// Only emits when the encoded balances differ from the last emitted ones.
  892. async fn emit_balances_updated(&self) {
  893. let Some(node) = self.node.upgrade() else { return };
  894. let balances = match self.get_balances().await {
  895. Ok(b) => b,
  896. Err(e) => {
  897. e!("Failed to get balances for balances_updated signal: {e}");
  898. return
  899. }
  900. };
  901. let mut data = vec![];
  902. if let Err(e) = balances.encode(&mut data) {
  903. e!("Failed to encode balances for balances_updated signal: {e}");
  904. return
  905. }
  906. let mut last = self.last_balances.lock();
  907. if let Some(last) = &*last {
  908. if *last == balances {
  909. return
  910. }
  911. }
  912. *last = Some(balances);
  913. let _ = node.trigger("balances_updated", data).await;
  914. }
  915. /// Emit a progress update
  916. async fn emit_progress_update(&self, blocks_scanned: u32, total_blocks: u32) {
  917. let Some(node) = self.node.upgrade() else { return };
  918. let percentage = if total_blocks > 0 { blocks_scanned * 100 / total_blocks } else { 0 };
  919. let status = if percentage > 50 { 2u8 } else { 1u8 };
  920. let _ = node
  921. .trigger(
  922. "connect",
  923. serialize(&(status, format!("{blocks_scanned}/{total_blocks} [{percentage}%]"))),
  924. )
  925. .await;
  926. }
  927. /// Emit tx_updated signal
  928. async fn emit_tx_updated(&self, state: &TxState) {
  929. if let Some(node) = self.node.upgrade() {
  930. let mut data = vec![];
  931. state.id.clone().encode(&mut data).unwrap();
  932. Some(state.confirmed).encode(&mut data).unwrap();
  933. state.amount.encode(&mut data).unwrap();
  934. state.token_symbol.clone().encode(&mut data).unwrap();
  935. state.recipient.map(|r| r.to_string()).encode(&mut data).unwrap();
  936. let _ = node.trigger("tx_updated", data).await;
  937. }
  938. }
  939. async fn emit_tx_status_updated(&self, confirmed: bool) {
  940. if let Some(node) = self.node.upgrade() {
  941. let mut data = vec![];
  942. None::<String>.encode(&mut data).unwrap();
  943. Some(confirmed).encode(&mut data).unwrap();
  944. None::<String>.encode(&mut data).unwrap();
  945. None::<String>.encode(&mut data).unwrap();
  946. None::<String>.encode(&mut data).unwrap();
  947. let _ = node.trigger("tx_updated", data).await;
  948. }
  949. }
  950. /// Emit tx_built signal when transaction is built
  951. async fn emit_tx_built(
  952. &self,
  953. amount: String,
  954. token_symbol: String,
  955. recipient: Address,
  956. tx: Transaction,
  957. ) {
  958. if let Some(node) = self.node.upgrade() {
  959. let mut data = vec![];
  960. amount.encode(&mut data).unwrap();
  961. token_symbol.encode(&mut data).unwrap();
  962. recipient.to_string().encode(&mut data).unwrap();
  963. tx.encode(&mut data).unwrap();
  964. let _ = node.trigger("tx_built", data).await;
  965. }
  966. }
  967. /// Emit tx_built_error signal when transaction building fails
  968. async fn emit_tx_built_error(&self, error: String) {
  969. if let Some(node) = self.node.upgrade() {
  970. let mut data = vec![];
  971. error.encode(&mut data).unwrap();
  972. let _ = node.trigger("tx_built_error", data).await;
  973. }
  974. }
  975. async fn process_get_default_address(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  976. let Ok(method_call) = sub.receive().await else {
  977. d!("get_default_address method closed");
  978. return false
  979. };
  980. t!("method called: get_default_address()");
  981. let Some(self_) = me.upgrade() else {
  982. e!("drk plugin destroyed before get_default_address task was stopped!");
  983. if let Some(send_res) = method_call.send_res {
  984. let _ = send_res.send(vec![]).await;
  985. }
  986. return false
  987. };
  988. let address = match self_.get_default_address().await {
  989. Ok(addr) => addr,
  990. Err(e) => {
  991. e!("Failed to get default address: {e}");
  992. if let Some(send_res) = method_call.send_res {
  993. let _ = send_res.send(vec![]).await;
  994. }
  995. return true
  996. }
  997. };
  998. i!("Got default address: {address}");
  999. if let Some(send_res) = method_call.send_res {
  1000. let mut cur = Cursor::new(vec![]);
  1001. if address.encode(&mut cur).is_ok() {
  1002. let _ = send_res.send(cur.into_inner()).await;
  1003. } else {
  1004. e!("Failed to encode default address");
  1005. let _ = send_res.send(vec![]).await;
  1006. }
  1007. } else {
  1008. e!("No send_res channel available");
  1009. }
  1010. true
  1011. }
  1012. async fn process_get_balances(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  1013. let Ok(method_call) = sub.receive().await else {
  1014. d!("get_balances method closed");
  1015. return false
  1016. };
  1017. t!("method called: get_balances()");
  1018. let Some(self_) = me.upgrade() else {
  1019. e!("drk plugin destroyed before get_balances task was stopped!");
  1020. if let Some(send_res) = method_call.send_res {
  1021. let _ = send_res.send(vec![]).await;
  1022. }
  1023. return false
  1024. };
  1025. let balances = match self_.get_balances().await {
  1026. Ok(b) => b,
  1027. Err(e) => {
  1028. e!("Failed to get balances: {e}");
  1029. if let Some(send_res) = method_call.send_res {
  1030. let _ = send_res.send(vec![]).await;
  1031. }
  1032. return true
  1033. }
  1034. };
  1035. if let Some(send_res) = method_call.send_res {
  1036. let mut cur = Cursor::new(vec![]);
  1037. if balances.encode(&mut cur).is_ok() {
  1038. let _ = send_res.send(cur.into_inner()).await;
  1039. } else {
  1040. e!("Failed to encode balances");
  1041. let _ = send_res.send(vec![]).await;
  1042. }
  1043. } else {
  1044. e!("No send_res channel available");
  1045. }
  1046. true
  1047. }
  1048. async fn process_get_tx_status(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  1049. let Ok(method_call) = sub.receive().await else {
  1050. d!("get_tx_status method closed");
  1051. return false
  1052. };
  1053. t!("method called: get_tx_status()");
  1054. fn decode_data(data: &[u8]) -> std::io::Result<String> {
  1055. let mut cur = Cursor::new(&data);
  1056. let tx_id = String::decode(&mut cur)?;
  1057. Ok(tx_id)
  1058. }
  1059. let Ok(tx_id) = decode_data(&method_call.data) else {
  1060. d!("get_tx_status() method invalid arg data");
  1061. return true
  1062. };
  1063. let Some(self_) = me.upgrade() else {
  1064. e!("drk plugin destroyed before get_tx_status task was stopped!");
  1065. if let Some(send_res) = method_call.send_res {
  1066. let _ = send_res.send(vec![]).await;
  1067. }
  1068. return false
  1069. };
  1070. let drk = self_.drk.read().await;
  1071. let Ok((_, status, _block_height, _tx)) = drk.get_tx_history_record(&tx_id).await else {
  1072. d!("get_tx_history() method failed to get tx history record");
  1073. return true
  1074. };
  1075. if let Some(send_res) = method_call.send_res {
  1076. let mut cur = Cursor::new(vec![]);
  1077. let confirmed = status.as_str() == "Confirmed";
  1078. if confirmed.encode(&mut cur).is_ok() {
  1079. let _ = send_res.send(cur.into_inner()).await;
  1080. } else {
  1081. e!("Failed to encode tx status");
  1082. let _ = send_res.send(vec![]).await;
  1083. }
  1084. } else {
  1085. e!("No send_res channel available");
  1086. }
  1087. true
  1088. }
  1089. /// Build a transaction without broadcasting it
  1090. pub async fn build_tx(
  1091. &self,
  1092. amount: &str,
  1093. token_id: TokenId,
  1094. recipient: PublicKey,
  1095. ) -> DarkFiResult<Transaction> {
  1096. let drk = self.drk.read().await;
  1097. drk.transfer(amount, token_id, recipient, None, None, false).await
  1098. }
  1099. /// Build a transaction from a BuildTxRequest (called by background task)
  1100. async fn build_tx_request(
  1101. &self,
  1102. request: BuildTxRequest,
  1103. ) -> DarkFiResult<(Transaction, String, Address, String)> {
  1104. let drk = self.drk.read().await;
  1105. let aliases = drk.get_aliases_mapped_by_token().await.unwrap_or_default();
  1106. let token_symbol =
  1107. aliases.get(&request.token_id.to_string()).unwrap_or(&"UNKN".to_string()).to_string();
  1108. let recipient: Address =
  1109. StandardAddress::from_public(drk.network, request.recipient).into();
  1110. let tx = self.build_tx(&request.amount, request.token_id, request.recipient).await?;
  1111. Ok((tx, token_symbol, recipient, request.amount))
  1112. }
  1113. async fn process_build_tx(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  1114. let Ok(method_call) = sub.receive().await else {
  1115. d!("build_tx method closed");
  1116. return false
  1117. };
  1118. t!("method called: build_tx()");
  1119. // Send empty response immediately to unblock the caller
  1120. if let Some(send_res) = method_call.send_res {
  1121. let _ = send_res.send(vec![]).await;
  1122. }
  1123. fn decode_data(data: &[u8]) -> std::io::Result<(String, TokenId, PublicKey)> {
  1124. let mut cur = Cursor::new(&data);
  1125. let amount = String::decode(&mut cur)?;
  1126. let token_id = TokenId::decode(&mut cur)?;
  1127. let recipient = PublicKey::decode(&mut cur)?;
  1128. Ok((amount, token_id, recipient))
  1129. }
  1130. let Ok((amount, token_id, recipient)) = decode_data(&method_call.data) else {
  1131. d!("build_tx() method invalid arg data");
  1132. return true
  1133. };
  1134. let Some(self_) = me.upgrade() else {
  1135. e!("drk plugin destroyed before build_tx task was stopped!");
  1136. return false
  1137. };
  1138. // Send request to channel for background processing
  1139. let request = BuildTxRequest { amount, token_id, recipient };
  1140. let _ = self_.build_tx_channel.send(request).await;
  1141. true
  1142. }
  1143. /// Broadcast a transaction
  1144. pub async fn broadcast_tx(&self, tx: Transaction) -> Result<String> {
  1145. let drk = self.drk.read().await;
  1146. let tx_id = drk.broadcast_tx(&tx, &mut vec![]).await.map_err(|e| {
  1147. e!("Failed to broadcast transaction: {e}");
  1148. Error::ServiceFailed
  1149. })?;
  1150. Ok(tx_id)
  1151. }
  1152. async fn process_broadcast_tx(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
  1153. let Ok(method_call) = sub.receive().await else {
  1154. d!("broadcast_tx method closed");
  1155. return false
  1156. };
  1157. t!("method called: broadcast_tx()");
  1158. let Some(send_res) = method_call.send_res else { return true };
  1159. // Send empty response immediately to unblock the caller
  1160. let _ = send_res.send(vec![]).await;
  1161. let Some(self_) = me.upgrade() else {
  1162. e!("drk plugin destroyed before broadcast_tx task was stopped!");
  1163. return false
  1164. };
  1165. let Ok(tx) = Transaction::decode(&mut Cursor::new(&method_call.data)) else {
  1166. d!("broadcast_tx() method invalid arg data");
  1167. return true
  1168. };
  1169. let drk = self_.drk.read().await;
  1170. if let Err(e) = drk.mark_tx_spend(&tx, &mut vec![]).await {
  1171. e!("Failed to mark transaction coins as spent: {e}");
  1172. self_.emit_tx_status_updated(false).await;
  1173. return true
  1174. };
  1175. let tx_id = match drk.broadcast_tx(&tx, &mut vec![]).await {
  1176. Ok(t) => t,
  1177. Err(e) => {
  1178. e!("Failed to broadcast transaction: {e}");
  1179. self_.emit_tx_status_updated(false).await;
  1180. return true
  1181. }
  1182. };
  1183. drop(drk);
  1184. let state = TxState {
  1185. id: Some(tx_id),
  1186. confirmed: false,
  1187. amount: None,
  1188. token_symbol: None,
  1189. recipient: None,
  1190. };
  1191. self_.emit_tx_updated(&state).await;
  1192. self_.emit_balances_updated().await;
  1193. true
  1194. }
  1195. /// `net.transport` or one of the `wallet.*.rpc` endpoint settings was
  1196. /// changed: cancel the scan/subscribe session, stop drk's RPC client,
  1197. /// and swap it over to the recomputed endpoint. The subscribe loop in
  1198. /// `Self::start()` sees the dead session and reconnects immediately.
  1199. async fn restart_rpc_connection(&self) {
  1200. let endpoint = Self::endpoint(&self.sg_root, &self.net_transport);
  1201. i!("Restarting darkfid connection at {endpoint}");
  1202. // Cancel the scan/subscribe task
  1203. let subscribe_task = self.subscribe_task.lock().take();
  1204. if let Some(subscribe_task) = &subscribe_task {
  1205. subscribe_task.stop_nowait();
  1206. }
  1207. // Stop drk's rpc client
  1208. let old_client = self.drk.write().await.rpc_client.take();
  1209. if let Some(old_client) = old_client {
  1210. old_client.read().await.stop().await;
  1211. }
  1212. // Swap drk's rpc client over to the new endpoint
  1213. let new_client = DarkfidRpcClient::new(endpoint, self.ex.clone()).await;
  1214. self.drk.write().await.rpc_client = Some(RwLock::new(new_client));
  1215. }
  1216. async fn start(self: Arc<Self>, tasks: Vec<smol::Task<()>>) {
  1217. let me2 = Arc::downgrade(&self);
  1218. let subscribe_task = self.ex.spawn(async move {
  1219. loop {
  1220. let Some(self2) = me2.upgrade() else { break };
  1221. let endpoint = Self::endpoint(&self2.sg_root, &self2.net_transport);
  1222. i!("Attempting to connect to darkfid daemon at {}", endpoint);
  1223. let subscribe_rpc_task = StoppableTask::new();
  1224. let subscribe_rpc_task_ = subscribe_rpc_task.clone();
  1225. if let Some(node) = self2.node.upgrade() {
  1226. let _ = node.trigger("connect", serialize(&(0u8, String::new()))).await;
  1227. }
  1228. let task = StoppableTask::new();
  1229. *self2.subscribe_task.lock() = Some(task.clone());
  1230. let (task_res_tx, task_res_rx) = smol::channel::bounded(1);
  1231. let self3 = self2.clone();
  1232. task.clone().start(
  1233. async move {
  1234. let res = self3.subscribe_blocks(subscribe_rpc_task).await;
  1235. let _ = task_res_tx.send(res).await;
  1236. Ok(())
  1237. },
  1238. |res| async move {
  1239. match res {
  1240. Ok(()) | Err(DarkFiError::DetachedTaskStopped) => {}
  1241. Err(e) => {
  1242. e!("Session task error: {e}");
  1243. }
  1244. }
  1245. },
  1246. DarkFiError::DetachedTaskStopped,
  1247. self2.ex.clone(),
  1248. );
  1249. let res = match task_res_rx.recv().await {
  1250. Ok(res) => res,
  1251. Err(_) => {
  1252. i!("Subscribe task cancelled");
  1253. Err(DarkFiError::DetachedTaskStopped)
  1254. }
  1255. };
  1256. subscribe_rpc_task_.stop_nowait();
  1257. let reset = match res {
  1258. Ok(()) => true,
  1259. Err(e) => {
  1260. e!("Failed during drk scanning: {e}");
  1261. false
  1262. }
  1263. };
  1264. *self2.subscribe_task.lock() = None;
  1265. if let Some(node) = self2.node.upgrade() {
  1266. let _ = node.trigger("connect", serialize(&(0u8, String::new()))).await;
  1267. }
  1268. // Endpoint changed while we were connected, no need to sleep
  1269. if Self::endpoint(&self2.sg_root, &self2.net_transport) != endpoint {
  1270. continue
  1271. }
  1272. if !reset {
  1273. i!("Retrying connection to darkfid in {} seconds...", DARKFID_RETRY_TIME);
  1274. sleep(DARKFID_RETRY_TIME).await;
  1275. }
  1276. }
  1277. });
  1278. // Watch the settings feeding the darkfid endpoint. Changing any of
  1279. // them restarts the RPC connection at the recomputed endpoint.
  1280. let setting_node = self.sg_root.lookup_node("/setting").unwrap();
  1281. let watch_props = vec![
  1282. self.net_transport.prop(),
  1283. setting_node.get_property("wallet.tcp.rpc").unwrap(),
  1284. setting_node.get_property("wallet.tor.rpc").unwrap(),
  1285. ];
  1286. let mut watch_tasks = vec![];
  1287. for prop in watch_props {
  1288. let me = Arc::downgrade(&self);
  1289. let name = prop.name.clone();
  1290. let sub = prop.subscribe_modify();
  1291. let task = self.ex.spawn(async move {
  1292. while let Ok(_) = sub.receive().await {
  1293. let Some(me2) = me.upgrade() else { break };
  1294. i!("Setting {name} changed, restarting darkfid connection");
  1295. me2.restart_rpc_connection().await;
  1296. }
  1297. });
  1298. watch_tasks.push(task);
  1299. }
  1300. let mut all_tasks = vec![subscribe_task];
  1301. all_tasks.extend(watch_tasks);
  1302. all_tasks.extend(tasks);
  1303. self.tasks.set(all_tasks).unwrap();
  1304. }
  1305. }