drk.rs 56 KB

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