diff --git a/crates/rpc/src/server/api.rs b/crates/rpc/src/server/api.rs index f2a88cc05..6d5ca862e 100644 --- a/crates/rpc/src/server/api.rs +++ b/crates/rpc/src/server/api.rs @@ -152,8 +152,13 @@ impl RpcService { } } +// API IMPLEMENTATION +// ================================================================================================ + #[tonic::async_trait] impl api_server::Api for RpcService { + // -- Nullifier endpoints ----------------------------------------------------------------- + async fn check_nullifiers( &self, request: Request, @@ -183,6 +188,8 @@ impl api_server::Api for RpcService { self.store.clone().sync_nullifiers(request).await } + // -- Block endpoints --------------------------------------------------------------------- + async fn get_block_header_by_number( &self, request: Request, @@ -192,6 +199,17 @@ impl api_server::Api for RpcService { self.store.clone().get_block_header_by_number(request).await } + async fn get_block_by_number( + &self, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + + debug!(target: COMPONENT, ?request); + + self.store.clone().get_block_by_number(request).await + } + async fn sync_chain_mmr( &self, request: Request, @@ -201,14 +219,7 @@ impl api_server::Api for RpcService { self.store.clone().sync_chain_mmr(request).await } - async fn sync_account_storage_maps( - &self, - request: Request, - ) -> Result, Status> { - debug!(target: COMPONENT, request = ?request.get_ref()); - - self.store.clone().sync_account_storage_maps(request).await - } + // -- Note endpoints ---------------------------------------------------------------------- async fn sync_notes( &self, @@ -242,6 +253,26 @@ impl api_server::Api for RpcService { self.store.clone().get_notes_by_id(request).await } + async fn get_note_script_by_root( + &self, + request: Request, + ) -> Result, Status> { + debug!(target: COMPONENT, request = ?request); + + self.store.clone().get_note_script_by_root(request).await + } + + // -- Account endpoints ------------------------------------------------------------------- + + async fn sync_account_storage_maps( + &self, + request: Request, + ) -> Result, Status> { + debug!(target: COMPONENT, request = ?request.get_ref()); + + self.store.clone().sync_account_storage_maps(request).await + } + async fn sync_account_vault( &self, request: tonic::Request, @@ -252,6 +283,41 @@ impl api_server::Api for RpcService { self.store.clone().sync_account_vault(request).await } + /// Validates storage map key limits before forwarding the account request to the store. + async fn get_account( + &self, + request: Request, + ) -> Result, Status> { + use proto::rpc::account_request::account_detail_request::storage_map_detail_request::{ + SlotData::AllEntries as ProtoMapAllEntries, SlotData::MapKeys as ProtoMapKeys, + }; + + let request = request.into_inner(); + + debug!(target: COMPONENT, ?request); + + // Validate total storage map key limit before forwarding to store + if let Some(details) = &request.details { + let total_keys: usize = details + .storage_maps + .iter() + .filter_map(|m| m.slot_data.as_ref()) + .filter_map(|d| match d { + ProtoMapKeys(keys) => Some(keys.map_keys.len()), + ProtoMapAllEntries(_) => None, + }) + .sum(); + check::(total_keys)?; + } + + self.store.clone().get_account(request).await + } + + // -- Transaction submission -------------------------------------------------------------- + + /// Deserializes and rebuilds the transaction with MAST decorators stripped from output note + /// scripts, verifies the transaction proof, optionally re-executes via the validator if + /// transaction inputs are provided, then forwards the transaction to the block producer. async fn submit_proven_transaction( &self, request: Request, @@ -285,18 +351,7 @@ impl api_server::Api for RpcService { .account_update_details(tx.account_update().details().clone()) .add_input_notes(tx.input_notes().iter().cloned()); - let stripped_outputs = tx.output_notes().iter().map(|note| match note { - OutputNote::Full(note) => { - let mut mast = note.script().mast().clone(); - Arc::make_mut(&mut mast).strip_decorators(); - let script = NoteScript::from_parts(mast, note.script().entrypoint()); - let recipient = - NoteRecipient::new(note.serial_num(), script, note.storage().clone()); - let new_note = Note::new(note.assets().clone(), note.metadata().clone(), recipient); - OutputNote::Full(new_note) - }, - other => other.clone(), - }); + let stripped_outputs = strip_output_note_decorators(tx.output_notes().iter()); builder = builder.add_output_notes(stripped_outputs); let rebuilt_tx = builder.build().map_err(|e| Status::invalid_argument(e.to_string()))?; let mut request = request; @@ -330,6 +385,8 @@ impl api_server::Api for RpcService { block_producer.clone().submit_proven_transaction(request).await } + /// Deserializes the batch, strips MAST decorators from full output note scripts, rebuilds + /// the batch, then forwards it to the block producer. async fn submit_proven_batch( &self, request: tonic::Request, @@ -344,23 +401,8 @@ impl api_server::Api for RpcService { .map_err(|err| Status::invalid_argument(err.as_report_context("invalid batch")))?; // Build a new batch with output notes' decorators removed - let stripped_outputs: Vec = batch - .output_notes() - .iter() - .map(|note| match note { - OutputNote::Full(note) => { - let mut mast = note.script().mast().clone(); - Arc::make_mut(&mut mast).strip_decorators(); - let script = NoteScript::from_parts(mast, note.script().entrypoint()); - let recipient = - NoteRecipient::new(note.serial_num(), script, note.storage().clone()); - let new_note = - Note::new(note.assets().clone(), note.metadata().clone(), recipient); - OutputNote::Full(new_note) - }, - other => other.clone(), - }) - .collect(); + let stripped_outputs: Vec = + strip_output_note_decorators(batch.output_notes().iter()).collect(); let rebuilt_batch = ProvenBatch::new( batch.id(), @@ -388,44 +430,17 @@ impl api_server::Api for RpcService { block_producer.clone().submit_proven_batch(request).await } - async fn get_block_by_number( - &self, - request: Request, - ) -> Result, Status> { - let request = request.into_inner(); - - debug!(target: COMPONENT, ?request); + // -- Status & utility endpoints ---------------------------------------------------------- - self.store.clone().get_block_by_number(request).await - } - - async fn get_account( + async fn sync_transactions( &self, - request: Request, - ) -> Result, Status> { - use proto::rpc::account_request::account_detail_request::storage_map_detail_request::{ - SlotData::AllEntries as ProtoMapAllEntries, SlotData::MapKeys as ProtoMapKeys, - }; - - let request = request.into_inner(); - - debug!(target: COMPONENT, ?request); + request: Request, + ) -> Result, Status> { + debug!(target: COMPONENT, request = ?request); - // Validate total storage map key limit before forwarding to store - if let Some(details) = &request.details { - let total_keys: usize = details - .storage_maps - .iter() - .filter_map(|m| m.slot_data.as_ref()) - .filter_map(|d| match d { - ProtoMapKeys(keys) => Some(keys.map_keys.len()), - ProtoMapAllEntries(_) => None, - }) - .sum(); - check::(total_keys)?; - } + check::(request.get_ref().account_ids.len())?; - self.store.clone().get_account(request).await + self.store.clone().sync_transactions(request).await } async fn status( @@ -464,26 +479,6 @@ impl api_server::Api for RpcService { })) } - async fn get_note_script_by_root( - &self, - request: Request, - ) -> Result, Status> { - debug!(target: COMPONENT, request = ?request); - - self.store.clone().get_note_script_by_root(request).await - } - - async fn sync_transactions( - &self, - request: Request, - ) -> Result, Status> { - debug!(target: COMPONENT, request = ?request); - - check::(request.get_ref().account_ids.len())?; - - self.store.clone().sync_transactions(request).await - } - async fn get_limits( &self, request: Request<()>, @@ -494,6 +489,29 @@ impl api_server::Api for RpcService { } } +// HELPERS +// ================================================================================================ + +/// Strips decorators from full output notes' scripts. +/// +/// This removes MAST decorators from note scripts before forwarding to the block producer, +/// as decorators are not needed for transaction processing. +fn strip_output_note_decorators<'a>( + notes: impl Iterator + 'a, +) -> impl Iterator + 'a { + notes.map(|note| match note { + OutputNote::Full(note) => { + let mut mast = note.script().mast().clone(); + Arc::make_mut(&mut mast).strip_decorators(); + let script = NoteScript::from_parts(mast, note.script().entrypoint()); + let recipient = NoteRecipient::new(note.serial_num(), script, note.storage().clone()); + let new_note = Note::new(note.assets().clone(), note.metadata().clone(), recipient); + OutputNote::Full(new_note) + }, + other => other.clone(), + }) +} + // LIMIT HELPERS // ================================================================================================