iuna

iuna

iuna - experimental mainnet-candidate protocol
git clone https://getiuna.org/git/iuna.git
Log | Files | Refs | README | LICENSE

commit 8b476b4294f886ca36fa0afb745f439489808486
parent c4e72b586405c929cb29937fdc6e7944abd4b9ca
Author: Joris Hartog <jorishartog@hotmail.com>
Date:   Wed, 19 Aug 2026 21:29:48 +0200

Cover local chain reset persistence

Diffstat:
MPLAN.md | 4+++-
Msrc/adapters/http/actions.rs | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 99 insertions(+), 1 deletion(-)

diff --git a/PLAN.md b/PLAN.md @@ -132,7 +132,9 @@ Acceptance: Status: started with atomic JSON writes for config and wallet files, stale temp-file regression coverage, and rollback tests that keep the last committed -chain snapshot/UI projection after failed persistence work. +chain snapshot/UI projection after failed persistence work. Local chain reset now +has regression coverage that clears chain/UI persistence while preserving wallet +and config files. Goal: prove local persistence survives process death at bad moments. diff --git a/src/adapters/http/actions.rs b/src/adapters/http/actions.rs @@ -536,3 +536,99 @@ fn validate_address_book_name(name: String) -> Result<String> { } Ok(name) } + +#[cfg(test)] +mod tests { + use std::{ + collections::BTreeMap, + net::{IpAddr, Ipv4Addr, SocketAddr}, + sync::Arc, + }; + + use tempfile::tempdir; + use tokio::sync::Mutex; + + use crate::{ + adapters::{ + chain_store::SqliteChainStore, config_store, config_store::UiConfig, + p2p::GossipNetwork, ui_data_store::SqliteUiDataStore, wallet_store, + }, + app::{NodeCore, PeerBook, StratumStatus}, + domain::{GenesisBurn, Ledger, MICRO_IUNA}, + }; + + use super::super::{AuthSession, HttpState, UiChainCache, state::AuthBackoff}; + use super::{CHAIN_RESET_CONFIRMATION, reset_local_chain}; + + fn socket() -> SocketAddr { + SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9444) + } + + #[tokio::test] + async fn local_chain_reset_clears_chain_and_ui_without_touching_wallet_or_config() { + let dir = tempdir().unwrap(); + let wallet_path = dir.path().join("wallet.json"); + let config_path = dir.path().join("config.json"); + let chain_store = SqliteChainStore::open(dir.path().join("chain.sqlite3")).unwrap(); + let ui_data_store = SqliteUiDataStore::open(dir.path().join("ui.sqlite3")).unwrap(); + let wallet = wallet_store::replace_with_imported_seed_phrase( + &wallet_path, + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", + ) + .unwrap(); + let config = UiConfig { + setup_complete: true, + peers: vec!["127.0.0.1:9445".to_string()], + ..UiConfig::default() + }; + config_store::save(&config_path, &config).unwrap(); + let wallet_before = std::fs::read(&wallet_path).unwrap(); + let config_before = std::fs::read(&config_path).unwrap(); + + let mut allocations = BTreeMap::new(); + allocations.insert(wallet.address().to_string(), 10 * MICRO_IUNA); + let ledger = Ledger::new_with_genesis_burns( + allocations, + vec![GenesisBurn::new(wallet.address(), MICRO_IUNA)], + 1, + ) + .unwrap(); + let snapshot = ledger.snapshot(); + chain_store.save(&snapshot).unwrap(); + ui_data_store.project_snapshot(&snapshot, true).unwrap(); + + let node = Arc::new(Mutex::new(NodeCore::from_ledger(wallet, ledger, 0))); + let peers = Arc::new(Mutex::new(PeerBook::default())); + let gossip = GossipNetwork::start(node.clone(), peers.clone(), socket(), None, false) + .await + .unwrap(); + let state = HttpState { + node, + peers, + gossip, + ui_config: Arc::new(Mutex::new(config)), + config_path: config_path.clone(), + chain_store: chain_store.clone(), + ui_data_store: ui_data_store.clone(), + wallet_path: wallet_path.clone(), + stratum: StratumStatus { + enabled: false, + listen_addr: None, + }, + auth_sessions: Arc::new(Mutex::new(BTreeMap::<String, AuthSession>::new())), + auth_backoff: Arc::new(Mutex::new(BTreeMap::<String, AuthBackoff>::new())), + ui_cache: Arc::new(Mutex::new(UiChainCache::default())), + ui_data_refresh: Arc::new(Mutex::new(())), + }; + + reset_local_chain(&state, CHAIN_RESET_CONFIRMATION) + .await + .unwrap(); + + assert!(!state.node.lock().await.has_real_chain()); + assert!(chain_store.load().unwrap().is_none()); + assert!(ui_data_store.load_metrics().unwrap().is_empty()); + assert_eq!(std::fs::read(&wallet_path).unwrap(), wallet_before); + assert_eq!(std::fs::read(&config_path).unwrap(), config_before); + } +}