use alloc::boxed::Box;
use alloc::collections::{BTreeMap, BTreeSet};
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use domain::account::{AccountProof, FetchedAccount};
use domain::note::{FetchedNote, NoteSyncInfo, SyncNotesResult};
use domain::nullifier::NullifierUpdate;
use domain::sync::ChainMmrInfo;
use miden_protocol::Word;
use miden_protocol::account::{AccountCode, AccountId};
use miden_protocol::address::NetworkId;
use miden_protocol::block::{BlockHeader, BlockNumber, ProvenBlock};
use miden_protocol::crypto::merkle::mmr::MmrProof;
use miden_protocol::crypto::merkle::smt::SmtProof;
use miden_protocol::note::{NoteId, NoteScript, NoteTag, NoteType, Nullifier};
use miden_protocol::transaction::{ProvenTransaction, TransactionInputs};
use crate::rpc::domain::storage_map::StorageMapInfo;
pub mod domain;
mod errors;
pub use errors::*;
mod endpoint;
pub(crate) use domain::limits::RPC_LIMITS_STORE_SETTING;
pub use domain::limits::RpcLimits;
pub use domain::status::RpcStatusInfo;
pub use endpoint::Endpoint;
#[cfg(not(feature = "testing"))]
mod generated;
#[cfg(feature = "testing")]
pub mod generated;
#[cfg(feature = "tonic")]
mod tonic_client;
#[cfg(feature = "tonic")]
pub use tonic_client::GrpcClient;
use crate::rpc::domain::account::AccountStorageRequirements;
use crate::rpc::domain::account_vault::AccountVaultInfo;
use crate::rpc::domain::transaction::TransactionsInfo;
use crate::store::InputNoteRecord;
use crate::store::input_note_states::UnverifiedNoteState;
pub enum AccountStateAt {
ChainTip,
Block(BlockNumber),
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
pub trait NodeRpcClient: Send + Sync {
async fn set_genesis_commitment(&self, commitment: Word) -> Result<(), RpcError>;
fn has_genesis_commitment(&self) -> Option<Word>;
async fn submit_proven_transaction(
&self,
proven_transaction: ProvenTransaction,
transaction_inputs: TransactionInputs,
) -> Result<BlockNumber, RpcError>;
async fn get_block_header_by_number(
&self,
block_num: Option<BlockNumber>,
include_mmr_proof: bool,
) -> Result<(BlockHeader, Option<MmrProof>), RpcError>;
async fn get_block_by_number(&self, block_num: BlockNumber) -> Result<ProvenBlock, RpcError>;
async fn get_notes_by_id(&self, note_ids: &[NoteId]) -> Result<Vec<FetchedNote>, RpcError>;
async fn sync_chain_mmr(
&self,
block_from: BlockNumber,
block_to: Option<BlockNumber>,
) -> Result<ChainMmrInfo, RpcError>;
async fn get_account_details(&self, account_id: AccountId) -> Result<FetchedAccount, RpcError>;
async fn sync_notes(
&self,
block_num: BlockNumber,
block_to: Option<BlockNumber>,
note_tags: &BTreeSet<NoteTag>,
) -> Result<NoteSyncInfo, RpcError>;
async fn sync_notes_with_details(
&self,
block_from: BlockNumber,
block_to: Option<BlockNumber>,
note_tags: &BTreeSet<NoteTag>,
) -> Result<SyncNotesResult, RpcError> {
let mut all_blocks = Vec::new();
let mut cursor = block_from;
let mut chain_tip;
loop {
let note_sync = self.sync_notes(cursor, block_to, note_tags).await?;
chain_tip = note_sync.chain_tip;
cursor = note_sync.block_to + 1;
let range_end = block_to.unwrap_or(chain_tip);
let done = note_sync.blocks.is_empty() || cursor >= range_end;
all_blocks.extend(note_sync.blocks);
if done {
break;
}
}
let note_ids: Vec<NoteId> = all_blocks
.iter()
.flat_map(|b| b.notes.values())
.filter(|n| n.metadata().is_none() || n.note_type() != NoteType::Private)
.map(|n| *n.note_id())
.collect();
let mut public_notes = BTreeMap::new();
if !note_ids.is_empty() {
let fetched = self.get_notes_by_id(¬e_ids).await?;
for fetched_note in fetched {
let note_id = fetched_note.id();
for block in &mut all_blocks {
if let Some(note) = block.notes.get_mut(¬e_id)
&& note.metadata().is_none()
{
note.set_metadata(fetched_note.metadata().clone());
}
}
if let FetchedNote::Public(note, _) = fetched_note {
public_notes.insert(note.id(), note);
}
}
}
Ok(SyncNotesResult { blocks: all_blocks, public_notes })
}
async fn sync_nullifiers(
&self,
prefix: &[u16],
block_num: BlockNumber,
block_to: Option<BlockNumber>,
) -> Result<Vec<NullifierUpdate>, RpcError>;
async fn check_nullifiers(&self, nullifiers: &[Nullifier]) -> Result<Vec<SmtProof>, RpcError>;
async fn get_account_proof(
&self,
account_id: AccountId,
storage_requirements: AccountStorageRequirements,
account_state: AccountStateAt,
known_account_code: Option<AccountCode>,
known_vault_commitment: Option<Word>,
) -> Result<(BlockNumber, AccountProof), RpcError>;
async fn get_nullifier_commit_heights(
&self,
requested_nullifiers: BTreeSet<Nullifier>,
block_from: BlockNumber,
) -> Result<BTreeMap<Nullifier, Option<BlockNumber>>, RpcError> {
let prefixes: Vec<u16> =
requested_nullifiers.iter().map(crate::note::Nullifier::prefix).collect();
let retrieved_nullifiers = self.sync_nullifiers(&prefixes, block_from, None).await?;
let mut nullifiers_height = BTreeMap::new();
for nullifier in requested_nullifiers {
if let Some(update) =
retrieved_nullifiers.iter().find(|update| update.nullifier == nullifier)
{
nullifiers_height.insert(nullifier, Some(update.block_num));
} else {
nullifiers_height.insert(nullifier, None);
}
}
Ok(nullifiers_height)
}
async fn get_public_note_records(
&self,
note_ids: &[NoteId],
current_timestamp: Option<u64>,
) -> Result<Vec<InputNoteRecord>, RpcError> {
if note_ids.is_empty() {
return Ok(vec![]);
}
let mut public_notes = Vec::with_capacity(note_ids.len());
let note_details = self.get_notes_by_id(note_ids).await?;
for detail in note_details {
if let FetchedNote::Public(note, inclusion_proof) = detail {
let state = UnverifiedNoteState {
metadata: note.metadata().clone(),
inclusion_proof,
}
.into();
let note = InputNoteRecord::new(note.into(), current_timestamp, state);
public_notes.push(note);
}
}
Ok(public_notes)
}
async fn get_block_header_with_proof(
&self,
block_num: BlockNumber,
) -> Result<(BlockHeader, MmrProof), RpcError> {
let (header, proof) = self.get_block_header_by_number(Some(block_num), true).await?;
Ok((header, proof.ok_or(RpcError::ExpectedDataMissing(String::from("MmrProof")))?))
}
async fn get_note_by_id(&self, note_id: NoteId) -> Result<FetchedNote, RpcError> {
let notes = self.get_notes_by_id(&[note_id]).await?;
notes.into_iter().next().ok_or(RpcError::NoteNotFound(note_id))
}
async fn get_note_script_by_root(&self, root: Word) -> Result<NoteScript, RpcError>;
async fn sync_storage_maps(
&self,
block_from: BlockNumber,
block_to: Option<BlockNumber>,
account_id: AccountId,
) -> Result<StorageMapInfo, RpcError>;
async fn sync_account_vault(
&self,
block_from: BlockNumber,
block_to: Option<BlockNumber>,
account_id: AccountId,
) -> Result<AccountVaultInfo, RpcError>;
async fn sync_transactions(
&self,
block_from: BlockNumber,
block_to: Option<BlockNumber>,
account_ids: Vec<AccountId>,
) -> Result<TransactionsInfo, RpcError>;
async fn get_network_id(&self) -> Result<NetworkId, RpcError>;
async fn get_rpc_limits(&self) -> Result<RpcLimits, RpcError>;
fn has_rpc_limits(&self) -> Option<RpcLimits>;
async fn set_rpc_limits(&self, limits: RpcLimits);
async fn get_status_unversioned(&self) -> Result<RpcStatusInfo, RpcError>;
}
#[derive(Debug, Clone, Copy)]
pub enum RpcEndpoint {
Status,
CheckNullifiers,
SyncNullifiers,
GetAccount,
GetBlockByNumber,
GetBlockHeaderByNumber,
GetNotesById,
SyncChainMmr,
SubmitProvenTx,
SyncNotes,
GetNoteScriptByRoot,
SyncStorageMaps,
SyncAccountVault,
SyncTransactions,
GetLimits,
}
impl RpcEndpoint {
pub fn proto_name(&self) -> &'static str {
match self {
RpcEndpoint::Status => "Status",
RpcEndpoint::CheckNullifiers => "CheckNullifiers",
RpcEndpoint::SyncNullifiers => "SyncNullifiers",
RpcEndpoint::GetAccount => "GetAccount",
RpcEndpoint::GetBlockByNumber => "GetBlockByNumber",
RpcEndpoint::GetBlockHeaderByNumber => "GetBlockHeaderByNumber",
RpcEndpoint::GetNotesById => "GetNotesById",
RpcEndpoint::SyncChainMmr => "SyncChainMmr",
RpcEndpoint::SubmitProvenTx => "SubmitProvenTransaction",
RpcEndpoint::SyncNotes => "SyncNotes",
RpcEndpoint::GetNoteScriptByRoot => "GetNoteScriptByRoot",
RpcEndpoint::SyncStorageMaps => "SyncStorageMaps",
RpcEndpoint::SyncAccountVault => "SyncAccountVault",
RpcEndpoint::SyncTransactions => "SyncTransactions",
RpcEndpoint::GetLimits => "GetLimits",
}
}
}
impl fmt::Display for RpcEndpoint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RpcEndpoint::Status => write!(f, "status"),
RpcEndpoint::CheckNullifiers => write!(f, "check_nullifiers"),
RpcEndpoint::SyncNullifiers => {
write!(f, "sync_nullifiers")
},
RpcEndpoint::GetAccount => write!(f, "get_account_proof"),
RpcEndpoint::GetBlockByNumber => write!(f, "get_block_by_number"),
RpcEndpoint::GetBlockHeaderByNumber => {
write!(f, "get_block_header_by_number")
},
RpcEndpoint::GetNotesById => write!(f, "get_notes_by_id"),
RpcEndpoint::SyncChainMmr => write!(f, "sync_chain_mmr"),
RpcEndpoint::SubmitProvenTx => write!(f, "submit_proven_transaction"),
RpcEndpoint::SyncNotes => write!(f, "sync_notes"),
RpcEndpoint::GetNoteScriptByRoot => write!(f, "get_note_script_by_root"),
RpcEndpoint::SyncStorageMaps => write!(f, "sync_storage_maps"),
RpcEndpoint::SyncAccountVault => write!(f, "sync_account_vault"),
RpcEndpoint::SyncTransactions => write!(f, "sync_transactions"),
RpcEndpoint::GetLimits => write!(f, "get_limits"),
}
}
}