feat: Add Announcement viewer
Every transaction can have zero or more announcements, which are essentially
messages that must be included across all future mergers and updates.
Announcements are typically used for transmitting information related to
receiving UTXOs, encrypted. However, a new use case is that of *transparent
transaction info*, which is an announcement containing the UTXOs and the
commitment randomnesses needed to reproduce the transaction's inputs and
outputs. With such a transparent transaction info announcement, third parties
can transparently audit transactions.
This commit adds a page for viewing announcements, and if the announcement can
be parsed as a transparent transaction info type announcement then it is
rendered as such, complete with native currency amounts and linkable UTXOs
(where possible).
The path for accessing announcements is any of
- `/announcement/digest/<hex-string>/<index>`
- `/announcement/tip/<index>`
- `/announcement/genesis/<index>`
- `/announcement/height/<height>/<index>`
- `/announcement/height_or_digest/<height-or-diges>/index/<index>`.
The last bullet point exists to support the quick lookup functionality, which is
also new. A `AnnouncementSelector` has display and from-string methods relating
these path formats to the relevant object. A suite of (prop)tests verifies
parsing.
Also, this commit enables mocking, which is useful when the neptune-core node
the explorer is connected to is outdated, unsynced, or for whatever reason does
not serve the desired data. The RPC client call is intercepted, and if it fails
and mocking is enabled, an imagined (pseudorandom) resource of the requested
type is returned. To enable mocking, compile with the "mock" feature flag and
set the "MOCK" environment variable.
2025-08-12 18:21:27 +02:00
|
|
|
use crate::html::component::header::HeaderHtml;
|
|
|
|
|
use crate::html::page::not_found::not_found_html_response;
|
|
|
|
|
use crate::http_util::rpc_method_err;
|
|
|
|
|
use crate::model::announcement_selector::AnnouncementSelector;
|
|
|
|
|
use crate::model::announcement_type::AnnouncementType;
|
|
|
|
|
use crate::model::app_state::AppState;
|
2025-08-13 19:34:02 +02:00
|
|
|
use crate::model::transparent_utxo_tuple::TransparentUtxoTuple;
|
feat: Add Announcement viewer
Every transaction can have zero or more announcements, which are essentially
messages that must be included across all future mergers and updates.
Announcements are typically used for transmitting information related to
receiving UTXOs, encrypted. However, a new use case is that of *transparent
transaction info*, which is an announcement containing the UTXOs and the
commitment randomnesses needed to reproduce the transaction's inputs and
outputs. With such a transparent transaction info announcement, third parties
can transparently audit transactions.
This commit adds a page for viewing announcements, and if the announcement can
be parsed as a transparent transaction info type announcement then it is
rendered as such, complete with native currency amounts and linkable UTXOs
(where possible).
The path for accessing announcements is any of
- `/announcement/digest/<hex-string>/<index>`
- `/announcement/tip/<index>`
- `/announcement/genesis/<index>`
- `/announcement/height/<height>/<index>`
- `/announcement/height_or_digest/<height-or-diges>/index/<index>`.
The last bullet point exists to support the quick lookup functionality, which is
also new. A `AnnouncementSelector` has display and from-string methods relating
these path formats to the relevant object. A suite of (prop)tests verifies
parsing.
Also, this commit enables mocking, which is useful when the neptune-core node
the explorer is connected to is outdated, unsynced, or for whatever reason does
not serve the desired data. The RPC client call is intercepted, and if it fails
and mocking is enabled, an imagined (pseudorandom) resource of the requested
type is returned. To enable mocking, compile with the "mock" feature flag and
set the "MOCK" environment variable.
2025-08-12 18:21:27 +02:00
|
|
|
use axum::extract::rejection::PathRejection;
|
|
|
|
|
use axum::extract::Path;
|
|
|
|
|
use axum::extract::State;
|
|
|
|
|
use axum::response::Html;
|
|
|
|
|
use axum::response::Response;
|
|
|
|
|
use html_escaper::Escape;
|
|
|
|
|
use html_escaper::Trusted;
|
|
|
|
|
use neptune_cash::api::export::BlockHeight;
|
|
|
|
|
use neptune_cash::prelude::tasm_lib::prelude::Digest;
|
|
|
|
|
use neptune_cash::prelude::triton_vm::prelude::BFieldCodec;
|
|
|
|
|
use neptune_cash::prelude::twenty_first::tip5::Tip5;
|
2025-08-13 19:34:02 +02:00
|
|
|
use neptune_cash::util_types::mutator_set::addition_record::AdditionRecord;
|
|
|
|
|
use std::collections::HashMap;
|
feat: Add Announcement viewer
Every transaction can have zero or more announcements, which are essentially
messages that must be included across all future mergers and updates.
Announcements are typically used for transmitting information related to
receiving UTXOs, encrypted. However, a new use case is that of *transparent
transaction info*, which is an announcement containing the UTXOs and the
commitment randomnesses needed to reproduce the transaction's inputs and
outputs. With such a transparent transaction info announcement, third parties
can transparently audit transactions.
This commit adds a page for viewing announcements, and if the announcement can
be parsed as a transparent transaction info type announcement then it is
rendered as such, complete with native currency amounts and linkable UTXOs
(where possible).
The path for accessing announcements is any of
- `/announcement/digest/<hex-string>/<index>`
- `/announcement/tip/<index>`
- `/announcement/genesis/<index>`
- `/announcement/height/<height>/<index>`
- `/announcement/height_or_digest/<height-or-diges>/index/<index>`.
The last bullet point exists to support the quick lookup functionality, which is
also new. A `AnnouncementSelector` has display and from-string methods relating
these path formats to the relevant object. A suite of (prop)tests verifies
parsing.
Also, this commit enables mocking, which is useful when the neptune-core node
the explorer is connected to is outdated, unsynced, or for whatever reason does
not serve the desired data. The RPC client call is intercepted, and if it fails
and mocking is enabled, an imagined (pseudorandom) resource of the requested
type is returned. To enable mocking, compile with the "mock" feature flag and
set the "MOCK" environment variable.
2025-08-12 18:21:27 +02:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
use tarpc::context;
|
|
|
|
|
|
|
|
|
|
#[axum::debug_handler]
|
|
|
|
|
pub async fn announcement_page(
|
|
|
|
|
maybe_path: Result<Path<AnnouncementSelector>, PathRejection>,
|
|
|
|
|
State(state_rw): State<Arc<AppState>>,
|
|
|
|
|
) -> Result<Html<String>, Response> {
|
|
|
|
|
#[derive(Debug, Clone, boilerplate::Boilerplate)]
|
|
|
|
|
#[boilerplate(filename = "web/html/page/announcement.html")]
|
|
|
|
|
pub struct AnnouncementHtmlPage<'a> {
|
|
|
|
|
header: HeaderHtml<'a>,
|
|
|
|
|
index: usize,
|
|
|
|
|
num_announcements: usize,
|
|
|
|
|
block_hash: Digest,
|
|
|
|
|
block_height: BlockHeight,
|
|
|
|
|
announcement_type: AnnouncementType,
|
2025-08-13 19:34:02 +02:00
|
|
|
addition_record_indices: HashMap<AdditionRecord, Option<u64>>,
|
feat: Add Announcement viewer
Every transaction can have zero or more announcements, which are essentially
messages that must be included across all future mergers and updates.
Announcements are typically used for transmitting information related to
receiving UTXOs, encrypted. However, a new use case is that of *transparent
transaction info*, which is an announcement containing the UTXOs and the
commitment randomnesses needed to reproduce the transaction's inputs and
outputs. With such a transparent transaction info announcement, third parties
can transparently audit transactions.
This commit adds a page for viewing announcements, and if the announcement can
be parsed as a transparent transaction info type announcement then it is
rendered as such, complete with native currency amounts and linkable UTXOs
(where possible).
The path for accessing announcements is any of
- `/announcement/digest/<hex-string>/<index>`
- `/announcement/tip/<index>`
- `/announcement/genesis/<index>`
- `/announcement/height/<height>/<index>`
- `/announcement/height_or_digest/<height-or-diges>/index/<index>`.
The last bullet point exists to support the quick lookup functionality, which is
also new. A `AnnouncementSelector` has display and from-string methods relating
these path formats to the relevant object. A suite of (prop)tests verifies
parsing.
Also, this commit enables mocking, which is useful when the neptune-core node
the explorer is connected to is outdated, unsynced, or for whatever reason does
not serve the desired data. The RPC client call is intercepted, and if it fails
and mocking is enabled, an imagined (pseudorandom) resource of the requested
type is returned. To enable mocking, compile with the "mock" feature flag and
set the "MOCK" environment variable.
2025-08-12 18:21:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let state = &state_rw.load();
|
|
|
|
|
|
|
|
|
|
let Path(AnnouncementSelector {
|
|
|
|
|
block_selector,
|
|
|
|
|
index,
|
|
|
|
|
}) = maybe_path.map_err(|e| not_found_html_response(state, Some(e.to_string())))?;
|
|
|
|
|
|
|
|
|
|
let block_info = state
|
|
|
|
|
.rpc_client
|
|
|
|
|
.block_info(context::current(), state.token(), block_selector)
|
|
|
|
|
.await
|
|
|
|
|
.map_err(|e| not_found_html_response(state, Some(e.to_string())))?
|
|
|
|
|
.map_err(rpc_method_err)?
|
|
|
|
|
.ok_or(not_found_html_response(
|
|
|
|
|
state,
|
|
|
|
|
Some("The requested block does not exist".to_string()),
|
|
|
|
|
))?;
|
|
|
|
|
let block_hash = block_info.digest;
|
|
|
|
|
let block_height = block_info.height;
|
|
|
|
|
|
|
|
|
|
let announcements = state
|
|
|
|
|
.rpc_client
|
|
|
|
|
.announcements_in_block(context::current(), state.token(), block_selector)
|
|
|
|
|
.await
|
|
|
|
|
.map_err(|e| not_found_html_response(state, Some(e.to_string())))?
|
|
|
|
|
.map_err(rpc_method_err)?
|
|
|
|
|
.expect(
|
|
|
|
|
"block guaranteed to exist because we got here; getting its announcements should work",
|
|
|
|
|
);
|
|
|
|
|
let num_announcements = announcements.len();
|
|
|
|
|
let announcement = announcements
|
|
|
|
|
.get(index)
|
|
|
|
|
.ok_or(not_found_html_response(
|
|
|
|
|
state,
|
|
|
|
|
Some("The requested announcement does not exist".to_string()),
|
|
|
|
|
))?
|
|
|
|
|
.clone();
|
|
|
|
|
let announcement_type = AnnouncementType::parse(announcement);
|
|
|
|
|
|
2025-08-13 19:34:02 +02:00
|
|
|
let mut addition_record_indices = HashMap::<AdditionRecord, Option<u64>>::new();
|
|
|
|
|
if let AnnouncementType::TransparentTxInfo(tx_info) = announcement_type.clone() {
|
|
|
|
|
let addition_records = tx_info
|
|
|
|
|
.outputs
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|output| output.addition_record())
|
|
|
|
|
.collect::<Vec<_>>();
|
2025-08-23 00:57:00 +02:00
|
|
|
addition_record_indices = state
|
|
|
|
|
.rpc_client
|
|
|
|
|
.addition_record_indices_for_block(
|
|
|
|
|
context::current(),
|
|
|
|
|
state.token(),
|
|
|
|
|
block_selector,
|
|
|
|
|
&addition_records,
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
.map_err(|e| not_found_html_response(state, Some(e.to_string())))?
|
|
|
|
|
.map_err(rpc_method_err)?
|
|
|
|
|
.into_iter()
|
|
|
|
|
.collect::<HashMap<_, _>>();
|
2025-08-13 19:34:02 +02:00
|
|
|
|
|
|
|
|
let mut transparent_utxos_cache = state.transparent_utxos_cache.lock().await;
|
|
|
|
|
|
|
|
|
|
for input in &tx_info.inputs {
|
|
|
|
|
let addition_record = input.addition_record();
|
|
|
|
|
if let Some(existing_entry) = transparent_utxos_cache
|
|
|
|
|
.iter_mut()
|
|
|
|
|
.find(|tu| tu.addition_record() == addition_record)
|
|
|
|
|
{
|
|
|
|
|
existing_entry.upgrade_with_transparent_input(input, block_hash);
|
|
|
|
|
} else {
|
|
|
|
|
tracing::info!("Adding transparent UTXO (input side) to cache.");
|
|
|
|
|
transparent_utxos_cache.push(TransparentUtxoTuple::new_from_transparent_input(
|
|
|
|
|
input, block_hash,
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for output in &tx_info.outputs {
|
|
|
|
|
let addition_record = output.addition_record();
|
|
|
|
|
if let Some(existing_entry) = transparent_utxos_cache
|
|
|
|
|
.iter_mut()
|
|
|
|
|
.find(|tu| tu.addition_record() == addition_record)
|
|
|
|
|
{
|
|
|
|
|
existing_entry.upgrade_with_transparent_output(block_hash);
|
|
|
|
|
} else {
|
|
|
|
|
tracing::info!("Adding transparent UTXO (output side) to cache.");
|
|
|
|
|
transparent_utxos_cache.push(TransparentUtxoTuple::new_from_transparent_output(
|
|
|
|
|
output,
|
|
|
|
|
addition_record_indices
|
|
|
|
|
.get(&addition_record)
|
|
|
|
|
.cloned()
|
|
|
|
|
.unwrap_or(None),
|
|
|
|
|
block_hash,
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
feat: Add Announcement viewer
Every transaction can have zero or more announcements, which are essentially
messages that must be included across all future mergers and updates.
Announcements are typically used for transmitting information related to
receiving UTXOs, encrypted. However, a new use case is that of *transparent
transaction info*, which is an announcement containing the UTXOs and the
commitment randomnesses needed to reproduce the transaction's inputs and
outputs. With such a transparent transaction info announcement, third parties
can transparently audit transactions.
This commit adds a page for viewing announcements, and if the announcement can
be parsed as a transparent transaction info type announcement then it is
rendered as such, complete with native currency amounts and linkable UTXOs
(where possible).
The path for accessing announcements is any of
- `/announcement/digest/<hex-string>/<index>`
- `/announcement/tip/<index>`
- `/announcement/genesis/<index>`
- `/announcement/height/<height>/<index>`
- `/announcement/height_or_digest/<height-or-diges>/index/<index>`.
The last bullet point exists to support the quick lookup functionality, which is
also new. A `AnnouncementSelector` has display and from-string methods relating
these path formats to the relevant object. A suite of (prop)tests verifies
parsing.
Also, this commit enables mocking, which is useful when the neptune-core node
the explorer is connected to is outdated, unsynced, or for whatever reason does
not serve the desired data. The RPC client call is intercepted, and if it fails
and mocking is enabled, an imagined (pseudorandom) resource of the requested
type is returned. To enable mocking, compile with the "mock" feature flag and
set the "MOCK" environment variable.
2025-08-12 18:21:27 +02:00
|
|
|
let header = HeaderHtml { state };
|
|
|
|
|
|
|
|
|
|
let utxo_page = AnnouncementHtmlPage {
|
|
|
|
|
index,
|
|
|
|
|
header,
|
|
|
|
|
block_hash,
|
|
|
|
|
block_height,
|
|
|
|
|
num_announcements,
|
|
|
|
|
announcement_type,
|
2025-08-13 19:34:02 +02:00
|
|
|
addition_record_indices,
|
feat: Add Announcement viewer
Every transaction can have zero or more announcements, which are essentially
messages that must be included across all future mergers and updates.
Announcements are typically used for transmitting information related to
receiving UTXOs, encrypted. However, a new use case is that of *transparent
transaction info*, which is an announcement containing the UTXOs and the
commitment randomnesses needed to reproduce the transaction's inputs and
outputs. With such a transparent transaction info announcement, third parties
can transparently audit transactions.
This commit adds a page for viewing announcements, and if the announcement can
be parsed as a transparent transaction info type announcement then it is
rendered as such, complete with native currency amounts and linkable UTXOs
(where possible).
The path for accessing announcements is any of
- `/announcement/digest/<hex-string>/<index>`
- `/announcement/tip/<index>`
- `/announcement/genesis/<index>`
- `/announcement/height/<height>/<index>`
- `/announcement/height_or_digest/<height-or-diges>/index/<index>`.
The last bullet point exists to support the quick lookup functionality, which is
also new. A `AnnouncementSelector` has display and from-string methods relating
these path formats to the relevant object. A suite of (prop)tests verifies
parsing.
Also, this commit enables mocking, which is useful when the neptune-core node
the explorer is connected to is outdated, unsynced, or for whatever reason does
not serve the desired data. The RPC client call is intercepted, and if it fails
and mocking is enabled, an imagined (pseudorandom) resource of the requested
type is returned. To enable mocking, compile with the "mock" feature flag and
set the "MOCK" environment variable.
2025-08-12 18:21:27 +02:00
|
|
|
};
|
|
|
|
|
Ok(Html(utxo_page.to_string()))
|
|
|
|
|
}
|