- The FallbackProvider in ethers.js wraps multiple JSON-RPC providers to enhance data reliability and reduce single-node dependency.
- It uses priorities, weights, and timeouts to determine consensus from multiple sources.
- Configurable quorums ensure you only trust data that a majority or required number of providers agree on.
- This method helps avoid inconsistent results from forks, latency issues, or out-of-sync nodes.
Main article
In the world of blockchain technology, where decentralization and transparency are paramount, ensuring the reliability and consistency of data is crucial. Sometimes, you might use different endpoints from different providers. However, they may be subject to network latency, temporary forks, or being out of sync with the rest of the network. Enter theFallbackProvider, a powerful tool provided by the ethers.js library. This utility is designed to enhance the reliability and accuracy of blockchain data by aggregating responses from multiple providers and forming a consensus. By leveraging redundancy and a consensus mechanism, the FallbackProvider mitigates the risks associated with relying on a single node, ensuring that the data you interact with is consistent and up-to-date.
In this tutorial, we will dive into the inner workings of the FallbackProvider, exploring its configuration options, consensus mechanisms, and error-handling capabilities.
The need for redundancy
Blockchain networks are designed to be decentralized and distributed, with multiple nodes contributing to the maintenance and validation of the ledger. Relying solely on a single node to retrieve blockchain data can be risky, as it introduces potential points of failure and inconsistencies. One of the primary risks of depending on a single node is the possibility of temporary forks or network partitions. In such scenarios, different nodes may have divergent views of the blockchain’s state, leading to inconsistent data being returned. Additionally, individual nodes may experience network latency, causing delays in propagating the latest blockchain data to other nodes. Nodes can occasionally become out of sync with the rest of the network, potentially providing outdated or incorrect information. To mitigate these risks and ensure the reliability and consistency of blockchain data, it is crucial to embrace redundancy by using multiple nodes. By querying multiple nodes and aggregating their responses, the chances of encountering inconsistent or inaccurate data are significantly reduced. Employing redundancy in blockchain data retrieval offers several benefits:- Increased reliability: With multiple nodes serving as data sources, the system becomes more resilient to individual node failures or temporary outages. If one node becomes unresponsive or returns erroneous data, the system can seamlessly fall back to other nodes, ensuring uninterrupted access to reliable blockchain data.
- Improved data accuracy: By aggregating responses from multiple nodes, inconsistencies or temporary forks can be detected and resolved through a consensus mechanism. This mechanism ensures that the data retrieved is consistent with most nodes, reducing the likelihood of interacting with outdated or incorrect information.
- Load balancing: Distributing queries across multiple nodes helps to balance the load and avoid overwhelming any single node with excessive requests. This load balancing can improve overall system performance and responsiveness.
- Fault tolerance: Redundancy introduces fault tolerance into the system, as the failure or misconfiguration of a single node does not necessarily lead to complete system failure. The system can gracefully degrade and continue operating by leveraging the remaining functional nodes.
Learn how to build a simple load balancer in JavaScript by reading Make your DApp more reliable with Chainstack.
The ethers FallbackProvider
The ethers.js library, a popular JavaScript library for interacting with Ethereum-based blockchains, provides the FallbackProvider tool. This utility is designed to enhance the reliability and consistency of blockchain data retrieval by leveraging redundancy and a consensus mechanism across multiple providers.
At its core, the FallbackProvider acts as a wrapper around a set of individual providers, such as Ethereum JSON-RPC providers. When querying for blockchain data, the FallbackProvider sends requests to multiple providers simultaneously and aggregates their responses. It then applies a configurable consensus mechanism to determine the most reliable and consistent result.
The FallbackProvider operates by distributing requests across multiple providers, each with its own priority, weight, and stall timeout settings. These settings allow the FallbackProvider to prioritize and weight the responses from different providers based on their expected reliability and responsiveness.
When a request is made to the FallbackProvider, it sends the request to all configured providers concurrently. As responses start arriving, the FallbackProvider evaluates them against a pre-defined quorum value, which specifies the minimum number of providers that must agree on the same result for it to be considered a consensus.
If the quorum is met, meaning that the required number of providers return the same result, the FallbackProvider considers this the consensus result and returns it to the caller. However, if the quorum is unmet, the FallbackProvider employs a fallback mechanism to handle potential inconsistencies or failures.
The fallback mechanism prioritizes providers based on their assigned weights and stall timeouts. If a provider fails to respond within its configured stall timeout, the Fallback provider disregards its response and moves to the next highest-priority provider. This process continues until the quorum is met or all providers have been exhausted.
By aggregating responses from multiple providers and applying a consensus mechanism, the FallbackProvider helps mitigate the risks of relying on a single node for blockchain data. It ensures that the data returned is consistent with most providers, reducing the likelihood of interacting with outdated, incorrect, or divergent information.
Deploy a Chainstack node
Before diving into the implementation, ensure you have a Chainstack account. Deploying a node on Chainstack is essential for accessing and interacting with blockchain networks. Deploy 3 or mix Chainstack and public nodes for a good demonstration. Choose different geographical regions for each to maximize network uptime and reduce latency, which is crucial for a robust and efficient blockchain application.Project setup
To set up the JavaScript project and integrate theFallbackProvider from the ethers.js library, we’ll need to follow these steps:
First, create a new directory for your project and initialize a new Node.js project by running npm init in your terminal. This will create a package.json file, which will manage your project’s dependencies.
Check out Web3 node.js: From zero to a full-fledged project to learn how to manage Node projects.
ethers.js library, which provides the FallbackProvider functionality, and the dotenv package, which allows us to load environment variables from a .env file.
After the installation is complete, create a new file named .env in the root directory of your project. This file will store the URLs of the JSON-RPC providers you want to use with the FallbackProvider. Add the following lines to the .env file, replacing the placeholders with the actual provider URLs:
The full code
Now that the project is setup create a new file namedindex.js and paste the following code:
Code breakdown
The code is designed to interact with blockchain networks through Ethereum’s JSON RPC API using multiple providers for enhanced reliability and performance. It uses theethers.js library, a popular choice for interacting with the Ethereum blockchain and its ecosystems. Let’s break down how this code works, focusing on its key components and functionalities:
Setup and configuration
ethers object is imported from the ethers.js library, which provides the functionality for interacting with Ethereum-based blockchains, including the FallbackProvider. The dotenv package is loaded, which allows us to load environment variables from the .env file.
RPC URLs
.env file. These URLs will be used to create instances of the JsonRpcProvider.
Configuration constants
stallTimeout and quorum. stallTimeout is set to 2000 milliseconds (2 seconds), which determines the maximum time the FallbackProvider will wait for a response from a provider before considering it unresponsive. quorum is set to 2, specifying that at least two providers must return the same result to be considered a consensus.
JSON RPC providers
ethers.JsonRpcProvider using the URLs retrieved from the environment variables. These providers will be used as the underlying data sources for the FallbackProvider.
Fallback provider
ethers.FallbackProvider by passing an array of provider configurations and the desired quorum value. Each provider configuration includes the following properties:
provider: The instance of theJsonRpcProviderto be used.Priority: A numeric value representing the provider’s priority. Higher values indicate higher priority.weight: A numeric value representing the weight or reliability of the provider. Higher values indicate higher reliability.stallTimeout: The maximum time (in milliseconds) to wait for a response from the provider before considering it unresponsive.
provider1 is given the highest priority (2) and weight (3), assuming it is the most reliable provider. provider2 and provider3 have lower priorities (1) and weights (2 and 1, respectively), with adjusted stallTimeout values based on their expected responsiveness.
getBlockNumber function
getBlockNumber function is an asynchronous function that fetches the latest block number from the fallbackProvider.
Inside the try block, it calls fallbackProvider.getBlockNumber() and awaits the result. The console logs the latest block number if the block number is fetched successfully. If an error occurs, it catches the error and logs the error message. It also logs a message indicating that it’s attempting to restart the program and includes a comment suggesting that a retry mechanism or other logic could be implemented here. This example uses setTimeout to call getBlockNumber again after a 3-second delay.
Periodic execution
setInterval to call the getBlockNumber function every 3 seconds, continuously fetching and logging the latest block number.
By combining the FallbackProvider with multiple JSON-RPC providers and configuring their priorities, weights, and stall timeouts, this code demonstrates how to enhance the reliability and consistency of blockchain data retrieval. The FallbackProvider will aggregate responses from the configured providers, apply the consensus mechanism based on the specified quorum, and handle failures or timeouts by falling back to other providers.
Error handling and retries
The error handling withingetBlockNumber uses a try-catch block to catch any exceptions. Suppose an error occurs within the FallbackProvider, meaning there is a disagreement in the consensus or the providers with higher priority and weights fail. In that case, it logs the message and attempts to restart the function after a 3-second delay, demonstrating a simple retry mechanism.
Understanding the FallbackProvider configuration
The FallbackProvider instance is created by passing an array of provider configurations and the desired quorum value to the ethers.FallbackProvider constructor. This array allows you to specify multiple providers and configure their behavior within the FallbackProvider.
Each provider configuration in the array is an object with the following properties:
provider: This is an instance of theJsonRpcProvideryou want to include in theFallbackProvider. In this example,provider1,provider2, andprovider3are instances created earlier using the provider URLs from the environment variables.priority: This numeric value represents the provider’s priority. Higher values indicate a higher priority. When theFallbackProviderneeds to select a provider for a request, it will prioritize providers with higher priority values. In the example,provider1has the highest priority of 2, whileprovider2andprovider3have a lower priority of 1.weight: This numeric value represents the provider’s weight or reliability. Higher values indicate a higher level of reliability. TheFallbackProvideruses these weights when determining the consensus result. In the example,provider1has the highest weight of 3, indicating that it is considered the most reliable provider, whileprovider2weights 2, andprovider3weights 1.stallTimeout: This value specifies the maximum time (in milliseconds) that theFallbackProviderwill wait for a response from the provider before considering it unresponsive or “stalled.” If the provider doesn’t respond within this time, theFallbackProviderwill disregard its response and move on to the next provider. In the example,provider1uses the defaultstallTimeoutvalue of 2000 milliseconds (2 seconds),provider2has a shorterstallTimeoutof 1500 milliseconds (1.5 seconds), andprovider3has a longerstallTimeoutof 2500 milliseconds (2.5 seconds).
FallbackProvider based on your specific requirements and your providers’ expected reliability and responsiveness.
The quorum parameter passed to the FallbackProvider constructor specifies the minimum number of providers agreeing on the same result to be considered a consensus. In this example, the quorum is set to 2, meaning that at least two providers must return the same result for the FallbackProvider to consider it a valid consensus.
Users can customize the configuration of the FallbackProvider by adjusting the properties of the provider objects in the array and the quorum value. For instance, if you have a provider that is known to be highly reliable, you can assign it a higher priority and weight. If you expect a provider to respond slower, you can increase its stallTimeout value accordingly. Additionally, you can adjust the quorum value based on the level of consensus you require for your application.
Conclusion
In this tutorial, we explored theFallbackProvider from the ethers.js library, a powerful tool designed to enhance the reliability and consistency of blockchain data retrieval. We learned the importance of redundancy when interacting with blockchain networks and how relying solely on a single node can introduce risks of inconsistent or inaccurate data due to factors like network latency, temporary forks, or out-of-synchrony nodes.
The FallbackProvider addresses these challenges by leveraging multiple JSON-RPC providers and employing a consensus mechanism. By aggregating responses from multiple providers, prioritizing them based on their expected reliability, and applying a configurable quorum, the FallbackProvider ensures that the data retrieved is consistent with most providers, mitigating the risks associated with relying on a single source.
We walked through the setup process, including installing dependencies, configuring environment variables, and creating instances of the JsonRpcProvider and FallbackProvider. We also explored the code implementation, breaking down each component and explaining the configuration options such as provider priority, weight, and stall timeout.
By embracing redundancy and consensus mechanisms like the FallbackProviderdevelopers can build more robust and fault-tolerant applications that interact with blockchain networks, ensuring reliable and accurate data retrieval, even in the face of potential inconsistencies or failures.