From b00cbb5c27f8375e90768b4b94a347e353a65029 Mon Sep 17 00:00:00 2001 From: Shubbu03 Date: Tue, 2 Dec 2025 14:16:29 +0530 Subject: [PATCH 1/4] feat: add magic router-compatible RPC methods to validator --- .../http/get_blockhash_for_accounts.rs | 18 +++++++++ .../requests/http/get_delegation_status.rs | 39 +++++++++++++++++++ .../src/requests/http/get_routes.rs | 15 +++++++ magicblock-aperture/src/requests/http/mod.rs | 5 +++ magicblock-aperture/src/requests/mod.rs | 9 +++++ .../src/server/http/dispatch.rs | 3 ++ 6 files changed, 89 insertions(+) create mode 100644 magicblock-aperture/src/requests/http/get_blockhash_for_accounts.rs create mode 100644 magicblock-aperture/src/requests/http/get_delegation_status.rs create mode 100644 magicblock-aperture/src/requests/http/get_routes.rs diff --git a/magicblock-aperture/src/requests/http/get_blockhash_for_accounts.rs b/magicblock-aperture/src/requests/http/get_blockhash_for_accounts.rs new file mode 100644 index 000000000..989a27c69 --- /dev/null +++ b/magicblock-aperture/src/requests/http/get_blockhash_for_accounts.rs @@ -0,0 +1,18 @@ +use super::prelude::*; + +impl HttpDispatcher { + /// Handles the `getBlockhashForAccounts` RPC request. + /// + /// On the validator, this is intentionally implemented as an alias for + /// `getLatestBlockhash`. The method exists purely for API compatibility + /// with the Magic Router SDK and does not implement full router routing + /// logic. + pub(crate) fn get_blockhash_for_accounts( + &self, + request: &JsonRequest, + ) -> HandlerResult { + // We ignore any parameters and simply return the validator's latest + // blockhash, matching the behavior of `getLatestBlockhash`. + self.get_latest_blockhash(request) + } +} diff --git a/magicblock-aperture/src/requests/http/get_delegation_status.rs b/magicblock-aperture/src/requests/http/get_delegation_status.rs new file mode 100644 index 000000000..09d88f4f7 --- /dev/null +++ b/magicblock-aperture/src/requests/http/get_delegation_status.rs @@ -0,0 +1,39 @@ +use super::prelude::*; + +impl HttpDispatcher { + /// Handles the `getDelegationStatus` RPC request. + /// + /// Returns a minimal delegation status object of the form: + /// `{ "isDelegated": true | false }` + /// + /// The status is derived solely from the `AccountSharedData::delegated()` + /// flag of the local `AccountsDb`. No delegation record resolution or + /// router-style logic is performed here by design. + pub(crate) async fn get_delegation_status( + &self, + request: &mut JsonRequest, + ) -> HandlerResult { + // Parse single pubkey parameter from positional params without using + // the generic `parse_params!` helper to keep the logic simple and + // avoid tuple unpacking for this custom method. + let params = request.params()?; + // We expect exactly one positional parameter: the account pubkey. + let value = params.pop().ok_or_else(|| { + RpcError::invalid_params("missing pubkey parameter") + })?; + let pubkey_bytes: Serde32Bytes = + json::from_value(&value).map_err(RpcError::parse_error)?; + let pubkey: Pubkey = pubkey_bytes.into(); + + // Ensure the account is present in the local AccountsDb, cloning it + // from the reference cluster if necessary. + let account = self.read_account_with_ensure(&pubkey).await; + + let is_delegated = + account.as_ref().map(|acc| acc.delegated()).unwrap_or(false); + + let payload = json::json!({ "isDelegated": is_delegated }); + + Ok(ResponsePayload::encode_no_context(&request.id, payload)) + } +} diff --git a/magicblock-aperture/src/requests/http/get_routes.rs b/magicblock-aperture/src/requests/http/get_routes.rs new file mode 100644 index 000000000..44586278c --- /dev/null +++ b/magicblock-aperture/src/requests/http/get_routes.rs @@ -0,0 +1,15 @@ +use super::prelude::*; + +impl HttpDispatcher { + /// Handles the `getRoutes` RPC request. + /// + /// This is a validator-specific, mocked implementation used only to satisfy + /// the Magic Router / SDK API. A validator does not act as a router and + /// therefore always returns an empty list of routes. + pub(crate) fn get_routes(&self, request: &JsonRequest) -> HandlerResult { + Ok(ResponsePayload::encode_no_context( + &request.id, + json::json!([]), + )) + } +} diff --git a/magicblock-aperture/src/requests/http/mod.rs b/magicblock-aperture/src/requests/http/mod.rs index 4c68cfb6e..477f8f515 100644 --- a/magicblock-aperture/src/requests/http/mod.rs +++ b/magicblock-aperture/src/requests/http/mod.rs @@ -302,3 +302,8 @@ pub(crate) mod mocked; pub(crate) mod request_airdrop; pub(crate) mod send_transaction; pub(crate) mod simulate_transaction; + +// Magic Router compatibility methods. +pub(crate) mod get_blockhash_for_accounts; +pub(crate) mod get_delegation_status; +pub(crate) mod get_routes; diff --git a/magicblock-aperture/src/requests/mod.rs b/magicblock-aperture/src/requests/mod.rs index 1b4c14e77..87c4f4f7d 100644 --- a/magicblock-aperture/src/requests/mod.rs +++ b/magicblock-aperture/src/requests/mod.rs @@ -77,6 +77,12 @@ pub(crate) enum JsonRpcHttpMethod { RequestAirdrop, SendTransaction, SimulateTransaction, + /// Custom Magic Router-compatible method: mocked on validator. + GetRoutes, + /// Custom Magic Router-compatible method: alias of `getLatestBlockhash` on validator. + GetBlockhashForAccounts, + /// Custom Magic Router-compatible method: exposes simple delegation flag. + GetDelegationStatus, } /// All supported JSON-RPC Websocket method names. @@ -140,6 +146,9 @@ impl JsonRpcHttpMethod { Self::RequestAirdrop => "requestAirdrop", Self::SendTransaction => "sendTransaction", Self::SimulateTransaction => "simulateTransaction", + Self::GetRoutes => "getRoutes", + Self::GetBlockhashForAccounts => "getBlockhashForAccounts", + Self::GetDelegationStatus => "getDelegationStatus", } } } diff --git a/magicblock-aperture/src/server/http/dispatch.rs b/magicblock-aperture/src/server/http/dispatch.rs index 32955d2e7..cff4abd7b 100644 --- a/magicblock-aperture/src/server/http/dispatch.rs +++ b/magicblock-aperture/src/server/http/dispatch.rs @@ -226,6 +226,9 @@ impl HttpDispatcher { RequestAirdrop => self.request_airdrop(request).await, SendTransaction => self.send_transaction(request).await, SimulateTransaction => self.simulate_transaction(request).await, + GetRoutes => self.get_routes(request), + GetBlockhashForAccounts => self.get_blockhash_for_accounts(request), + GetDelegationStatus => self.get_delegation_status(request).await, } } From e4b83e1a2f3fee11a88a38339af96195847ecd5b Mon Sep 17 00:00:00 2001 From: Shubbu03 Date: Tue, 2 Dec 2025 15:41:44 +0530 Subject: [PATCH 2/4] fix: update parameter handling in get_delegation_status to use first positional parameter --- .../src/requests/http/get_delegation_status.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/magicblock-aperture/src/requests/http/get_delegation_status.rs b/magicblock-aperture/src/requests/http/get_delegation_status.rs index 09d88f4f7..0e74e4b0c 100644 --- a/magicblock-aperture/src/requests/http/get_delegation_status.rs +++ b/magicblock-aperture/src/requests/http/get_delegation_status.rs @@ -17,8 +17,8 @@ impl HttpDispatcher { // the generic `parse_params!` helper to keep the logic simple and // avoid tuple unpacking for this custom method. let params = request.params()?; - // We expect exactly one positional parameter: the account pubkey. - let value = params.pop().ok_or_else(|| { + // We expect the *first* positional parameter to be the account pubkey. + let value = params.first().cloned().ok_or_else(|| { RpcError::invalid_params("missing pubkey parameter") })?; let pubkey_bytes: Serde32Bytes = From c107fbd2393ce17d4aff244f0714693eca8dc860 Mon Sep 17 00:00:00 2001 From: Shubbu03 Date: Wed, 10 Dec 2025 10:54:43 +0530 Subject: [PATCH 3/4] refactor: addressed requested changes --- .../http/get_blockhash_for_accounts.rs | 18 ------------------ .../src/requests/http/get_delegation_status.rs | 14 +++----------- .../src/requests/http/get_routes.rs | 15 --------------- .../src/requests/http/mocked.rs | 9 +++++++++ magicblock-aperture/src/requests/http/mod.rs | 2 -- .../src/server/http/dispatch.rs | 3 ++- 6 files changed, 14 insertions(+), 47 deletions(-) delete mode 100644 magicblock-aperture/src/requests/http/get_blockhash_for_accounts.rs delete mode 100644 magicblock-aperture/src/requests/http/get_routes.rs diff --git a/magicblock-aperture/src/requests/http/get_blockhash_for_accounts.rs b/magicblock-aperture/src/requests/http/get_blockhash_for_accounts.rs deleted file mode 100644 index 989a27c69..000000000 --- a/magicblock-aperture/src/requests/http/get_blockhash_for_accounts.rs +++ /dev/null @@ -1,18 +0,0 @@ -use super::prelude::*; - -impl HttpDispatcher { - /// Handles the `getBlockhashForAccounts` RPC request. - /// - /// On the validator, this is intentionally implemented as an alias for - /// `getLatestBlockhash`. The method exists purely for API compatibility - /// with the Magic Router SDK and does not implement full router routing - /// logic. - pub(crate) fn get_blockhash_for_accounts( - &self, - request: &JsonRequest, - ) -> HandlerResult { - // We ignore any parameters and simply return the validator's latest - // blockhash, matching the behavior of `getLatestBlockhash`. - self.get_latest_blockhash(request) - } -} diff --git a/magicblock-aperture/src/requests/http/get_delegation_status.rs b/magicblock-aperture/src/requests/http/get_delegation_status.rs index 0e74e4b0c..006056c1e 100644 --- a/magicblock-aperture/src/requests/http/get_delegation_status.rs +++ b/magicblock-aperture/src/requests/http/get_delegation_status.rs @@ -13,17 +13,9 @@ impl HttpDispatcher { &self, request: &mut JsonRequest, ) -> HandlerResult { - // Parse single pubkey parameter from positional params without using - // the generic `parse_params!` helper to keep the logic simple and - // avoid tuple unpacking for this custom method. - let params = request.params()?; - // We expect the *first* positional parameter to be the account pubkey. - let value = params.first().cloned().ok_or_else(|| { - RpcError::invalid_params("missing pubkey parameter") - })?; - let pubkey_bytes: Serde32Bytes = - json::from_value(&value).map_err(RpcError::parse_error)?; - let pubkey: Pubkey = pubkey_bytes.into(); + let (pubkey,) = parse_params!(request.params()?, Serde32Bytes); + + let pubkey: Pubkey = some_or_err!(pubkey); // Ensure the account is present in the local AccountsDb, cloning it // from the reference cluster if necessary. diff --git a/magicblock-aperture/src/requests/http/get_routes.rs b/magicblock-aperture/src/requests/http/get_routes.rs deleted file mode 100644 index 44586278c..000000000 --- a/magicblock-aperture/src/requests/http/get_routes.rs +++ /dev/null @@ -1,15 +0,0 @@ -use super::prelude::*; - -impl HttpDispatcher { - /// Handles the `getRoutes` RPC request. - /// - /// This is a validator-specific, mocked implementation used only to satisfy - /// the Magic Router / SDK API. A validator does not act as a router and - /// therefore always returns an empty list of routes. - pub(crate) fn get_routes(&self, request: &JsonRequest) -> HandlerResult { - Ok(ResponsePayload::encode_no_context( - &request.id, - json::json!([]), - )) - } -} diff --git a/magicblock-aperture/src/requests/http/mocked.rs b/magicblock-aperture/src/requests/http/mocked.rs index 85a9c2725..134460f06 100644 --- a/magicblock-aperture/src/requests/http/mocked.rs +++ b/magicblock-aperture/src/requests/http/mocked.rs @@ -243,4 +243,13 @@ impl HttpDispatcher { }; Ok(ResponsePayload::encode_no_context(&request.id, status)) } + + /// Handles the `getRoutes` RPC request. + /// + /// This is a validator-specific, mocked implementation used only to satisfy + /// the Magic Router / SDK API. A validator does not act as a router and + /// therefore always returns an empty list of routes. + pub(crate) fn get_routes(&self, request: &JsonRequest) -> HandlerResult { + Ok(ResponsePayload::encode_no_context(&request.id, [])) + } } diff --git a/magicblock-aperture/src/requests/http/mod.rs b/magicblock-aperture/src/requests/http/mod.rs index 477f8f515..984b9e259 100644 --- a/magicblock-aperture/src/requests/http/mod.rs +++ b/magicblock-aperture/src/requests/http/mod.rs @@ -304,6 +304,4 @@ pub(crate) mod send_transaction; pub(crate) mod simulate_transaction; // Magic Router compatibility methods. -pub(crate) mod get_blockhash_for_accounts; pub(crate) mod get_delegation_status; -pub(crate) mod get_routes; diff --git a/magicblock-aperture/src/server/http/dispatch.rs b/magicblock-aperture/src/server/http/dispatch.rs index cff4abd7b..29569beca 100644 --- a/magicblock-aperture/src/server/http/dispatch.rs +++ b/magicblock-aperture/src/server/http/dispatch.rs @@ -227,7 +227,8 @@ impl HttpDispatcher { SendTransaction => self.send_transaction(request).await, SimulateTransaction => self.simulate_transaction(request).await, GetRoutes => self.get_routes(request), - GetBlockhashForAccounts => self.get_blockhash_for_accounts(request), + // Alias for getLatestBlockhash; exists for Magic Router SDK compatibility. + GetBlockhashForAccounts => self.get_latest_blockhash(request), GetDelegationStatus => self.get_delegation_status(request).await, } } From 5549072421f68e55b783aac1d797c303fe5d4ef6 Mon Sep 17 00:00:00 2001 From: Shubbu03 Date: Fri, 19 Dec 2025 15:30:54 +0530 Subject: [PATCH 4/4] refactor: improve parameter parsing and response handling in HTTP requests --- .../src/requests/http/get_delegation_status.rs | 5 +++-- magicblock-aperture/src/requests/http/mocked.rs | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/magicblock-aperture/src/requests/http/get_delegation_status.rs b/magicblock-aperture/src/requests/http/get_delegation_status.rs index 006056c1e..8ea976f79 100644 --- a/magicblock-aperture/src/requests/http/get_delegation_status.rs +++ b/magicblock-aperture/src/requests/http/get_delegation_status.rs @@ -13,8 +13,9 @@ impl HttpDispatcher { &self, request: &mut JsonRequest, ) -> HandlerResult { - let (pubkey,) = parse_params!(request.params()?, Serde32Bytes); - + // Parse the first positional parameter (account pubkey) using the + // standard helper macro, mirroring `get_account_info`. + let pubkey = parse_params!(request.params()?, Serde32Bytes); let pubkey: Pubkey = some_or_err!(pubkey); // Ensure the account is present in the local AccountsDb, cloning it diff --git a/magicblock-aperture/src/requests/http/mocked.rs b/magicblock-aperture/src/requests/http/mocked.rs index c2ed3683e..238811598 100644 --- a/magicblock-aperture/src/requests/http/mocked.rs +++ b/magicblock-aperture/src/requests/http/mocked.rs @@ -250,6 +250,9 @@ impl HttpDispatcher { /// the Magic Router / SDK API. A validator does not act as a router and /// therefore always returns an empty list of routes. pub(crate) fn get_routes(&self, request: &JsonRequest) -> HandlerResult { - Ok(ResponsePayload::encode_no_context(&request.id, [])) + Ok(ResponsePayload::encode_no_context( + &request.id, + Vec::<()>::new(), + )) } }