Skip to content

Commit

Permalink
feat: join TonicRpcClient with WebTonicRpcClient and added missin…
Browse files Browse the repository at this point in the history
…g endpoints
  • Loading branch information
tomyrd committed Feb 19, 2025
1 parent d5fb5f1 commit 083dcf4
Show file tree
Hide file tree
Showing 11 changed files with 386 additions and 452 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## 0.8.0 (TBD)

### Features

* [BREAKING] Merged `TonicRpcClient` with `WebTonicRpcClient` and added missing endpoints (#744).

### Changes

* Add check for empty pay to id notes (#714).
Expand Down
2 changes: 1 addition & 1 deletion crates/rust-client/src/account/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ impl<R: FeltRng> Client<R> {
// If the tracked account is locked, check that the account hash matches the one
// in the network
let network_account_hash =
self.rpc_api.get_account_update(account.id()).await?.hash();
self.rpc_api.get_account_details(account.id()).await?.hash();
if network_account_hash != account.hash() {
return Err(ClientError::AccountHashMismatch(network_account_hash));
}
Expand Down
37 changes: 32 additions & 5 deletions crates/rust-client/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ use std::env::temp_dir;
use async_trait::async_trait;
use miden_lib::transaction::TransactionKernel;
use miden_objects::{
account::{AccountCode, AccountId},
account::{AccountCode, AccountDelta, AccountId},
asset::{FungibleAsset, NonFungibleAsset},
block::{Block, BlockHeader, BlockNumber},
crypto::{
merkle::{Mmr, MmrProof},
merkle::{Mmr, MmrProof, SmtProof},
rand::RpoRandomCoin,
},
note::{Note, NoteId, NoteLocation, NoteTag},
note::{Note, NoteId, NoteLocation, NoteTag, Nullifier},
testing::{
account_id::{ACCOUNT_ID_NON_FUNGIBLE_FAUCET_OFF_CHAIN, ACCOUNT_ID_OFF_CHAIN_SENDER},
note::NoteBuilder,
Expand Down Expand Up @@ -284,7 +284,7 @@ impl NodeRpcClient for MockRpcApi {
Ok(())
}

async fn get_account_update(
async fn get_account_details(
&mut self,
_account_id: AccountId,
) -> Result<AccountDetails, RpcError> {
Expand All @@ -303,10 +303,37 @@ impl NodeRpcClient for MockRpcApi {
async fn check_nullifiers_by_prefix(
&mut self,
_prefix: &[u16],
) -> Result<Vec<(miden_objects::note::Nullifier, u32)>, RpcError> {
) -> Result<Vec<(Nullifier, u32)>, RpcError> {
// Always return an empty list for now since it's only used when importing
Ok(vec![])
}

async fn check_nullifiers(
&mut self,
_nullifiers: &[Nullifier],
) -> Result<Vec<SmtProof>, RpcError> {
panic!("shouldn't be used for now")
}

async fn get_account_state_delta(
&mut self,
_account_id: AccountId,
_from_block: BlockNumber,
_to_block: BlockNumber,
) -> Result<AccountDelta, RpcError> {
panic!("shouldn't be used for now")
}

async fn get_block_by_number(&mut self, block_num: BlockNumber) -> Result<Block, RpcError> {
let block = self
.blocks
.iter()
.find(|b| b.header().block_num() == block_num)
.unwrap()
.clone();

Ok(block)
}
}

// HELPERS
Expand Down
1 change: 1 addition & 0 deletions crates/rust-client/src/rpc/domain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod digest;
pub mod merkle;
pub mod note;
pub mod nullifier;
pub mod smt;
pub mod sync;
pub mod transaction;

Expand Down
126 changes: 126 additions & 0 deletions crates/rust-client/src/rpc/domain/smt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use alloc::string::ToString;

use miden_objects::{
crypto::merkle::{LeafIndex, SmtLeaf, SmtProof, SMT_DEPTH},
Digest, Word,
};

use super::MissingFieldHelper;
use crate::rpc::{errors::RpcConversionError, generated};

// SMT LEAF ENTRY
// ================================================================================================

impl From<&(Digest, Word)> for generated::smt::SmtLeafEntry {
fn from(value: &(Digest, Word)) -> Self {
generated::smt::SmtLeafEntry {
key: Some(value.0.into()),
value: Some(Digest::new(value.1).into()),
}
}
}

impl TryFrom<&generated::smt::SmtLeafEntry> for (Digest, Word) {
type Error = RpcConversionError;

fn try_from(value: &generated::smt::SmtLeafEntry) -> Result<Self, Self::Error> {
let key = match value.key {
Some(key) => key.try_into()?,
None => return Err(generated::smt::SmtLeafEntry::missing_field("key")),
};

let value: Digest = match value.value {
Some(value) => value.try_into()?,
None => return Err(generated::smt::SmtLeafEntry::missing_field("value")),
};

Ok((key, value.into()))
}
}

// SMT LEAF
// ================================================================================================

impl From<SmtLeaf> for generated::smt::SmtLeaf {
fn from(value: SmtLeaf) -> Self {
(&value).into()
}
}

impl From<&SmtLeaf> for generated::smt::SmtLeaf {
fn from(value: &SmtLeaf) -> Self {
match value {
SmtLeaf::Empty(index) => generated::smt::SmtLeaf {
leaf: Some(generated::smt::smt_leaf::Leaf::Empty(index.value())),
},
SmtLeaf::Single(entry) => generated::smt::SmtLeaf {
leaf: Some(generated::smt::smt_leaf::Leaf::Single(entry.into())),
},
SmtLeaf::Multiple(entries) => generated::smt::SmtLeaf {
leaf: Some(generated::smt::smt_leaf::Leaf::Multiple(
generated::smt::SmtLeafEntries {
entries: entries.iter().map(Into::into).collect(),
},
)),
},
}
}
}

impl TryFrom<&generated::smt::SmtLeaf> for SmtLeaf {
type Error = RpcConversionError;

fn try_from(value: &generated::smt::SmtLeaf) -> Result<Self, Self::Error> {
match &value.leaf {
Some(generated::smt::smt_leaf::Leaf::Empty(index)) => Ok(SmtLeaf::Empty(
LeafIndex::<SMT_DEPTH>::new(*index)
.map_err(|err| RpcConversionError::InvalidField(err.to_string()))?,
)),
Some(generated::smt::smt_leaf::Leaf::Single(entry)) => {
Ok(SmtLeaf::Single(entry.try_into()?))
},
Some(generated::smt::smt_leaf::Leaf::Multiple(entries)) => {
let entries =
entries.entries.iter().map(TryInto::try_into).collect::<Result<_, _>>()?;
Ok(SmtLeaf::Multiple(entries))
},
None => Err(generated::smt::SmtLeaf::missing_field("leaf")),
}
}
}

// SMT PROOF
// ================================================================================================

impl From<SmtProof> for generated::smt::SmtOpening {
fn from(value: SmtProof) -> Self {
(&value).into()
}
}

impl From<&SmtProof> for generated::smt::SmtOpening {
fn from(value: &SmtProof) -> Self {
generated::smt::SmtOpening {
leaf: Some(value.leaf().into()),
path: Some(value.path().into()),
}
}
}

impl TryFrom<&generated::smt::SmtOpening> for SmtProof {
type Error = RpcConversionError;

fn try_from(value: &generated::smt::SmtOpening) -> Result<Self, Self::Error> {
let leaf = match &value.leaf {
Some(leaf) => leaf.try_into()?,
None => return Err(generated::smt::SmtOpening::missing_field("leaf")),
};

let path = match &value.path {
Some(path) => path.try_into()?,
None => return Err(generated::smt::SmtOpening::missing_field("path")),
};

SmtProof::new(path, leaf).map_err(|err| RpcConversionError::InvalidField(err.to_string()))
}
}
2 changes: 2 additions & 0 deletions crates/rust-client/src/rpc/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub enum RpcConversionError {
NotAValidFelt,
#[error("invalid note type value")]
NoteTypeError(#[from] NoteError),
#[error("failed to convert rpc data: {0}")]
InvalidField(String),
#[error("field `{field_name}` expected to be present in protobuf representation of {entity}")]
MissingFieldInProtobufRepresentation {
entity: &'static str,
Expand Down
49 changes: 36 additions & 13 deletions crates/rust-client/src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ use domain::{
sync::StateSyncInfo,
};
use miden_objects::{
account::{Account, AccountCode, AccountHeader, AccountId},
block::{BlockHeader, BlockNumber},
crypto::merkle::MmrProof,
account::{Account, AccountCode, AccountDelta, AccountHeader, AccountId},
block::{Block, BlockHeader, BlockNumber},
crypto::merkle::{MmrProof, SmtProof},
note::{NoteId, NoteTag, Nullifier},
transaction::ProvenTransaction,
};
Expand All @@ -72,16 +72,9 @@ mod generated;
#[cfg(test)]
pub mod generated;

#[cfg(feature = "tonic")]
mod tonic_client;
#[cfg(feature = "tonic")]
pub use tonic_client::TonicRpcClient;

#[cfg(feature = "web-tonic")]
mod web_tonic_client;
#[cfg(feature = "web-tonic")]
pub use web_tonic_client::WebTonicRpcClient;

use crate::{
store::{input_note_states::UnverifiedNoteState, InputNoteRecord},
sync::get_nullifier_prefix,
Expand Down Expand Up @@ -117,6 +110,10 @@ pub trait NodeRpcClient {
include_mmr_proof: bool,
) -> Result<(BlockHeader, Option<MmrProof>), RpcError>;

/// Given a block number, fetches the block corresponding to that height from the node using
/// the `/GetBlockByNumber` RPC endpoint.
async fn get_block_by_number(&mut self, block_num: BlockNumber) -> Result<Block, RpcError>;

/// Fetches note-related data for a list of [NoteId] using the `/GetNotesById` rpc endpoint.
///
/// For any NoteType::Private note, the return data is only the
Expand Down Expand Up @@ -148,11 +145,15 @@ pub trait NodeRpcClient {
/// endpoint.
///
/// - `account_id` is the ID of the wanted account.
async fn get_account_update(
async fn get_account_details(
&mut self,
account_id: AccountId,
) -> Result<AccountDetails, RpcError>;

/// Fetches the notes related to the specified tags using the `/SyncNote` RPC endpoint.
///
/// - `block_num` is the last block number known by the client.
/// - `note_tags` is a list of tags used to filter the notes the client is interested in.
async fn sync_notes(
&mut self,
block_num: BlockNumber,
Expand All @@ -166,6 +167,13 @@ pub trait NodeRpcClient {
prefix: &[u16],
) -> Result<Vec<(Nullifier, u32)>, RpcError>;

/// Fetches the nullifier proofs corresponding to a list of nullifiers using the
/// `/CheckNullifiers` RPC endpoint.
async fn check_nullifiers(
&mut self,
nullifiers: &[Nullifier],
) -> Result<Vec<SmtProof>, RpcError>;

/// Fetches the account data needed to perform a Foreign Procedure Invocation (FPI) on the
/// specified foreign accounts, using the `GetAccountProofs` endpoint.
///
Expand All @@ -178,6 +186,15 @@ pub trait NodeRpcClient {
known_account_codes: Vec<AccountCode>,
) -> Result<AccountProofs, RpcError>;

/// Fetches the account state delta for the specified account between the specified blocks
/// using the `/GetAccountStateDelta` RPC endpoint.
async fn get_account_state_delta(
&mut self,
account_id: AccountId,
from_block: BlockNumber,
to_block: BlockNumber,
) -> Result<AccountDelta, RpcError>;

/// Fetches the commit height where the nullifier was consumed. If the nullifier isn't found,
/// then `None` is returned.
///
Expand Down Expand Up @@ -227,15 +244,15 @@ pub trait NodeRpcClient {
/// The `local_accounts` parameter is a list of account headers that the client has
/// stored locally and that it wants to check for updates. If an account is private or didn't
/// change, it is ignored and will not be included in the returned list.
/// The default implementation of this method uses [NodeRpcClient::get_account_update].
/// The default implementation of this method uses [NodeRpcClient::get_account_details].
async fn get_updated_public_accounts(
&mut self,
local_accounts: &[&AccountHeader],
) -> Result<Vec<Account>, RpcError> {
let mut public_accounts = vec![];

for local_account in local_accounts {
let response = self.get_account_update(local_account.id()).await?;
let response = self.get_account_details(local_account.id()).await?;

if let AccountDetails::Public(account, _) = response {
// We should only return an account if it's newer, otherwise we ignore it
Expand Down Expand Up @@ -278,9 +295,12 @@ pub trait NodeRpcClient {
/// RPC methods for the Miden protocol.
#[derive(Debug)]
pub enum NodeRpcClientEndpoint {
CheckNullifiers,
CheckNullifiersByPrefix,
GetAccountDetails,
GetAccountStateDelta,
GetAccountProofs,
GetBlockByNumber,
GetBlockHeaderByNumber,
SyncState,
SubmitProvenTx,
Expand All @@ -290,11 +310,14 @@ pub enum NodeRpcClientEndpoint {
impl fmt::Display for NodeRpcClientEndpoint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NodeRpcClientEndpoint::CheckNullifiers => write!(f, "check_nullifiers"),
NodeRpcClientEndpoint::CheckNullifiersByPrefix => {
write!(f, "check_nullifiers_by_prefix")
},
NodeRpcClientEndpoint::GetAccountDetails => write!(f, "get_account_details"),
NodeRpcClientEndpoint::GetAccountStateDelta => write!(f, "get_account_state_delta"),
NodeRpcClientEndpoint::GetAccountProofs => write!(f, "get_account_proofs"),
NodeRpcClientEndpoint::GetBlockByNumber => write!(f, "get_block_by_number"),
NodeRpcClientEndpoint::GetBlockHeaderByNumber => {
write!(f, "get_block_header_by_number")
},
Expand Down
Loading

0 comments on commit 083dcf4

Please sign in to comment.