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:
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);