use miden_node_proto::{DecodeMessage, Verify, generated as proto};
use miden_node_store::DatabaseError;
use miden_node_tracing::{debug, miden_instrument, miden_span_record};
use miden_node_utils::limiter::QueryParamNullifierPrefixLimit;
use super::error_codes::SyncNullifiersErrorCode;
use super::{RpcService, check, database_error_to_status, invalid_block_range_to_status};
use crate::{COMPONENT, LOG_TARGET};
#[tonic::async_trait]
impl proto::server::miden_node_v1_node_service::SyncNullifiers for RpcService {
type Input = proto::miden::node::v1::DecodedSyncNullifiersRequest;
type Output = proto::miden::node::v1::SyncNullifiersResponse;
fn decode(
request: proto::miden::node::v1::SyncNullifiersRequest,
) -> tonic::Result<Self::Input> {
request
.decode_fields()
.map_err(|err| SyncNullifiersErrorCode::DeserializationFailed.invalid_argument(err))
}
fn encode(
output: Self::Output,
) -> tonic::Result<proto::miden::node::v1::SyncNullifiersResponse> {
Ok(output)
}
#[miden_instrument(
target = COMPONENT,
name = "sync_nullifiers",
err,
)]
async fn handle(
&self,
request: Self::Input,
_metadata: &tonic::metadata::MetadataMap,
_extensions: &tonic::codegen::http::Extensions,
) -> tonic::Result<Self::Output> {
let range = request.block_range;
let nullifiers = request.nullifiers.into_inner();
miden_span_record!(
block_range.from = range.block_from,
block_range.to = range.block_to,
prefix_len = request.prefix_len,
prefixes = nullifiers.as_slice() #[nonstandard]
);
debug!(
target: LOG_TARGET,
"Syncing nullifiers",
block_range.from = range.block_from,
block_range.to = range.block_to,
prefix_len = request.prefix_len,
prefixes = nullifiers.as_slice() #[nonstandard]
);
check::<QueryParamNullifierPrefixLimit>(nullifiers.len())?;
if request.prefix_len != 16 {
return Err(SyncNullifiersErrorCode::InvalidPrefixLength.invalid_argument(format!(
"unsupported prefix length: {} (only 16-bit prefixes are supported)",
request.prefix_len
)));
}
if let Some(&prefix) = nullifiers.iter().find(|&&prefix| prefix > u32::from(u16::MAX)) {
return Err(SyncNullifiersErrorCode::DeserializationFailed.invalid_argument(format!(
"nullifier prefix {prefix} does not fit in the requested prefix length of 16 bits"
)));
}
let block_range = range.verify().map_err(invalid_block_range_to_status)?;
let (chain_tip, (nullifiers, block_num)) = self
.state
.with_view(async |view| {
view.sync_nullifiers(request.prefix_len, nullifiers, block_range)
.await
.map(|nullifiers| (view.tip(), nullifiers))
.map_err(|err| match err {
DatabaseError::RangeBeyondTip(_) => {
SyncNullifiersErrorCode::FutureBlock.invalid_argument(err)
},
err => database_error_to_status(&err),
})
})
.await?;
let nullifiers = nullifiers
.into_iter()
.map(|nullifier_info| {
proto::miden::node::v1::sync_nullifiers_response::NullifierUpdate {
nullifier: Some(nullifier_info.nullifier.as_word().into()),
block_num: nullifier_info.block_num.as_u32(),
}
})
.collect();
Ok(proto::miden::node::v1::SyncNullifiersResponse {
pagination_info: Some(proto::miden::node::v1::PaginationInfo {
chain_tip: chain_tip.as_u32(),
block_num: block_num.as_u32(),
}),
nullifiers,
})
}
}