iuna

iuna

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

commit aa19d0df3bc862ad94b9ba39d23da77c3e17926f
parent f7a5749be004d4ec463eff306ecbd9810ae84505
Author: Joris Hartog <jorishartog@hotmail.com>
Date:   Wed, 23 Sep 2026 19:17:54 +0200

fix(mining): prioritize required hybrid anchor burn

Diffstat:
Msrc/app/automatic_mining.rs | 7++++++-
Msrc/app/receive.rs | 14++++++++++++++
Msrc/domain/ledger_mempool.rs | 22+++++++++++++++++++++-
3 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/src/app/automatic_mining.rs b/src/app/automatic_mining.rs @@ -406,7 +406,7 @@ impl NodeCore { true, ) { Ok((transaction, _)) => { - self.submit_public_transaction_v2(transaction)?; + self.submit_prioritized_public_transaction_v2(transaction)?; self.last_auto_anchor_burn_height = Some(current_height); return Ok(None); } @@ -1085,6 +1085,10 @@ mod tests { .apply_preverified_block_at(migration_block, u64::MAX) .unwrap(); + let queued_wallet_burn = ledger.build_v2_burn(&wallet, 1, 1).unwrap(); + ledger + .submit_transaction_v2(queued_wallet_burn.clone()) + .unwrap(); let pending_migration = ledger.build_v2_migration(&migrator, 1).unwrap(); ledger.submit_transaction_v2(pending_migration).unwrap(); let timestamp_ms = ledger.recovery_block_min_timestamp(); @@ -1096,6 +1100,7 @@ mod tests { None ); assert!(node.local_block_anchor_burn.is_none()); + assert!(!node.ledger().pending_v2().contains(&queued_wallet_burn)); assert_eq!( node.ledger() .pending_v2() diff --git a/src/app/receive.rs b/src/app/receive.rs @@ -60,6 +60,20 @@ impl NodeCore { Ok(transaction) } + pub(super) fn submit_prioritized_public_transaction_v2( + &mut self, + transaction: TransactionV2, + ) -> Result<TransactionV2> { + let domain = self.ledger.transaction_v2_domain()?; + let envelope = hex_encode(transaction.encode(&domain)?); + let mut candidate = self.ledger.clone(); + if candidate.prioritize_transaction_v2_for_block_building(transaction.clone())? { + self.ledger = candidate; + self.outbox.push(GossipEnvelope::TransactionV2 { envelope }); + } + Ok(transaction) + } + pub fn receive_burn_bundle(&mut self, bundle: BurnBundle) -> Result<()> { let next_height = self.ledger.height().saturating_add(1); if bundle.height <= self.ledger.height() { diff --git a/src/domain/ledger_mempool.rs b/src/domain/ledger_mempool.rs @@ -10,7 +10,7 @@ use super::ledger_ops::{ use super::transaction::{Transaction, transaction_inputs_spent_by}; use super::{ Ledger, MAX_ORPHAN_TRANSACTIONS, MAX_PENDING_POOL_BYTES, MAX_PENDING_TRANSACTIONS, - TransactionSubmitOutcome, spend_inputs_with_lineage, + TransactionSubmitOutcome, TransactionV2, spend_inputs_with_lineage, }; impl Ledger { @@ -40,6 +40,26 @@ impl Ledger { Ok(true) } + /// Inserts a required v2 anchor ahead of the current v2 mempool and then + /// rebuilds the displaced entries in their original order. Callers use a + /// cloned ledger and only publish it after the priority transaction is + /// admitted, so a failed replacement cannot mutate live state. + pub(crate) fn prioritize_transaction_v2_for_block_building( + &mut self, + transaction: TransactionV2, + ) -> Result<bool> { + let displaced = std::mem::take(&mut self.pending_v2); + self.pending_v2_bytes = 0; + + if !self.submit_transaction_v2(transaction)?.added() { + return Ok(false); + } + for candidate in displaced { + let _ = self.submit_transaction_v2(candidate); + } + Ok(true) + } + pub(crate) fn reserve_transaction_inputs(&mut self, transaction: &Transaction) -> Result<()> { self.validate_new_transaction(transaction)?; let mut utxos = self.utxos.clone();