iuna

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

commit 50a2bf7ba058828f6c2d30ee820566f10de60a71
parent 898b9b80c65f7686253b26855c091ab91cf5488a
Author: Joris Hartog <jorishartog@hotmail.com>
Date:   Tue, 28 Jul 2026 10:34:18 +0200

Clean up stale mine actions

Diffstat:
MCargo.lock | 2+-
MCargo.toml | 2+-
Msrc/domain.rs | 73++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
3 files changed, 70 insertions(+), 7 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -502,7 +502,7 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "iuna" -version = "0.1.9" +version = "0.1.10" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iuna" -version = "0.1.9" +version = "0.1.10" edition = "2024" license = "Apache-2.0" diff --git a/src/domain.rs b/src/domain.rs @@ -1856,12 +1856,17 @@ impl Ledger { .collect::<BTreeSet<_>>(); self.utxos = utxos; self.tickets = tickets; - let available = self.utxos.clone(); - self.pending.retain(|tx| { - !mined_signatures.contains(tx.signature()) - && transaction_inputs_available(tx, &available) - }); self.chain.push(block); + let available = self.utxos.clone(); + let pending = std::mem::take(&mut self.pending); + self.pending = pending + .into_iter() + .filter(|tx| { + !mined_signatures.contains(tx.signature()) + && transaction_inputs_available(tx, &available) + && self.validate_transaction_terms(tx).is_ok() + }) + .collect(); self.vdf_rounds = self.next_vdf_rounds_after_tip(); Ok(()) } @@ -3454,6 +3459,43 @@ mod tests { } #[test] + fn block_selection_can_include_multiple_mine_actions() { + let alice = Wallet::from_seed("mine-multiple-actions-alice"); + let mut ledger = ledger_with_allocation(&alice, 10 * MICRO_IUNA); + + let burn = ledger.build_burn(&alice, MICRO_IUNA, 0).unwrap(); + ledger.submit_transaction(burn).unwrap(); + let first_mine = ledger + .build_mine_with_fee(alice.address(), MICRO_IUNA / 100) + .unwrap(); + ledger.submit_transaction(first_mine.clone()).unwrap(); + let second_mine = ledger + .build_mine_with_fee(alice.address(), MICRO_IUNA / 100) + .unwrap(); + ledger.submit_transaction(second_mine.clone()).unwrap(); + + assert_eq!(ledger.pending().len(), 3); + let block = ledger.mine_next_block(&alice, 1).unwrap(); + + assert_eq!(block.transactions.len(), 3); + assert!(block.transactions.iter().any(Transaction::is_burn)); + assert!( + block + .transactions + .iter() + .any(|tx| tx.signature() == first_mine.signature()) + ); + assert!( + block + .transactions + .iter() + .any(|tx| tx.signature() == second_mine.signature()) + ); + assert_ne!(first_mine.signature(), second_mine.signature()); + assert_eq!(block.reward, first_mine.fee() + second_mine.fee()); + } + + #[test] fn mine_difficulty_increases_when_issuance_exceeds_target_window() { let alice = Wallet::from_seed("mine-difficulty-up-alice"); let mut ledger = ledger_with_allocation(&alice, 100 * MICRO_IUNA); @@ -3506,6 +3548,27 @@ mod tests { } #[test] + fn pending_mine_actions_are_removed_when_anchor_expires() { + let alice = Wallet::from_seed("pending-mine-anchor-expiry-alice"); + let bob = Wallet::from_seed("pending-mine-anchor-expiry-bob"); + let mut ledger = ledger_with_allocation(&alice, 100 * MICRO_IUNA); + ledger.launch_profile.max_block_transactions = 1; + let stale_mine = ledger.build_mine(bob.address()).unwrap(); + ledger.submit_transaction(stale_mine.clone()).unwrap(); + + for _ in 0..=MINE_MAX_ANCHOR_AGE_BLOCKS { + mine_burn_block_with_mines(&mut ledger, &alice, 0); + } + + assert!( + ledger + .pending() + .iter() + .all(|tx| tx.signature() != stale_mine.signature()) + ); + } + + #[test] fn stratum_mine_header_proof_is_validated() { let alice = Wallet::from_seed("stratum-proof-alice"); let ledger = ledger_with_allocation(&alice, 100 * MICRO_IUNA);