iuna

iuna - experimental devnet protocol
git clone https://iuna.jhx.app/git/iuna.git
Log | Files | Refs | README | LICENSE

tests.rs (7308B)


      1 use std::collections::BTreeMap;
      2 
      3 use tempfile::tempdir;
      4 
      5 use crate::domain::{GenesisBurn, Ledger, Wallet, run_vdf};
      6 
      7 use super::{SqliteChainStore, decode_compact_snapshot, encode_compact_snapshot};
      8 
      9 #[test]
     10 fn sqlite_chain_store_roundtrips_snapshot() {
     11     let dir = tempdir().unwrap();
     12     let store = SqliteChainStore::open(dir.path().join("nested/chain.sqlite3")).unwrap();
     13     let wallet = Wallet::from_seed("alice");
     14     let mut genesis = BTreeMap::new();
     15     genesis.insert(wallet.address().to_string(), 1);
     16     let ledger =
     17         Ledger::new_with_genesis_burns(genesis, vec![GenesisBurn::new(wallet.address(), 1)], 1)
     18             .unwrap();
     19 
     20     store.save(&ledger.snapshot()).unwrap();
     21 
     22     assert_eq!(store.load().unwrap(), Some(ledger.snapshot()));
     23     store
     24         .with_connection(|connection| {
     25             let columns = connection
     26                 .prepare("PRAGMA table_info(chain_snapshots)")?
     27                 .query_map([], |row| row.get::<_, String>(1))?
     28                 .collect::<std::result::Result<Vec<_>, _>>()?;
     29             assert!(columns.contains(&"snapshot_blob".to_string()));
     30             assert!(!columns.contains(&"snapshot_json".to_string()));
     31             Ok(())
     32         })
     33         .unwrap();
     34 }
     35 
     36 #[test]
     37 fn sqlite_chain_store_does_not_create_ui_projection_tables() {
     38     let dir = tempdir().unwrap();
     39     let store = SqliteChainStore::open(dir.path().join("chain.sqlite3")).unwrap();
     40 
     41     store
     42         .with_connection(|connection| {
     43             for table in [
     44                 "block_metrics",
     45                 "ui_cache_meta",
     46                 "ui_output_index",
     47                 "ui_revealed_transactions",
     48                 "ui_burn_leader_ranks",
     49                 "ui_burn_leader_rank_blocks",
     50             ] {
     51                 let count = connection.query_row(
     52                     "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = ?1",
     53                     [table],
     54                     |row| row.get::<_, u64>(0),
     55                 )?;
     56                 assert_eq!(count, 0, "{table} should live in ui_data.sqlite3");
     57             }
     58             Ok(())
     59         })
     60         .unwrap();
     61 }
     62 
     63 #[test]
     64 fn sqlite_chain_store_clear_chain_removes_snapshot() {
     65     let dir = tempdir().unwrap();
     66     let store = SqliteChainStore::open(dir.path().join("chain.sqlite3")).unwrap();
     67     let wallet = Wallet::from_seed("clear-chain-alice");
     68     let mut genesis = BTreeMap::new();
     69     genesis.insert(wallet.address().to_string(), 10);
     70     let ledger =
     71         Ledger::new_with_genesis_burns(genesis, vec![GenesisBurn::new(wallet.address(), 1)], 1)
     72             .unwrap();
     73 
     74     store.save(&ledger.snapshot()).unwrap();
     75     assert!(store.load().unwrap().is_some());
     76 
     77     store.clear_chain().unwrap();
     78 
     79     assert!(store.load().unwrap().is_none());
     80 }
     81 
     82 #[test]
     83 fn compact_snapshot_roundtrips_and_is_smaller_than_json() {
     84     let alice = Wallet::from_seed("compact-alice");
     85     let bob = Wallet::from_seed("compact-bob");
     86     let carol = Wallet::from_seed("compact-carol");
     87     let wallets = [alice.clone(), bob.clone()];
     88     let mut genesis = BTreeMap::new();
     89     genesis.insert(alice.address().to_string(), 10_000_000);
     90     genesis.insert(bob.address().to_string(), 10_000_000);
     91     genesis.insert(carol.address().to_string(), 10_000_000);
     92     let mut ledger = Ledger::new_with_genesis_burns(
     93         genesis,
     94         vec![
     95             GenesisBurn::new(alice.address(), 1_000_000),
     96             GenesisBurn::new(bob.address(), 1_000_000),
     97         ],
     98         1,
     99     )
    100     .unwrap();
    101     let blinded = ledger
    102         .build_blinded_burn(&carol, 3, 7, ledger.height() + 4)
    103         .unwrap();
    104     ledger
    105         .submit_blinded_transaction(blinded.transaction.clone())
    106         .unwrap();
    107     let leader = ledger.expected_leader_for_next_block().unwrap();
    108     let wallet = wallets
    109         .iter()
    110         .find(|wallet| wallet.address() == leader)
    111         .unwrap();
    112     let burn = ledger.build_burn(wallet, 1, 0).unwrap();
    113     ledger.submit_transaction(burn).unwrap();
    114     let block = ledger.mine_next_block(wallet, 1).unwrap();
    115     assert_eq!(
    116         block.blinded_transactions,
    117         vec![blinded.transaction.clone()]
    118     );
    119     ledger.apply_locally_mined_block(block).unwrap();
    120     ledger.submit_blinded_reveal(blinded.reveal).unwrap();
    121     let leader = ledger.expected_leader_for_next_block().unwrap();
    122     let wallet = wallets
    123         .iter()
    124         .find(|wallet| wallet.address() == leader)
    125         .unwrap();
    126     let burn = ledger.build_burn(wallet, 1, 0).unwrap();
    127     ledger.submit_transaction(burn).unwrap();
    128     let bundles = ledger
    129         .reveal_committee_for_next_block()
    130         .into_iter()
    131         .filter_map(|member| {
    132             let wallet = wallets
    133                 .iter()
    134                 .find(|wallet| wallet.address() == member.owner)
    135                 .unwrap();
    136             ledger.build_reveal_bundle(wallet).unwrap()
    137         })
    138         .collect::<Vec<_>>();
    139     assert!(!bundles.is_empty());
    140     let prepared = ledger
    141         .prepare_next_block_with_reveal_bundles(wallet.address(), 2, bundles)
    142         .unwrap();
    143     let vdf_output = run_vdf(prepared.vdf_seed(), prepared.vdf_rounds());
    144     let block = prepared.finish(wallet, vdf_output);
    145     assert_eq!(block.all_blinded_reveals().len(), 1);
    146     ledger.apply_locally_mined_block(block).unwrap();
    147 
    148     let snapshot = ledger.snapshot();
    149     let compact = encode_compact_snapshot(&snapshot).unwrap();
    150     let json = serde_json::to_vec(&snapshot).unwrap();
    151 
    152     assert_eq!(decode_compact_snapshot(&compact).unwrap(), snapshot);
    153     assert!(
    154         compact.len() < json.len(),
    155         "compact snapshot should be smaller than JSON: compact={} JSON={}",
    156         compact.len(),
    157         json.len()
    158     );
    159 }
    160 
    161 #[test]
    162 fn sqlite_chain_store_overwrites_latest_snapshot() {
    163     let dir = tempdir().unwrap();
    164     let store = SqliteChainStore::open(dir.path().join("chain.sqlite3")).unwrap();
    165     let wallet = Wallet::from_seed("alice");
    166     let mut genesis = BTreeMap::new();
    167     genesis.insert(wallet.address().to_string(), 2);
    168     let mut ledger =
    169         Ledger::new_with_genesis_burns(genesis, vec![GenesisBurn::new(wallet.address(), 1)], 1)
    170             .unwrap();
    171     store.save(&ledger.snapshot()).unwrap();
    172 
    173     let burn = ledger.build_burn(&wallet, 1, 0).unwrap();
    174     ledger.submit_transaction(burn).unwrap();
    175     let block = ledger.mine_next_block(&wallet, 1_000).unwrap();
    176     ledger.apply_locally_mined_block(block).unwrap();
    177     store.save(&ledger.snapshot()).unwrap();
    178 
    179     let restored = store.load().unwrap().unwrap();
    180     assert_eq!(restored.blocks.last().unwrap().height, 1);
    181     assert_eq!(restored, ledger.snapshot());
    182 }
    183 
    184 #[test]
    185 fn sqlite_chain_store_reports_invalid_compact_snapshot() {
    186     let dir = tempdir().unwrap();
    187     let store = SqliteChainStore::open(dir.path().join("chain.sqlite3")).unwrap();
    188     store
    189         .with_connection(|connection| {
    190             connection.execute(
    191                 r#"
    192 INSERT INTO chain_snapshots (id, height, tip_hash, snapshot_blob, updated_at_ms)
    193 VALUES (1, 9, 'bad-tip', x'00010203', 0)
    194 "#,
    195                 [],
    196             )?;
    197             Ok(())
    198         })
    199         .unwrap();
    200 
    201     let error = store.load().unwrap_err();
    202 
    203     assert!(
    204         format!("{error:#}").contains("failed to parse compact chain snapshot from database"),
    205         "{error:#}"
    206     );
    207 }