commit 326ef64ba79e47bed4ecb94e23b4017296cda3f1
parent 93e0aa65d53dc7986f99f0f2c4513c82b74d3d9a
Author: Joris Hartog <jorishartog@hotmail.com>
Date: Fri, 7 Aug 2026 15:48:00 +0200
Prepare v0.2.31
Diffstat:
8 files changed, 103 insertions(+), 9 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -537,7 +537,7 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "iuna"
-version = "0.2.30"
+version = "0.2.31"
dependencies = [
"anyhow",
"axum",
diff --git a/Cargo.toml b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "iuna"
-version = "0.2.30"
+version = "0.2.31"
edition = "2024"
license = "Apache-2.0"
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
@@ -1511,7 +1511,7 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "iuna-desktop"
-version = "0.2.30"
+version = "0.2.31"
dependencies = [
"tauri",
"tauri-build",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "iuna-desktop"
-version = "0.2.30"
+version = "0.2.31"
edition = "2024"
publish = false
diff --git a/src/adapters/http.rs b/src/adapters/http.rs
@@ -824,6 +824,11 @@ async fn api_mempool(
let pending = node.pending_transactions();
let pending_blinded = node.pending_blinded_transactions();
let pending_reveals = node.pending_blinded_reveals();
+ let pending_revealed = node
+ .pending_revealed_blinded_transactions()
+ .into_iter()
+ .map(|revealed| (revealed.commitment.clone(), revealed))
+ .collect::<BTreeMap<_, _>>();
let outputs = known_output_index(&snapshot, &pending);
let mut items = pending
.iter()
@@ -834,7 +839,12 @@ async fn api_mempool(
.iter()
.map(|transaction| ui_blinded_transaction(transaction, &outputs)),
);
- items.extend(pending_reveals.iter().map(ui_blinded_reveal));
+ items.extend(pending_reveals.iter().map(|reveal| {
+ pending_revealed
+ .get(&reveal.commitment)
+ .map(|revealed| ui_pending_revealed_transaction(revealed, &outputs))
+ .unwrap_or_else(|| ui_blinded_reveal(reveal))
+ }));
Json(page_items(items, query))
}
@@ -1900,6 +1910,15 @@ fn ui_revealed_transaction(
row
}
+fn ui_pending_revealed_transaction(
+ revealed: &RevealedBlindedTransaction,
+ outputs_by_outpoint: &BTreeMap<OutPoint, TxOutput>,
+) -> UiTransaction {
+ let mut row = ui_revealed_transaction(&revealed.transaction, outputs_by_outpoint);
+ row.commitment = Some(revealed.commitment.clone());
+ row
+}
+
fn ui_transaction(
transaction: &Transaction,
outputs_by_outpoint: &BTreeMap<OutPoint, TxOutput>,
@@ -4046,7 +4065,7 @@ const INDEX_HTML: &str = r#"<!doctype html>
<div class="tx-field"><span class="tx-label">Fee</span><span class="tx-value money" x-text="txFeeLabel(selectedTransaction?.tx)"></span></div>
<div class="tx-field" x-show="!isBlindedMempoolItem(selectedTransaction?.tx)"><span class="tx-label">From</span><code class="tx-value hash" x-text="txFrom(selectedTransaction?.tx || {})"></code></div>
<div class="tx-field" x-show="txTo(selectedTransaction?.tx || {})"><span class="tx-label">To</span><code class="tx-value hash" x-text="txTo(selectedTransaction?.tx || {})"></code></div>
- <div class="tx-field" x-show="isBlindedMempoolItem(selectedTransaction?.tx)"><span class="tx-label">Commitment</span><code class="tx-value hash" x-text="selectedTransaction?.tx?.commitment || selectedTransaction?.tx?.signature || '-'"></code></div>
+ <div class="tx-field" x-show="isBlindedMempoolItem(selectedTransaction?.tx) || selectedTransaction?.tx?.commitment"><span class="tx-label">Commitment</span><code class="tx-value hash" x-text="selectedTransaction?.tx?.commitment || selectedTransaction?.tx?.signature || '-'"></code></div>
<div class="tx-field" x-show="selectedTransaction?.tx?.encrypted_size || selectedTransaction?.tx?.encryptedSize"><span class="tx-label">Encrypted Bytes</span><span class="tx-value number" x-text="selectedTransaction?.tx?.encrypted_size || selectedTransaction?.tx?.encryptedSize"></span></div>
<div class="tx-field" x-show="selectedTransaction?.tx?.expires_at_height || selectedTransaction?.tx?.expiresAtHeight"><span class="tx-label">Expires</span><span class="tx-value number" x-text="selectedTransaction?.tx?.expires_at_height || selectedTransaction?.tx?.expiresAtHeight"></span></div>
<div class="tx-field" x-show="isMineTx(selectedTransaction?.tx)"><span class="tx-label">Difficulty</span><span class="tx-value number" x-text="txDifficultyBits(selectedTransaction?.tx) ?? '-'"></span></div>
@@ -4278,6 +4297,21 @@ mod tests {
let blinded_row = super::ui_blinded_transaction(&blinded, &outputs);
let reveal_row = super::ui_blinded_reveal(&reveal);
+ let revealed_row = super::ui_pending_revealed_transaction(
+ &crate::domain::RevealedBlindedTransaction {
+ height: 2,
+ commitment: reveal.commitment.clone(),
+ included_by: owner.clone(),
+ transaction: Transaction::Burn {
+ inputs: blinded.inputs.clone(),
+ change: Vec::new(),
+ amount: 12,
+ fee: blinded.fee,
+ signature: "f".repeat(128),
+ },
+ },
+ &outputs,
+ );
assert_eq!(blinded_row.kind, "blinded");
assert_eq!(blinded_row.from, owner);
@@ -4288,6 +4322,12 @@ mod tests {
assert_eq!(blinded_row.expires_at_height, Some(42));
assert_eq!(reveal_row.kind, "reveal");
assert_eq!(reveal_row.commitment, blinded_row.commitment);
+ assert_eq!(revealed_row.kind, "burn");
+ assert!(revealed_row.revealed);
+ assert_eq!(revealed_row.commitment, Some(reveal.commitment));
+ assert_eq!(revealed_row.amount, 12);
+ assert_eq!(revealed_row.fee, 7);
+ assert_eq!(revealed_row.inputs[0].amount, Some(99));
}
#[test]
@@ -5505,8 +5545,12 @@ mod tests {
fn reveal_mempool_items_show_unknown_fee_label() {
let app_js = include_str!("../../www/assets/iuna-ui.js");
assert!(app_js.contains("txFeeLabel(tx)"));
- assert!(app_js.contains("tx?.kind === \"reveal\""));
+ assert!(app_js.contains("!tx?.revealed && tx?.kind === \"reveal\""));
assert!(app_js.contains("unknown until reveal"));
+ assert!(
+ app_js
+ .contains("!tx?.revealed && (tx?.kind === \"blinded\" || tx?.kind === \"reveal\")")
+ );
assert!(app_js.contains("mempoolFirstSeenHeights"));
assert!(app_js.contains("syncMempoolBlockMarker"));
assert!(app_js.contains("mempoolItemClass"));
diff --git a/src/app.rs b/src/app.rs
@@ -430,6 +430,12 @@ impl NodeCore {
self.ledger.pending_blinded_reveals().to_vec()
}
+ pub fn pending_revealed_blinded_transactions(
+ &self,
+ ) -> Vec<crate::domain::RevealedBlindedTransaction> {
+ self.ledger.pending_revealed_blinded_transactions()
+ }
+
pub fn owned_blinded_payloads(&self) -> Vec<Transaction> {
self.owned_blinded_payloads.values().cloned().collect()
}
diff --git a/src/domain.rs b/src/domain.rs
@@ -2164,6 +2164,22 @@ impl Ledger {
&self.pending_reveals
}
+ pub fn pending_revealed_blinded_transactions(&self) -> Vec<RevealedBlindedTransaction> {
+ self.pending_reveals
+ .iter()
+ .filter_map(|reveal| {
+ let active = self.active_blinded.get(&reveal.commitment)?;
+ let transaction = self.pending_reveal_transaction(reveal).ok()?;
+ Some(RevealedBlindedTransaction {
+ height: self.height().saturating_add(1),
+ commitment: reveal.commitment.clone(),
+ included_by: active.included_by.clone(),
+ transaction,
+ })
+ })
+ .collect()
+ }
+
pub(crate) fn drop_pending_blinded_conflicting_with_transaction(
&mut self,
transaction: &Transaction,
@@ -7734,6 +7750,34 @@ mod tests {
}
#[test]
+ fn pending_blinded_reveals_expose_revealed_transaction_data() {
+ let alice = Wallet::from_seed("pending-reveal-data-finalizer-alice");
+ let bob = Wallet::from_seed("pending-reveal-data-finalizer-bob");
+ let carol = Wallet::from_seed("pending-reveal-data-carol");
+ let finalizers = [alice.clone(), bob.clone()];
+ let mut ledger = ledger_with_finalizers(&finalizers, &[(&carol, 10 * MICRO_IUNA)]);
+ let blinded = ledger
+ .build_blinded_burn(&carol, 3, 7, ledger.height() + 6)
+ .unwrap();
+ let commitment = blinded.transaction.commitment.clone();
+ ledger
+ .submit_blinded_transaction(blinded.transaction)
+ .unwrap();
+ queue_next_leader_burn(&mut ledger, &finalizers);
+ mine_preverified_as_next_leader(&mut ledger, &finalizers, 1);
+
+ ledger.submit_blinded_reveal(blinded.reveal).unwrap();
+
+ let revealed = ledger.pending_revealed_blinded_transactions();
+ assert_eq!(revealed.len(), 1);
+ assert_eq!(revealed[0].commitment, commitment);
+ assert_eq!(revealed[0].height, ledger.height() + 1);
+ assert_eq!(revealed[0].transaction.amount(), 3);
+ assert_eq!(revealed[0].transaction.fee(), 7);
+ assert!(revealed[0].transaction.is_burn());
+ }
+
+ #[test]
fn abandoned_fork_blinded_transactions_return_to_mempool() {
let alice = Wallet::from_seed("blinded-reorg-finalizer-alice");
let bob = Wallet::from_seed("blinded-reorg-finalizer-bob");
diff --git a/www/assets/iuna-ui.js b/www/assets/iuna-ui.js
@@ -1618,11 +1618,11 @@ window.iunaApp = function iunaApp() {
},
isBlindedMempoolItem(tx) {
- return tx?.kind === "blinded" || tx?.kind === "reveal";
+ return !tx?.revealed && (tx?.kind === "blinded" || tx?.kind === "reveal");
},
txFeeLabel(tx) {
- if (tx?.kind === "reveal") return "unknown until reveal";
+ if (!tx?.revealed && tx?.kind === "reveal") return "unknown until reveal";
return `IUNA ${this.amountLabel(tx?.fee ?? 0)}`;
},