miden-node-rpc 0.17.0-rc.3

Miden node's front-end RPC server
Documentation
use miden_node_proto::domain::account::RegisterAccountRequest;
use miden_node_proto::errors::ConversionError;
use miden_node_proto::{DecodeMessageExt, generated as proto};
use miden_node_store::allowlist::{AllowlistError, InvitationCode, RegistrationOutcome};
use miden_node_tracing::{ErrorReport, miden_instrument, miden_span_record};
use tonic::{Code, Request, Status};

use super::{RpcBackend, RpcService};
use crate::COMPONENT;

#[tonic::async_trait]
impl proto::server::rpc_api::RegisterAccount for RpcService {
    type Input = RegisterAccountRequest;
    type Output = ();

    fn decode(request: proto::rpc::RegisterAccountRequest) -> tonic::Result<Self::Input> {
        request.decode_and_verify().map_err(ConversionError::into_status)
    }

    fn encode((): Self::Output) -> tonic::Result<()> {
        Ok(())
    }

    #[miden_instrument(target = COMPONENT, name = "register_account", err)]
    async fn handle(
        &self,
        request: Self::Input,
        metadata: &tonic::metadata::MetadataMap,
        _extensions: &tonic::codegen::http::Extensions,
    ) -> tonic::Result<Self::Output> {
        let account_id = request.account_id;
        miden_span_record!(account.id = account_id);

        match &self.backend {
            RpcBackend::Sequencer { account_admission, .. } => {
                let registered = if account_admission.is_disabled() {
                    account_admission.allowlist.add_account(account_id).await.map_err(|error| {
                        Status::internal(AllowlistError::Database(error).as_report())
                    })?
                } else {
                    let invitation = InvitationCode::new(&request.invitation_code)
                        .map_err(|error| Status::invalid_argument(error.to_string()))?;
                    let outcome = account_admission
                        .allowlist
                        .register_account(invitation, account_id)
                        .await
                        .map_err(|error| {
                            let code = match &error {
                                AllowlistError::InvitationNotFound => Code::NotFound,
                                AllowlistError::InvitationAlreadyUsed
                                | AllowlistError::AccountAlreadyRegistered(_) => {
                                    Code::AlreadyExists
                                },
                                AllowlistError::Database(_) => Code::Internal,
                            };
                            Status::new(code, error.as_report())
                        })?;
                    outcome == RegistrationOutcome::Registered
                };
                if registered && let Some(funding) = &account_admission.funding {
                    funding
                        .fund(account_id)
                        .await
                        .map_err(|error| Status::unavailable(error.as_report()))?;
                }
                Ok(())
            },
            RpcBackend::FullNode { source_rpc, .. } => {
                let mut request = Request::new(proto::rpc::RegisterAccountRequest {
                    account_id: Some(account_id.into()),
                    invitation_code: request.invitation_code,
                });
                if let Some(accept) = metadata.get(http::header::ACCEPT.as_str()) {
                    request.metadata_mut().insert(http::header::ACCEPT.as_str(), accept.clone());
                }
                source_rpc.as_ref().clone().register_account(request).await.map(|_| ())
            },
        }
    }
}