diff --git a/Cargo.lock b/Cargo.lock
index 5543243a..7d240a64 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2721,6 +2721,7 @@ dependencies = [
"env_logger",
"indexer_service_protocol",
"indexer_service_rpc",
+ "itertools 0.14.0",
"jsonrpsee",
"leptos",
"leptos_axum",
@@ -3769,6 +3770,7 @@ dependencies = [
"schemars 1.2.1",
"serde",
"serde_with",
+ "sha2",
]
[[package]]
diff --git a/bedrock/docker-compose.yml b/bedrock/docker-compose.yml
index cb93a624..93f5d485 100644
--- a/bedrock/docker-compose.yml
+++ b/bedrock/docker-compose.yml
@@ -11,7 +11,7 @@ services:
image: ghcr.io/logos-blockchain/logos-blockchain@sha256:000982e751dfd346ca5346b8025c685fc3abc585079c59cde3bde7fd63100657
ports:
# Map 0 port so that multiple instances can run on the same host
- - "0:18080/tcp"
+ - "8080:18080/tcp"
volumes:
- ./scripts:/etc/logos-blockchain/scripts
- ./kzgrs_test_params:/kzgrs_test_params:z
diff --git a/explorer_service/Cargo.toml b/explorer_service/Cargo.toml
index 219f2bc0..1dc989d0 100644
--- a/explorer_service/Cargo.toml
+++ b/explorer_service/Cargo.toml
@@ -50,6 +50,9 @@ clap = { workspace = true, features = ["derive"], optional = true }
url = { workspace = true, optional = true }
env_logger = { workspace = true, optional = true }
+# Mandatory server side dependencies
+itertools.workspace = true
+
[features]
hydrate = ["leptos/hydrate"]
ssr = [
diff --git a/explorer_service/src/components/transaction_preview.rs b/explorer_service/src/components/transaction_preview.rs
index 68c1e86e..7fc98e77 100644
--- a/explorer_service/src/components/transaction_preview.rs
+++ b/explorer_service/src/components/transaction_preview.rs
@@ -19,14 +19,12 @@ pub fn TransactionPreview(transaction: Transaction) -> impl IntoView {
let (type_name, type_class) = transaction_type_info(&transaction);
// Get additional metadata based on transaction type
+ let affected_pub_account_ids = transaction
+ .affected_public_account_ids();
+
let metadata = match &transaction {
- Transaction::Public(tx) => {
- let indexer_service_protocol::PublicTransaction {
- hash: _,
- message,
- witness_set: _,
- } = tx;
- format!("{} accounts involved", message.account_ids.len())
+ Transaction::Public(_) => {
+ format!("{} accounts involved", affected_pub_account_ids.len())
}
Transaction::PrivacyPreserving(tx) => {
let indexer_service_protocol::PrivacyPreservingTransaction {
@@ -36,7 +34,7 @@ pub fn TransactionPreview(transaction: Transaction) -> impl IntoView {
} = tx;
format!(
"{} public accounts, {} commitments",
- message.public_account_ids.len(),
+ affected_pub_account_ids.len(),
message.new_commitments.len()
)
}
diff --git a/explorer_service/src/pages/transaction_page.rs b/explorer_service/src/pages/transaction_page.rs
index d0f1b4da..8ab33ff9 100644
--- a/explorer_service/src/pages/transaction_page.rs
+++ b/explorer_service/src/pages/transaction_page.rs
@@ -4,6 +4,7 @@ use indexer_service_protocol::{
HashType, PrivacyPreservingMessage, PrivacyPreservingTransaction, ProgramDeploymentMessage,
ProgramDeploymentTransaction, PublicMessage, PublicTransaction, Transaction, WitnessSet,
};
+use itertools::{EitherOrBoth, Itertools};
use leptos::prelude::*;
use leptos_router::{components::A, hooks::use_params_map};
@@ -65,7 +66,9 @@ pub fn TransactionPage() -> impl IntoView {
- {match tx {
+ {
+ let affected_pub_acc_ids = tx.affected_public_account_ids();
+ match tx {
Transaction::Public(ptx) => {
let PublicTransaction {
hash: _,
@@ -74,7 +77,7 @@ pub fn TransactionPage() -> impl IntoView {
} = ptx;
let PublicMessage {
program_id,
- account_ids,
+ account_ids: _,
nonces,
instruction_data,
} = message;
@@ -113,11 +116,13 @@ pub fn TransactionPage() -> impl IntoView {
"Accounts"
- {account_ids
+ {affected_pub_acc_ids
.into_iter()
- .zip(nonces.into_iter())
- .map(|(account_id, nonce)| {
- let account_id_str = account_id.to_string();
+ .zip_longest(nonces.into_iter())
+ .map(|maybe_pair| {
+ match maybe_pair {
+ EitherOrBoth::Both(account_id, nonce) => {
+ let account_id_str = account_id.to_string();
view! {
}
+ }
+ EitherOrBoth::Left(account_id) => {
+ let account_id_str = account_id.to_string();
+ view! {
+
+ }
+ }
+ EitherOrBoth::Right(_) => {
+ view! {
+
+ }
+ }
+ }
})
.collect::
>()}
diff --git a/indexer/service/protocol/Cargo.toml b/indexer/service/protocol/Cargo.toml
index f9a3c2ad..2f787d2d 100644
--- a/indexer/service/protocol/Cargo.toml
+++ b/indexer/service/protocol/Cargo.toml
@@ -16,6 +16,7 @@ base64.workspace = true
base58.workspace = true
hex.workspace = true
anyhow.workspace = true
+sha2.workspace = true
[features]
# Enable conversion to/from NSSA core types
diff --git a/indexer/service/protocol/src/lib.rs b/indexer/service/protocol/src/lib.rs
index 8fdd3289..f13a51c5 100644
--- a/indexer/service/protocol/src/lib.rs
+++ b/indexer/service/protocol/src/lib.rs
@@ -3,13 +3,14 @@
//! Currently it mostly mimics types from `nssa_core`, but it's important to have a separate crate
//! to define a stable interface for the indexer service RPCs which evolves in its own way.
-use std::{fmt::Display, str::FromStr};
+use std::{collections::HashSet, fmt::Display, str::FromStr};
use anyhow::anyhow;
use base58::{FromBase58 as _, ToBase58 as _};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_with::{DeserializeFromStr, SerializeDisplay};
+use sha2::{Digest, Sha256};
#[cfg(feature = "convert")]
mod convert;
@@ -53,6 +54,18 @@ pub struct AccountId {
pub value: [u8; 32],
}
+impl From<&PublicKey> for AccountId {
+ fn from(key: &PublicKey) -> Self {
+ const PUBLIC_ACCOUNT_ID_PREFIX: &[u8; 32] =
+ b"/LEE/v0.3/AccountId/Public/\x00\x00\x00\x00\x00";
+
+ let mut hasher = Sha256::new();
+ hasher.update(PUBLIC_ACCOUNT_ID_PREFIX);
+ hasher.update(key.0);
+ Self{ value: hasher.finalize().into()}
+ }
+}
+
impl Display for AccountId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value.to_base58())
@@ -148,6 +161,15 @@ impl Transaction {
Transaction::ProgramDeployment(tx) => &tx.hash,
}
}
+
+ /// Get affected public account ids
+ pub fn affected_public_account_ids(&self) -> Vec {
+ match self {
+ Transaction::Public(tx) => tx.affected_public_account_ids(),
+ Transaction::PrivacyPreserving(tx) => tx.affected_public_account_ids(),
+ Transaction::ProgramDeployment(tx) => tx.affected_public_account_ids(),
+ }
+ }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
@@ -157,6 +179,28 @@ pub struct PublicTransaction {
pub witness_set: WitnessSet,
}
+impl PublicTransaction {
+
+
+ pub(crate) fn signer_account_ids(&self) -> Vec {
+ self.witness_set
+ .signatures_and_public_keys()
+ .iter()
+ .map(|(_, public_key)| AccountId::from(public_key))
+ .collect()
+ }
+
+ pub fn affected_public_account_ids(&self) -> Vec {
+ let mut acc_set = self
+ .signer_account_ids()
+ .into_iter()
+ .collect::>();
+ acc_set.extend(&self.message.account_ids);
+
+ acc_set.into_iter().collect()
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct PrivacyPreservingTransaction {
pub hash: HashType,
@@ -164,6 +208,26 @@ pub struct PrivacyPreservingTransaction {
pub witness_set: WitnessSet,
}
+impl PrivacyPreservingTransaction {
+ pub(crate) fn signer_account_ids(&self) -> Vec {
+ self.witness_set
+ .signatures_and_public_keys()
+ .iter()
+ .map(|(_, public_key)| AccountId::from(public_key))
+ .collect()
+ }
+
+ pub fn affected_public_account_ids(&self) -> Vec {
+ let mut acc_set = self
+ .signer_account_ids()
+ .into_iter()
+ .collect::>();
+ acc_set.extend(&self.message.public_account_ids);
+
+ acc_set.into_iter().collect()
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct PublicMessage {
pub program_id: ProgramId,
@@ -190,6 +254,12 @@ pub struct WitnessSet {
pub proof: Proof,
}
+impl WitnessSet {
+ pub fn signatures_and_public_keys(&self) -> &[(Signature, PublicKey)] {
+ &self.signatures_and_public_keys
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct Proof(
#[serde(with = "base64")]
@@ -210,6 +280,12 @@ pub struct ProgramDeploymentTransaction {
pub message: ProgramDeploymentMessage,
}
+impl ProgramDeploymentTransaction {
+ pub fn affected_public_account_ids(&self) -> Vec {
+ vec![]
+ }
+}
+
pub type ViewTag = u8;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
diff --git a/indexer/service/rpc/Cargo.toml b/indexer/service/rpc/Cargo.toml
index 2bed63ae..b2194882 100644
--- a/indexer/service/rpc/Cargo.toml
+++ b/indexer/service/rpc/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
license = { workspace = true }
[dependencies]
-indexer_service_protocol = { workspace = true }
+indexer_service_protocol = { workspace = true, features = ["convert"] }
jsonrpsee = { workspace = true, features = ["macros"] }
serde_json.workspace = true