-
Notifications
You must be signed in to change notification settings - Fork 65
WIP: JSON-RPC server #3585
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
welf
wants to merge
229
commits into
master
Choose a base branch
from
json-rpc-server
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
WIP: JSON-RPC server #3585
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 tasks
This was
linked to
issues
Mar 25, 2025
Closed
f0973e1 to
123d757
Compare
bb569f5 to
bd7c86f
Compare
implementations, and to the AppState
…latest_blocks methods
get_blocks_count method and refactor it to return the number of finalized blocks + the number of candidate blocks if the flag is not set
add JSON-RPC block methods documentation to docs
Contributor
Author
|
@HDauven All JSON-RPC block methods are implemented and their documentation in Wiki is updated. The same documentation is located in the Implementation Plan in the PR description is also up to date. In a couple of days I'll implement JSON-RPC network methods. |
documentation of the jsonrpc::service::block module
module-level documentation of the jsonrpc::config module
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
Epic
A large feature encompassing multiple sub-issues
module:rusk
Issues related to rusk module
type:feature
implementing a new feature
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
JSON-RPC Server Implementation Plan
Closes #3353
Stage 0: Preparation
Stage 1: Core Infrastructure & HTTP Server
This stage focuses on setting up the core JSON-RPC components, configuration, error handling, shared state, basic infrastructure (DB adapter, metrics, manual rate limiting), and implementing the HTTP server with initial RPC method handling.
1. Project Setup & Configuration
1.1. Project Setup
rusk::jsonrpc.1.2. Core Configuration (
jsonrpc::config)JsonRpcConfig,HttpServerConfig,WebSocketServerConfig,RateLimitConfig,CorsConfig,MethodRateLimit,RateLimit,FeatureToggles,SanitizationConfig) with necessary fields and default values.JsonRpcConfigBuilderwith fluent methods.Default,Serialize,Deserializetraits for config structs.serdehelpers forSocketAddrandDuration.ConfigErrorenum usingthiserror.1.3. Configuration Loading (
jsonrpc::config)JsonRpcConfig::loadto handle defaults, TOML file parsing (from env var or argument), environment variable overrides, and validation.apply_env_overridesfor environment variable parsing and application.project_root,default_config_path).to_toml_stringandto_filefor config export.docs/jsonrpc_configuration.md).load_from_file_onlyfor testing.1.4. Configuration Enhancements & Validation
tracinglogs throughout configuration handling.JsonRpcConfig::validatewith sanity checks, TLS file validation, and security checks (public binding, CORS, limits, sanitization).is_binding_publicly, etc.).JsonRpcConfig::test_config()helper.JsonRpcConfig::build_cors_layer()helper fortower_httpintegration.2. Error Handling Adaptation (
jsonrpc::error)jsonrpc::error::Errorenum usingthiserrorwith appropriate variants andFromimplementations for underlying infrastructure, service, config, and serialization errors.Error::into_error_objectfor conversion tojsonrpsee::types::ErrorObjectOwned, mapping internal errors to JSON-RPC codes.sanitize_messagefunction with logic for term redaction, path sanitization, control character filtering, and truncation based onSanitizationConfig.Displayimplementations.3. Core Infrastructure (
jsonrpc::infrastructure)3.0 Data Models (
jsonrpc::model)This phase focuses on defining the data structures used for JSON-RPC request parameters and response payloads, ensuring they align with the API specifications and have proper serialization/deserialization and conversions from/to internal node data types.
archive.rs:ArchivedEventContractEventMoonlightGroupOrderblock.rs:BlockHeaderBlockBlockStatusCandidateBlockBlockLabelChainTipBlockFaultsFaultFaultTypeFaultItemchain.rs:ChainStatsconsensus.rs:ValidationResultQuorumTypeVoteTypegas.rs:GasPriceStatskey.rs:AccountPublicKeymempool.rs:MempoolInfonetwork.rs:PeersMetricsprovisioner.rs:ProvisionerInfoStakeOwnerInfoStakeInfo(alias forProvisionerInfo)transaction.rs:BaseTransactionTransactionTypeTransactionStatusTypeTransactionStatusEstimateTransactionFeeResponseMoonlightTransactionDataPhoenixTransactionDataTransactionDataTypeTransactionResponseMempoolTransactionTransactionInfoSimulationResultSpendingIdentifierSpendingIdTypeserde_helper.rs: Provides serialization helpers.contract.rs: Placeholder file.prover.rs: Placeholder file.subscription.rs: Placeholder file (Actual types ininfrastructure/subscription/types.rs).modelmodule have comprehensive documentation adhering to project standards (module, struct, field level comments, examples, doc tests with fully qualified paths).3.1. Service & Infrastructure Errors (
jsonrpc::service::error,jsonrpc::infrastructure::error)service::error::Errorandinfrastructure::error::Errorenums usingthiserror.DbError,StateError,RateLimitError) and implementFromfor wrapping withininfrastructure::error::Error.jsonrpc::error::Errorwraps these viaFromimplementations.3.2. Application State (
jsonrpc::infrastructure::state)3.2.1. Define
AppStateStruct (jsonrpc::infrastructure::state)AppStateholding shared config and adapters (Arc<dyn Trait>for adapters,Arc<RwLock<...>>forSubscriptionManager).3.2.2. Implement
AppStateFunctionalityAppState::newconstructor.Clone, ensuredSend + Sync.3.2.3. Implement Tests for Application State (
tests/jsonrpc/state.rs)Send+Sync, accessors, adapter delegation, andDebugimplementation using mock adapters and helpers.3.3. Metrics Collection (
jsonrpc::infrastructure::metrics)metricscrate (JSONRPC_REQUESTS_TOTAL,_DURATION_SECONDS,_ACTIVE_CONNECTIONS,_ACTIVE_SUBSCRIPTIONS).init_metrics_recorderusingmetrics-exporter-prometheusandonce_cellfor idempotent initialization.register_jsonrpc_metricsto describe and initialize metrics.3.4. Manual Rate Limiting (
jsonrpc::infrastructure::manual_limiter)3.4.1. Structs and Types
ManualRateLimitersstruct,BaseLimitertype alias, and nestedDashMaptype aliases for storing per-client/per-pattern limiters.3.4.2. Methods
ManualRateLimiters::newconstructor with config validation, pattern compilation, and initialization.check_method_limitfor enforcing limits based on method patterns, including lazy creation of limiters.rate_limit_to_quotahelper for config validation.3.4.3. Documentation
Arc<ManualRateLimiters>intoAppState.ManualMethodLimitExceededvariant toRateLimitError.3.4.4. Tests
check_method_limit(enforcement, pattern matching, reset, disabled state, per-client/pattern isolation).3.5. Database Adapter (
jsonrpc::infrastructure::db)DatabaseAdapterTrait:get_block_by_hashget_block_hash_by_heightget_block_header_by_hashget_block_label_by_heightget_spent_transaction_by_hashledger_tx_existsmempool_txmempool_tx_existsmempool_txs_sorted_by_feemempool_txs_countmempool_txs_ids_sorted_by_feemempool_txs_ids_sorted_by_low_feecandidatecandidate_by_iterationvalidation_resultmetadata_op_readmetadata_op_writeget_block_statusvalidate_nullifiersget_block_heightget_block_transactions_by_hashget_candidate_block_by_hashget_block_faults_by_hashget_latest_candidate_blockget_validation_resultget_latest_validation_resultget_transaction_statusget_mempool_transactionsget_mempool_transaction_by_hashget_mempool_infoget_chain_statsget_gas_pricesuggest_transaction_feeget_block_by_heightget_latest_blockget_blocks_rangeget_blocks_by_hashesget_latest_block_headerget_block_header_by_heightget_block_headers_rangeget_block_headers_by_hashesget_block_timestamp_by_hashget_block_timestamp_by_heightget_block_transactions_by_heightget_block_faults_by_heightget_latest_block_labelget_transaction_by_hashget_transactions_batchget_mempool_transactions_countget_latest_blocksget_blocks_countget_block_pairget_block_transaction_range_by_hashget_last_block_transactions_by_heightget_block_transaction_range_by_heightRuskDbAdapterImplementation: Defined struct holdingArc<RwLock<Backend>>.DatabaseAdapterTrait forRuskDbAdapter: Implement all required primitive methods usingspawn_blockingandnode::databasecomponents. Relies on default methods for higher-level API.3.6 Archive Adapter (
jsonrpc::infrastructure::archive)ArchiveAdapterTrait:get_moonlight_txs_by_memoget_last_archived_block(Returns height and hash)get_block_events_by_hashget_block_events_by_heightget_latest_block_eventsget_contract_finalized_eventsget_next_block_with_phoenix_transactionget_moonlight_transaction_history(HandlesAccountPublicKeyparsing from bs58 string)get_last_archived_block_height(Derived fromget_last_archived_block)get_contract_events(Alias forget_contract_finalized_events)get_contract_events_by_topic(Filtersget_contract_finalized_events)get_contract_events_by_block_height(Filtersget_block_events_by_height)get_contract_events_by_block_hash(Filtersget_block_events_by_hash)get_contract_transactions(Alias forget_contract_events)get_contract_transactions_by_block_height(Alias forget_contract_events_by_block_height)get_contract_transactions_by_block_hash(Alias forget_contract_events_by_block_hash)get_item_added_events(Filtersget_contract_events_by_topic)get_item_removed_events(Filtersget_contract_events_by_topic)get_item_modified_events(Filtersget_contract_events_by_topic)sync_contract(Method likely not needed on adapter, potentially AppState level)get_stake_events(Filtersget_contract_events_by_topic)get_unstake_events(Filtersget_contract_events_by_topic)get_slash_events(Filtersget_contract_events_by_topic)get_hard_slash_events(Filtersget_contract_events_by_topic)get_provisioner_changes(Filtersget_contract_events_by_topic)get_transfer_events(Filtersget_contract_events_by_topic)get_deposit_events(Filtersget_contract_events_by_topic)get_withdraw_events(Filtersget_contract_events_by_topic)get_convert_events(Filtersget_contract_events_by_topic)RuskArchiveAdapterImplementation: Defined struct holdingArc<node::archive::Archive>.ArchiveAdapterTrait forRuskArchiveAdapter:spawn_blocking(if needed) andnode::archive::Archivemethods.From/TryFrom).AccountPublicKeyparsing forget_moonlight_transaction_history.infrastructure::error::Error.3.7 Network Adapter (
jsonrpc::infrastructure::network)NetworkAdapterTraitbroadcast_transactionget_network_infoget_public_addressget_alive_peersget_alive_peers_countflood_requestget_network_peers_location(Note: Method not yet defined in trait)get_peers_metricsRuskNetworkAdapterImplementation: Defined struct holdingArc<RwLock<N: Network>>.NetworkAdapterTrait forRuskNetworkAdapter:broadcast_transactionget_network_infoget_public_addressget_alive_peersget_alive_peers_countflood_requestget_network_peers_location3.8 VM Adapter (
jsonrpc::infrastructure::vm)VmAdapterTrait:simulate_transactionpreverify_transactionget_chain_idget_account_dataget_state_rootget_block_gas_limitget_provisionersget_stake_info_by_pkquery_contract_rawget_vm_configget_account_balanceget_account_nonceget_stake_data_by_pkget_all_stake_dataget_provisioner_info_by_pkRuskVmAdapterImplementation: Defined struct holdingArc<RwLock<NodeRusk>>.VmAdapterTrait forRuskVmAdapter:simulate_transactionpreverify_transactionget_chain_idget_account_dataget_state_rootget_block_gas_limitget_provisionersget_stake_info_by_pkquery_contract_rawget_vm_config3.9 AppState Refactoring
AppState(e.g.,app_state.get_block_by_height(...)instead ofapp_state.db_adapter().get_block_by_height(...)) for a more user-friendly API.AppStatethat combine calls to multiple underlying adapters:deployContract(UsesVmAdapter,NetworkAdapter)getTransactionReceiptByHash(UsesDatabaseAdapter,ArchiveAdapter)getNetworkStatus(UsesAppState::get_alive_peers_count,AppState::get_block_height, andAppState::get_chain_id)getNodeInfo(UsesAppState::get_public_address,AppState::get_bootstrapping_nodes,AppState::get_vm_configandAppState::get_chain_id)estimateTransactionFee(UsesVmAdapter,DatabaseAdapter)getTransactionsByAddress(UsesDatabaseAdapter,ArchiveAdapter)4. HTTP Server Setup & Execution (
jsonrpc::server)4.1. HTTP Server Initialization
run_serverfunction usingaxumandaxum-server.RuskDbAdapter, etc.) andAppStateduring node startup (rusk::Builder::build_and_run), passingAppStatetorun_server.JsonRpcConfig.http.rustlsandrustls-pemfile(handled byload_tls_confighelper)./healthroute.4.2. Integrate
tower-governorLayer for HTTPaxumRouter using.layer().tower-governor: CreateGovernorLayerfromJsonRpcConfig.rate_limit.default_limitand add to router.JsonRpcConfig::build_cors_layer()helper and addCorsLayerto router.into_make_service_with_connect_info::<SocketAddr>. // Added check4.3. Implement Rate Limit Error Handling for Layer
tower_governor::GovernorErrorwas not added; the layer handles sending 429 responses directly.axum_server::Handleandtokio::signal::ctrl_c.AppStateand callrun_server.4.4. Define RPC Method Traits (Server-side)
RuskInfoRpcServertrait using#[rpc(server)].4.5. Implement RPC Method Dispatching for HTTP
RuskInfoRpcImplholdingArc<AppState>.RpcModuleusingmerge().rpc_handlerusingRpcModule::raw_json_request.axumrouter using.route("/rpc", post(rpc_handler)).with_state(rpc_module).run_serverandrpc_methods.5. Implement HTTP Services
Implement the actual logic for the HTTP-based RPC methods defined in Stage 1.4.4.
5.1. Implement Block Service (
jsonrpc::service::block)getBlockByHash(UsesAppState::get_block_by_hash, potentiallyAppState::get_block_transactions_by_hash)getBlockByHeight(UsesAppState::get_block_by_height, potentiallyAppState::get_block_transactions_by_hash)getLatestBlock(UsesAppState::get_latest_block, potentiallyAppState::get_block_transactions_by_hash)getBlockRange(UsesAppState::get_blocks_range, potentially transaction methods)getLatestBlocks(UsesAppState::get_latest_blocks, potentially transaction methods)getBlocksCount(UsesAppState::get_blocks_count)getBlockPair(UsesAppState::get_block_pair, potentially transaction methods)getBlockStatus(UsesAppState::get_block_label_by_heightor similar)getBlockEventsByHash(UsesAppState::get_block_events_by_hash)getBlockEventsByHeight(UsesAppState::get_block_events_by_height)getLatestBlockEvents(UsesAppState::get_latest_block_events)getBlockTransactionsByHash(UsesAppState::get_block_transactions_by_hash)getBlockTransactionRangeByHash(UsesAppState::get_block_transaction_range_by_hash)getLastBlockTransactionsByHash(UsesAppState::get_last_block_transactions_by_hash- adapter method TBD, currently block_txs only)getBlockTransactionsByHeight(UsesAppState::get_block_transactions_by_height)getBlockTransactionRangeByHeight(UsesAppState::get_block_transaction_range_by_height)getLastBlockTransactionsByHeight(UsesAppState::get_last_block_transactions_by_height)getNextBlockWithPhoenixTransaction(UsesAppState::get_next_block_with_phoenix_transaction)getGasPrice(UsesAppState::get_gas_price)5.2. Implement Transaction Service (
jsonrpc::service::transaction)getTransactionByHash(UsesAppState::get_transaction_by_hash)getTransactionStatus(UsesAppState::get_transaction_status)getTransactionsByAddress(UsesAppState::get_transactions_by_address- adapter/AppState method TBD)getTransactionsBatch(UsesAppState::get_transactions_batch)getTransactionReceiptByHash(UsesAppState::get_transaction_receipt_by_hash- adapter/AppState method TBD)findTransactionsByMemo(UsesAppState::get_moonlight_txs_by_memo)getMoonlightTransactionsByMemo(UsesAppState::get_moonlight_txs_by_memo)getMempoolTransactions(UsesAppState::get_mempool_transactions)getMempoolTransactionByHash(UsesAppState::get_mempool_transaction_by_hash)getMempoolInfo(UsesAppState::get_mempool_info)getMempoolTransactionsCount(UsesAppState::get_mempool_transactions_count)simulateTransaction(UsesAppState::simulate_transaction)sendTransaction(UsesAppState::broadcast_transaction, potentiallyAppState::preverify_transactionfirst)estimateTransactionFee(UsesAppState::estimate_transaction_fee- adapter/AppState method TBD)decodeRawTransaction(Pure deserialization, no adapter needed)suggestTransactionFee(UsesAppState::suggest_transaction_fee)5.3. Implement Contract Service (
jsonrpc::service::contract)callContract(UsesAppState::query_contract_raw- intended for read-only, might need dedicated write method on AppState/VMAdapter)queryContract(UsesAppState::query_contract_raw)deployContract(UsesAppState::deploy_contract- adapter/AppState method TBD)getAccountBalance(UsesAppState::get_account_balance)getAccountNonce(UsesAppState::get_account_nonce)validateNullifiers(UsesAppState::validate_nullifiers- adapter method TBD)getMoonlightTransactionHistory(UsesAppState::get_moonlight_transaction_history)getMoonlightTransactionsByMemo(UsesAppState::get_moonlight_txs_by_memo- already covered in Transaction Service)getTransferEvents(UsesAppState::get_transfer_events)getDepositEvents(UsesAppState::get_deposit_events)getWithdrawEvents(UsesAppState::get_withdraw_events)getConvertEvents(UsesAppState::get_convert_events)getProvisioners(UsesAppState::get_provisioners)getProvisionerInfo(UsesAppState::get_provisioner_info_by_pk)getStakeEvents(UsesAppState::get_stake_events)getUnstakeEvents(UsesAppState::get_unstake_events)getSlashEvents(UsesAppState::get_slash_events)getHardSlashEvents(UsesAppState::get_hard_slash_events)getProvisionerChanges(UsesAppState::get_provisioner_changes)getContractEvents(UsesAppState::get_contract_events)getContractEventsByTopic(UsesAppState::get_contract_events_by_topic)getContractEventsByBlockHeight(UsesAppState::get_contract_events_by_block_height)getContractEventsByBlockHash(UsesAppState::get_contract_events_by_block_hash)getContractFinalizedEvents(UsesAppState::get_contract_finalized_events)getContractTransactions(UsesAppState::get_contract_transactions)getContractTransactionsByBlockHeight(UsesAppState::get_contract_transactions_by_block_height)getContractTransactionsByBlockHash(UsesAppState::get_contract_transactions_by_block_hash)getItemAddedEvents(UsesAppState::get_item_added_events)getItemRemovedEvents(UsesAppState::get_item_removed_events)getItemModifiedEvents(UsesAppState::get_item_modified_events)syncContract(UsesAppState::sync_contract- adapter method TBD)getNextBlockWithPhoenixTransaction(UsesAppState::get_next_block_with_phoenix_transaction)5.4. Implement Network Service (
jsonrpc::service::network)getNodeInfo(UsesAppState::get_vm_config,AppState::get_chain_id,AppState::get_public_address- needs combined AppState method)getNodeVersion(Reads constants, no adapter needed)getChainId(UsesAppState::get_chain_id)getNetworkStatus(UsesAppState::get_alive_peers_count,AppState::get_block_height,AppState::get_chain_id- needs combined AppState method)getNetworkPeers(UsesAppState::get_alive_peers)getNetworkPeersLocation(UsesAppState::get_network_peers_location- adapter method TBD)5.5. Implement Prover Service (
jsonrpc::service::prover)prove(Uses external prover interaction - adapter TBD)verifyProof(Uses external prover interaction - adapter TBD)6. Testing Infrastructure & Stage 1 Tests (
tests/jsonrpc)6.1. Create Testing Infrastructure
tests/jsonrpc/utils.rs) including mock adapters (MockDbAdapter, etc.) andAppStatecreation helpers (create_test_app_state,create_custom_app_state).get_ephemeral_port).reqwestfor sending HTTP requests in integration tests.6.2. Implement Core Infrastructure Tests
/health), CORS,tower-governorrate limiting, and basic RPC call (rusk_getNodeInfo) via HTTP integration tests.6.3. Implement
RuskDbAdapterIntegration Teststests/jsonrpc/db_adapter.rs(feature-gatedchain).get_block_by_heightsuccess and NotFound cases.6.4. Implement HTTP Service Integration Tests
rusk_getNodeInfousingreqwest(test_rpc_method_via_server).7. Additional Tasks / Refinements for Stage 1
tracing::error!) inRuskDbAdaptererror handling paths.rusk/src/lib/jsonrpc/infrastructure/db.rsifDbError::InternalErrorwas replaced.Stage 2: WebSocket and Subscription Implementation
This stage focuses on adding WebSocket support, implementing the chosen subscription management strategy, defining and implementing subscription-related RPC methods, and adding WebSocket-specific testing.
1. Subscription Management Infrastructure (
jsonrpc::infrastructure::subscription)This section details the setup required for handling subscriptions, including types, errors, filters, and the core manager logic (presented with alternatives).
1.1. Core Subscription Types (
types.rs,error.rs)1.1.1. Define subscription error types
error.rswithSubscriptionErrorenum with variants:InvalidTopic- When topic is not supported/validInvalidSubscription- When subscription ID doesn't exist/is invalidInvalidSubscriptionIdFormat- When subscription ID is not a valid UUIDInvalidFilter- When filter configuration is invalidSessionNotFound- When session ID doesn't existPublishFailed- When event delivery failsChannelClosed- When event channel is closedTopicClosed- When a topic has been closedTooManySubscriptions- When subscription limits are exceeded1.1.2. Create
Topicenumtypes.rswith variants matching JSON-RPC methods from JSON‐RPC Websocket API subscription methods:BlockAcceptance- For subscribeBlockAcceptanceBlockFinalization- For subscribeBlockFinalizationChainReorganization- For subscribeChainReorganizationContractEvents- For subscribeContractEventsContractTransferEvents- For subscribeContractTransferEventsMempoolAcceptance- For subscribeMempoolAcceptanceMempoolEvents- For subscribeMempoolEventsTopicenum, serialize/deserialize, debug, Display, FromStr, andTopic::as_strmethod1.1.3. Create
SubscriptionIdtypetypes.rs.1.1.4. Create
SessionIdtypetypes.rs1.1.5. Create subscription parameters types
BlockSubscriptionParamswithinclude_txs: Option<bool>ContractSubscriptionParamswith fields:contract_id: Stringevent_names: Option<Vec<String>>include_metadata: Option<bool>min_amount: Option<String>(for transfer events)MempoolSubscriptionParamswith fields:contract_id: Option<String>include_details: Option<bool>ContractSubscriptionParamsand the regular builder pattern to the rest of the types1.1.6. Document
typesmodule1.2. Filters Implementation (
filters/)1.2.1. Design
FiltertraitFiltertrait infilter.rsmatches(&self, event: &dyn Any) -> boolmethodSend + Sync + 'static1.2.2. Implement
BlockFilterBlockFilterstruct inblock_filter.rsinclude_txs: boolFiltertrait for block events1.2.3. Implement
ContractFilterContractFilterstruct incontract_filter.rscontract_id: Stringevent_names: Option<Vec<String>>include_metadata: boolFiltertrait for contract events1.2.4. Implement
TransferFilterContractFilterfor transfer events intransfer_filter.rsmin_amount: Option<u64>1.2.5. Implement
MempoolFilterMempoolFilterstruct inmempool_filter.rscontract_id: Option<String>include_details: boolFiltertrait for mempool events1.2.6. Document
filtersmodule and all its submodulesfiltersmodule and all its submodules with examples and doc tests1.3. Subscription Manager Implementation (
manager.rs) - Alternative VariantsChoose one of the following variants to implement.
1.3.1. Variant A: Dual Server / Parse Legacy
RuesEventAssumption: The new JSON-RPC server will run simultaneously with the legacy HTTP server for a transition period. Event producers (
RuskVM,ChainEventStreamer) will continue to emit events in the existingRuesEventformat, and the legacy server requires this format. The new JSON-RPC server must adapt to this existing format.1.3.1.1. Relocate
RuesEventDefinition (Prerequisite)RuesEvent,RuesEventUri,DataType, and any other necessary dependent types from the legacyrusk::lib::http::eventmodule.jsonrpcmodule (e.g.,node-datacrate or a newrusk-events-commoncrate). Let's refer to the copied type asRelocatedRuesEvent.usestatements in all event producer modules (Rusk,ChainEventStreamer, etc.) to importRelocatedRuesEventfrom its new location. This ensures producers compile and function correctly before thehttpmodule is removed.RuskNodeBuilderuses the relocated type:broadcast::channel::<RelocatedRuesEvent>().1.3.1.2. Create
SubscriptionManagerskeleton (Variant A)SubscriptionManagerstruct inrusk::lib::jsonrpc::infrastructure::subscription::manager.rs.DashMaporRwLock<HashMap>depending on concurrency needs):topic_subscriptions: Map from internalTopicto a collection ofSubscriptionId->SubscriptionSink.session_subscriptions: Map fromSessionIdto a collection of associated(Topic, SubscriptionId)tuples.SubscriptionIdtoBox<dyn Filter>.SubscriptionIdtoSubscriptionStats.SubscriptionManager::newconstructor:broadcast::Receiver<RelocatedRuesEvent>(the receiver for the channel carrying the copied/relocated event type).1.3.1.3. Implement thread safety (Variant A)
topic_subscriptions,session_subscriptions,filters,subscription_stats) in appropriate concurrent primitives (e.g.,Arc<DashMap<...>>orArc<RwLock<HashMap<...>>>) to allow safe access from multiple threads (RPC handler threads adding/removing subscriptions, background task reading subscriptions).SubscriptionManageritself derivesCloneand isSend + Sync.1.3.1.4. Define Internal
SystemEventStructuresSystemEventenum within therusk::lib::jsonrpc::infrastructure::subscriptionmodule (or a submodule).BlockFinalized,ContractEventEmitted,MempoolTxAccepted). Each variant should hold the corresponding structured Rust data (e.g.,BlockHeader,ContractTxEvent,Transaction). Import these underlying data structures from appropriate crates (e.g.,node-data).try_parse_relocated_rues_event(event: RelocatedRuesEvent) -> Result<(Topic, SystemEvent), ParseError>function (or similar). This function is the core adaptation layer:event.uri(based on RUES specification:/on/[target]/[topic]) to determine the event type and map it to the internalTopicenum.event.data(likelyDataType::Binary) into the appropriate structured Rust type (e.g.,BlockHeader,ContractTxEvent) based on the URI.SystemEventvariant containing the deserialized structured data.ParseErrorenum to handle errors during URI parsing or data deserialization withintry_parse_relocated_rues_event.1.3.1.5. Implement background task for event processing (Variant A)
SubscriptionManager::newconstructor.RelocatedRuesEventmessages from the broadcast receiver passed during construction.broadcast::error::RecvError::Laggederrors gracefully (log a warning and continue).RelocatedRuesEvent:try_parse_relocated_rues_eventto convert the raw event intoResult<(Topic, SystemEvent), ParseError>. LogParseErrorand skip the event if conversion fails.Ok((topic, system_event)), acquire necessary locks and look up all(SubscriptionId, SubscriptionSink)pairs associated with thetopicin thetopic_subscriptionsregistry.subscription_id,sink):Filterin thefiltersregistry usingsubscription_id.matches(&system_event_data)method, passing a reference to the structured data contained within thesystem_eventvariant (e.g.,&block_header). Use&dyn Anyfor the filter to downcast.matchesmethod returnstrue.system_event, construct the JSON-RPC notification payload expected by the client for this subscription type. This involves serializing the structured data into the appropriate JSON format according to the API specification.jsonrpsee::SubscriptionMessagecontaining the formatted JSON payload and attempt to send it asynchronously usingSubscriptionSink::send().sink.send(). If an error occurs (e.g.,Disconnected), log the error, potentially increment failure counters inSubscriptionStats, and implement logic for potential subscription cleanup (see Task 1.6).SubscriptionStatsfor thesubscription_id(e.g.,events_processed,events_dropped_bufferon sink errors).tracinglogs for the task lifecycle, event reception, parsing success/failure, filtering decisions, sink errors, and shutdown.1.3.1.6. Document
managermodule (Variant A)SubscriptionManagerstruct, explaining its role, the dual-registry mechanism, and its interaction with the background task.SystemEventenum, thetry_parse_relocated_rues_eventfunction, and theParseErrortype.jsonrpseecontext.1.3.2. Variant B: Hard Cutover / Use
NodeBroadcastEventAssumption: The legacy HTTP server will be completely removed when the new JSON-RPC server is deployed. Event producers can be modified to emit a new, structured event type directly, eliminating the need for the legacy
RuesEventformat entirely.1.3.2.1. Define
NodeBroadcastEvent& Refactor Producers (Prerequisite)NodeBroadcastEventenum in a stable, shared location (e.g.,node-dataorrusk-events-common). This enum should have variants corresponding to logical event types, holding the relevant structured Rust data directly (e.g.,BlockHeader,ContractTxEvent). Ensure it derivesCloneandDebug.Rusk,ChainEventStreamer, etc.):RuesEvent(fromhttp::eventor the relocated copy).impl From<RawNodeStructure> for NodeBroadcastEventfor relevant raw types (e.g.,BlockHeader,ContractTxEvent). These implementations should directly wrap the data into the enum variant without any serialization. Remember to handle cloning if the raw structure is needed elsewhere.NodeBroadcastEventinstead ofRuesEvent.RuskNodeBuilderto use the new type:broadcast::channel::<NodeBroadcastEvent>(). Ensure the sender/receiver are passed correctly.1.3.2.2. Create
SubscriptionManagerskeleton (Variant B)SubscriptionManagerstruct inrusk::lib::jsonrpc::infrastructure::subscription::manager.rs.DashMaporRwLock<HashMap>):topic_subscriptions: Map from internalTopicto a collection ofSubscriptionId->SubscriptionSink.session_subscriptions: Map fromSessionIdto a collection of associated(Topic, SubscriptionId)tuples.SubscriptionIdtoBox<dyn Filter>.SubscriptionIdtoSubscriptionStats.SubscriptionManager::newconstructor:broadcast::Receiver<NodeBroadcastEvent>(the receiver for the channel carrying the new structured event type).1.3.2.3. Implement thread safety (Variant B)
topic_subscriptions,session_subscriptions,filters,subscription_stats) in appropriate concurrent primitives (e.g.,Arc<DashMap<...>>orArc<RwLock<HashMap<...>>>) for safe multi-threaded access.SubscriptionManageritself derivesCloneand isSend + Sync.1.3.2.4. Map
NodeBroadcastEventto InternalTopicget_topic_for_event(event: &NodeBroadcastEvent) -> Option<Topic>) within the subscription module to map the receivedNodeBroadcastEventvariant to the corresponding internalTopicenum used for routing subscriptions. This mapping should be straightforward based on the enum variant.1.3.2.5. Implement background task for event processing (Variant B)
SubscriptionManager::newconstructor.NodeBroadcastEventmessages from the broadcast receiver passed during construction.broadcast::error::RecvError::Laggederrors gracefully.NodeBroadcastEvent:get_topic_for_event(from Task 1.3.2.4) to get the internalTopic. If no topic maps (shouldn't happen with exhaustive matching), log an error and skip.(SubscriptionId, SubscriptionSink)pairs associated with theTopicin thetopic_subscriptionsregistry.subscription_id,sink):Filterin thefiltersregistry usingsubscription_id.matches(&node_broadcast_event_data)method, passing a reference to the structured data held directly within the receivedNodeBroadcastEventvariant (e.g.,&block_headerif the event isNodeBroadcastEvent::BlockFinalized(block_header)). Use&dyn Anyfor the filter to downcast.matchesmethod returnstrue.NodeBroadcastEvent, construct the JSON-RPC notification payload expected by the client for this subscription type. This involves serializing the structured data into the appropriate JSON format according to the API specification.jsonrpsee::SubscriptionMessagecontaining the formatted JSON payload and attempt to send it asynchronously usingSubscriptionSink::send().sink.send(). If an error occurs (e.g.,Disconnected), log the error, potentially increment failure counters inSubscriptionStats, and implement logic for potential subscription cleanup (see Task 1.6).SubscriptionStatsfor thesubscription_id.tracinglogs for the task lifecycle, event reception, topic mapping, filtering decisions, sink errors, and shutdown.1.3.2.6. Document
managermodule (Variant B)SubscriptionManagerstruct, explaining its role and the dual-registry mechanism.NodeBroadcastEventtype (likely linking to its definition in the shared crate).jsonrpseecontext.1.3.3. Comparison of Variants
RuesEvent)NodeBroadcastEvent)usestatements for relocatedRuesEvent.RuesEventlogic, addingNodeBroadcastEventlogic.jsonrpcServer LogicRelocatedRuesEventdefinition,SystemEventdefinition, andtry_parse...function (deserialization).NodeBroadcastEventdirectly, no parsing/deserialization needed for the event itself.jsonrpc. Producers still perform serialization for legacyRuesEvent.jsonrpc. Data flows as structs.try_parse...) injsonrpc. Requires later refactoring to removeRelocatedRuesEventfully.RelocatedRuesEventcopy. Parsing logic depends on legacy format knowledge.Recommendation:
jsonrpcmodule and defers the larger producer refactoring. Accept the technical debt of copying/parsingRuesEventtemporarily.Variant B is architecturally superior in the long run, but Variant A provides a more phased approach if required by project constraints or risk tolerance during the transition.
1.4. Subscription Lifecycle Methods
Implement
add_subscriptionmethodsession_id: SessionId, topic: Topic, sink: SubscriptionSink, filter: Option<Box<dyn Filter>>ManualRateLimitersfor Subscription Creation:RateLimitConfig.add_subscriptionmethod, retrieveClientInfo(likely needs to be passed in or obtained from context) and callAppState::manual_rate_limiters().check_method_limit(&client_info, "subscription:create").Err(SubscriptionError::RateLimitExceeded).Implement
remove_subscriptionmethodImplement
remove_session_subscriptionsmethodImplement synchronous
publishmethod (Optional)Note: Primarily for internal use or specific low-volume cases. Background task handles main event flow.
ManualRateLimitersfor Event Delivery (Sync):ClientInfoassociated with theSubscriptionSink.check_method_limit(&client_info, "<topic_pattern>").events_dropped_rate_limit.Implement asynchronous
publish_asyncmethod (Optional)Note: Only if direct async publishing outside the main event loop is needed.
1.5. Subscription Status and Management
Implement
SubscriptionStatsstructevents_processed: u64events_dropped_buffer: u64(Events dropped due to client buffer full/slow)events_dropped_rate_limit: u64(Events dropped due to manual rate limiter)last_event_time: Option<u64>creation_time: u64Implement
get_subscription_statusmethod (within Subscription Service)Note: Method implementation is in the service layer, but relies on stats from infrastructure.
SubscriptionStatsfor that ID from theSubscriptionManager.getSubscriptionStatusWebSocket method specification (includingactive,events_processed,events_dropped(sum of dropped counts),last_event_time, and potentially calculatingthrottledbased on recentevents_dropped_rate_limit).Integrate
ManualRateLimitersfor Event Delivery (Background Task)RateLimitConfigfor different subscription topics (e.g., "subscription:BlockAcceptance", "subscription:ContractEvents:*").ClientInfo: EnsureClientInfo(or relevant parts like IP) is stored alongside theSubscriptionSinkwhen a subscription is added (likely needs modification toadd_subscriptionand internal structures).SubscriptionSink::send():ClientInfofor the sink.AppState::manual_rate_limiters().check_method_limit(&client_info, "<topic_pattern>").check_method_limitreturnsErr(RateLimitError::ManualMethodLimitExceeded), do not send the event to that specific sink.events_dropped_rate_limitcounter inSubscriptionStatsfor the specific subscription when an event is dropped due to the rate limit check.1.6. Error Handling and Cleanup
1.6.1. Enhance background task with error handling
events_dropped_bufferinSubscriptionStats)Disconnectedsink error)events_dropped_buffer,events_dropped_rate_limit)1.6.2. Add subscription cleanup logic
remove_session_subscriptions.WebSocketServerConfig.idle_timeout) -jsonrpseelikely handles basic idle disconnects. Ensure manager cleans up associated subscriptions.1.7.
AppStateIntegrationRemove placeholder from
state.rsSubscriptionManagerstruct if one was used in Stage 1.SubscriptionManagerimplementation.Update
AppStateto use new implementationAppState'ssubscription_managerfield toArc<RwLock<ActualSubscriptionManager>>.AppState::newconstructor to initialize the actualSubscriptionManager(passing the event receiver).subscription_managerstill work.AppStatefields related to subscriptions.1.8. Documentation for Subscription Infrastructure
1.8.1. Add module-level documentation (
infrastructure/subscription/mod.rs)1.8.2. Document synchronous vs asynchronous publishing (if applicable)
1.8.3. Add thread-safety documentation
1.9. Testing for Subscription Infrastructure
These tests focus on the manager/infrastructure itself, before the RPC layer.
1.9.1. Create unit tests for SubscriptionManager (
infrastructure/subscription/manager.rstests)add_subscription,remove_subscription).remove_session_subscriptions).SubscriptionStats).2. WebSocket Server Setup & Execution (
jsonrpc::server)This section focuses on enabling and configuring the WebSocket part of the
jsonrpseeserver.2.1. WebSocket Server Initialization
jsonrpseeJsonRpcServersetup (from Stage 1.4.1) to configure both HTTP and WebSocket listeners usingServerBuilder::buildandServerBuilder::ws_tokio::bind(or similar combined setup).JsonRpcConfig.websocket(bind address, port, max connections, max message size etc.).2.2. Manual WebSocket Rate Limiting Integration
websocket_limitersmap withinManualRateLimiters::newbased onconfig.websocket_limit.ServerBuilder::set_rpc_connect_validator(or similar mechanism injsonrpsee) to hook into the WebSocket connection establishment phase.ClientInfo(IP address) from the incoming connection details.AppState::manual_rate_limiters().check_websocket_limit(&client_info).Err(RateLimitError::ManualWebSocketLimitExceeded), reject the connection (returnfalseor appropriate error from the validator).Ok(()), allow the connection (returntrue).RateLimitError::ManualWebSocketLimitExceededto a suitable log message or potentially an error response if the framework allows.2.3. Define RPC Method Traits (Subscription-side)
jsonrpc::service::subscription)SubscriptionRpcServer) using#[rpc(server, subscription)]attribute.subscribeBlockAcceptance,unsubscribe).&self, parameters,SubscriptionSink, and optionally context (subscription_context).&self,SubscriptionId, and optionally context.getSubscriptionStatususe#[method(name = "...")].2.4. Implement RPC Method Dispatching for WebSocket
SubscriptionRpcImpl) implementing the subscription RPC trait(s). This struct will hold theAppState.RpcModule::merge().RpcModule(containing both HTTP and WS methods) with thejsonrpsee::Serverduring startup.3. Implement Subscription Service (
jsonrpc::service::subscription)Implement the logic for the subscription-related RPC methods.
3.1. Implement Block Event Subscriptions
subscribeBlockAcceptance: CallAppState::subscription_manager().add_subscription(...)withTopic::BlockAcceptanceand appropriateBlockFilter.subscribeBlockFinalization: Calladd_subscriptionwithTopic::BlockFinalizationandBlockFilter.subscribeChainReorganization: Calladd_subscriptionwithTopic::ChainReorganization.3.2. Implement Contract Event Subscriptions
subscribeContractEvents: Calladd_subscriptionwithTopic::ContractEventsandContractFilter.subscribeContractTransferEvents: Calladd_subscriptionwithTopic::ContractTransferEventsandTransferFilter.3.3. Implement Mempool Event Subscriptions
subscribeMempoolAcceptance: Calladd_subscriptionwithTopic::MempoolAcceptanceandMempoolFilter.subscribeMempoolEvents: Calladd_subscriptionwithTopic::MempoolEventsandMempoolFilter.3.4. Implement Subscription Management Methods
unsubscribe: CallAppState::subscription_manager().remove_subscription(...)using the providedSubscriptionId.getSubscriptionStatus: Retrieve stats usingAppState::subscription_manager().get_subscription_status(...)(needs adding to manager) and format the response.4. Stage 2 Testing (
tests/jsonrpc)4.1. Implement Subscription Infrastructure Unit Tests
Covered in Task 1.9.
4.2. Implement Subscription Service Integration Tests
jsonrpsee::ws_client::WsClientfor testing.check_websocket_limit).subscribe*method.check_method_limitfor topic patterns).getSubscriptionStatusreturns correct stats after events.unsubscribestops notifications for that client/ID.getSubscriptionStatusor internal state check).check_method_limitfor "subscription:create").4.3. Test Background Task Behavior
RecvError::Lagged).Integration Testing Schedule
subscribe*,unsubscribe,getStatus), event delivery with filters, WebSocket rate limits, and subscription cleanup.