use std::sync::atomic::Ordering;
use miden_node_proto::generated as grpc;
use miden_node_utils::ErrorReport;
use miden_node_utils::tracing::miden_instrument;
use miden_protocol::Word;
use miden_protocol::block::{BlockNumber, ProposedBlock};
use miden_protocol::crypto::dsa::ecdsa_k256_keccak::{PublicKey, Signature};
use miden_tx::utils::serde::{Deserializable, Serializable};
use tracing::{Instrument, info_span};
use super::ValidatorService;
use crate::COMPONENT;
#[tonic::async_trait]
impl grpc::server::validator_api::SignBlock for ValidatorService {
type Input = ProposedBlock;
type Output = (Signature, Word, PublicKey);
#[miden_instrument(
target = COMPONENT,
err,
)]
fn decode(request: grpc::blockchain::ProposedBlock) -> tonic::Result<Self::Input> {
ProposedBlock::read_from_bytes(&request.proposed_block).map_err(|err| {
tonic::Status::invalid_argument(
err.as_report_context("Failed to deserialize proposed block"),
)
})
}
#[miden_instrument(
target = COMPONENT,
err,
)]
fn encode(output: Self::Output) -> tonic::Result<grpc::blockchain::SignBlockResponse> {
let (signature, block_commitment, public_key) = output;
Ok(grpc::blockchain::SignBlockResponse {
signature: Some(grpc::blockchain::BlockSignature { signature: signature.to_bytes() }),
block_commitment: Some(block_commitment.into()),
public_key: Some((&public_key).into()),
})
}
async fn handle(
&self,
proposed_block: Self::Input,
_metadata: &tonic::metadata::MetadataMap,
_extensions: &tonic::codegen::http::Extensions,
) -> tonic::Result<Self::Output> {
let _guard = self.serve_lock.try_read().map_err(|_| {
tonic::Status::resource_exhausted("validator is busy streaming a backup")
})?;
let _permit = self
.sign_block_semaphore
.acquire()
.instrument(info_span!("acquire_permit"))
.await
.map_err(|err| {
tonic::Status::internal(format!("sign_block semaphore closed: {err}"))
})?;
let chain_tip = self
.db
.load_chain_tip()
.await
.map_err(|err| {
tonic::Status::internal(format!("Failed to load chain tip: {}", err.as_report()))
})?
.ok_or_else(|| tonic::Status::internal("Chain tip not found in database"))?;
let (signature, header) =
self.validate_block(proposed_block, chain_tip).await.map_err(|err| {
tonic::Status::invalid_argument(format!(
"Failed to validate block: {}",
err.as_report()
))
})?;
let block_commitment = header.commitment();
let new_block_num = header.block_num().as_u32();
self.db.upsert_block_header(header).await.map_err(|err| {
tonic::Status::internal(format!("Failed to persist block header: {}", err.as_report()))
})?;
self.committed_tip.send_replace(BlockNumber::from(new_block_num));
self.signed_blocks_count.fetch_add(1, Ordering::Relaxed);
Ok((signature, block_commitment, self.signer.public_key()))
}
}