Indexed Database Designed Specifically for Account Abstraction UserOperations
BlockPI
Aug 27, 20235min read
Indexed Database Designed Specifically for Account Abstraction UserOperations
The market may not be very active, but the iteration of technology never stops. In the Ethereum ecosystem, the development of Account Abstraction (AA) is exciting. Whether it is the Ethereum Foundation, the Ethereum community, or Vitalik himself, they are all working tirelessly to advancing and promoting the adoption of AA. The reason is simple: the widespread adoption of account abstraction is a prerequisite for the widespread adoption of Web3. As Vitalik mentioned in the latest article, everyone moving to smart contract wallets is the one of three major technical transitions that the stack needs to undergo to make Ethereum a mature tech stack that is capable of actually bringing an open, global and permissionless experience to average users. Because “Without it, Ethereum fails because users are uncomfortable storing their funds (and non-financial assets), and everyone moves onto centralized exchanges.”
We have long recognized the significance of AA for Ethereum and have previously published an article on how to support account abstraction from the perspective of infrastructure. During our ongoing development and communication with AA developers, we have noticed that there are many areas that can be optimized.
One significant issue is indexing UserOperations. Since a bundle of UserOperations is considered as a single transaction on Ethereum blockchain, so an individual UserOperation is regarded as a part of a transaction (when the transaction interacting with the EntryPoint contract). Bundler provides the RPC method eth_getUserOperationByHash to AA users to query for the data of a UserOperation with its hash. To achieve that, the Bundler client sends eth_getLogs to scan the EntryPoint contract from a node client (Geth, Erigon, etc) to search for the hash of the UserOperation. Unlike a normal on-chain transaction, a UserOperation cannot be found using eth_getTransactionByHash which only gets the basic information of the bundle transaction containing multiple UserOperations. Since each UserOperation is an on-chain interaction between the smart wallet contract and the EntryPoint contract, the UserOperation data is stored in the logs of the EntryPoint contract. Let’s see how it is found from the payload of the eth_getLogs sent by the Bundler. The following is an example of an eth_getLogs request sent by the Bundler to the RPC node.
{ "method": "eth_getLogs", "params": [ { "address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", "topics": [ "0x49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f", "0xd9c5cdba03c5c74887fb5a2aa4d8eeac676841c6dc8573713e1798e924f1086c" ], "fromBlock": "earliest", "toBlock": "latest" } ], "id": 1, "jsonrpc": "2.0" }
where,
address
is the EntryPoint contract address, fixed value.
First line of the topics
is the log descriptor, fixed value.
The second line of the topics
is the hash of the UserOperation.
Then comes the challenging part. Since the hash of a UserOperation is the only parameter, the request must scan from the earliest block to the latest block (as demonstrated in the example above) to guarantee successful execution, which requires data of an archive blockchain node. This process generates a significant amount of read work on the node, which is inefficient and leading to slow returns for Bundler, ultimately resulting in slow responses to AA users. In addition, almost all RPC providers impose block request range restrictions on the eth_getLogs method. As a result, Bundlers cannot retrieve all block data in a single request and must segment it into multiple requests. This can even lead to slower processing speeds. Using customized RPC services from some RPC service providers can avoid such restriction, but this approach cannot be widely adopted because it is expensive and not affordable for everyone. The flowchart below shows the procedure.
Fortunately, there is only one EntryPoint contract at the same time, and due to the careful consideration given to each upgrade, it is basically not subject to frequent changes. Meanwhile, the value of the log descriptor is fixed. So to solve this problem, we should create an indexed database for UserOperation, either externally as an indexer or integrated into the node client. And the indexer only needs to extract data from nodes with a fixed address and a fixed log descriptor as parameters to establish a database, making the execution logic of the indexer backend very simple. There are several projects in the market that concentrate on data indexing, which, in theory, could be employed to address the issues. However, if using solutions like creating a Subgraph from The Graph directly, it is tantamount to relying on a third-party service, which is unacceptable. So an open-source client is an ideal solution.
Standalone Light Client
The benefit of indexing is there is no need to do repetitive work. Once the database is constructed by scanning from the earliest block to the latest block, only the logs in the newly added blocks need to be updated in future.
We have developed this open-source UserOperation Indexer and tested it on the BlockPI Network. Since only UserOperation indexing is performed, this client occupies relatively few system resources and is able to provide much higher QPS for UserOperation queries than original eth_getLogs. Below is a comparison of the results of a stress test querying the data of UserOperation on Polygon Mumbai (UserOperation hash: 0xcf8b2943927b6b905e5d3c870d19ff7cbfc8bce6c5fd3e59581cebe51f3400c1). The results show a significant difference between whether or not the data has been indexed.
The light client now supports three databases: Memory DB, Redis, and Pebble. After make
, the run command is only with parameters of the target blockchain, backend RPC endpoint and the called database. Taking Memory DB as an example:
./build/indexer \ --chain polygon-mumbai \ --backend https://polygon-mumbai.blockpi.network/v1/rpc/{APIKEY} \ --db.engin memory
It should be noted that the EntryPoint address is pre-configured internally. If the EntryPoint contract is upgraded, the address also needs to be updated. Now with this indexer, we can abstract this part of the eth_getLogs demand out and use a different method name to distinguish it from other raw node client APIs. We use eth_getLogsByUserOperation with only the hash of the UserOperation as input parameter. Since it is open-source, developers can modify the method name on their own. This method also implements the function of querying multiple UserOperations. You just need to list the hashes that need to be queried in the params. This was not achievable with eth_getlogs. Below is the request and response sample.
curl 'http://127.0.0.1:2052' \ -X POST -H "Content-Type: application/json" \ --data '{ "jsonrpc": "2.0", "method": "eth_getLogsByUserOperation", "params": [ "0xaa6f620266962dbed7778bff708be6891d92935ba1b6120781aca1aa37f9c560", "0xcf8b2943927b6b905e5d3c870d19ff7cbfc8bce6c5fd3e59581cebe51f3400c1" ], "id": 1 }' { "jsonrpc": "2.0", "id": 1, "result": [ {LOGS1}, {LOGS2} ] }
This indexer can be run individually, with the RPC node, or with the Bundler. When running with the RPC node, ensure that the backend is set to the local RPC endpoint. When running with Bundler, the communication between Bundler and indexer is more flexible, either modifying the code of the Bundler’s backend or altering the code of the Indexer to directly receive eth_getLogs from the Bundler.
Integrated into the Node client
Although developers can freely use the indexer client, making the Node client natively support indexing for UserOperation is the optimal solution, as it eliminates the need for external actors to run multiple clients or add databases. We want to start with integrating the UserOperation indexer functionality into Geth. This task is considerably more involved, necessitating a more in-depth study of the code and a thorough examination of the database of the Geth client, communicating with the team of the Geth client and then finding the best solution to implement the indexer for UserOperations. After modifying the client, extensive testing in various aspects is required. We are actively pushing forward with this matter.
GitHub: https://github.com/BlockPILabs/erc4337_user_operation_indexer
If you are an AA developer or user, we warmly invite you to join the BlockPI community to discuss any issues and suggestions related to AA. Let’s work together to contribute to the mass adoption of AA.