-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Implementation of Archive Node on TRON #6289
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
Comments
Hi @@halibobo1205 , it's a good news that TRON will implement an archive node to offer the historical information. And about the |
As I understand it, since java-tron uses protobuf as a prototype for database storage and API interaction. In the definition of the TRON protocol, the structure of accounts is more complex, so what kind of form will the archive node take when storing these historical account states? A simple example may make it easier to describe the problem, for example, the structure of account A has balance/stake information/vote information, etc. If in a certain block, account A initiates a transfer, then only one balance field in the information of account A has changed. In the archive node implementation, does the account A corresponding to the height of the block where the transaction occurred still store all the complete account information? Or is there some special storage structure that reduces the storage overhead? |
@lily309 still stores all the complete account information. The storage may become larger in this case, but the query operation will become easier. Still, at the same time, due to the database storage becoming larger, the query may also increase the time-consuming. |
stateRoot generation
![]()
![]() @waynercheung, I favor the second one, which I'll explain in more detail later. |
Understood. Of course, I'm also quite interested in erigon, who have implemented an archive node for Ethereum with only a tiny disk space. I may not know much about the exact technical details yet, but it's nice to see that the TRON development community is considering a more efficient storage structure to solve the huge disk footprint problem posed by archive nodes, so if you guys have a follow-up update, please let me know. |
Hi @halibobo1205 , where is the storage of contract data? For ETH, each account has a storage root, as "Storage trie is where all contract data lives. There is a separate storage trie for each account." mentioned by storage-trie. |
Hi @halibobo1205 , I have another question about this, to my knowledge, java-tron has an important concept called 'proposal'. Is this data included in the MPT tree? |
Ethereum will use the Verkle tree to replace Merkle-Patricia Trie. Will you consider adopting the Verkle tree directly to improve performance? |
@halibobo1205 The |
There are 25 database modifications included, so what is the selection criteria? receiptRoot of ETH is not generated, it's transactioninfo of TRON, why not used in |
Yes, contract data, Tron is currently associated with |
What does the "Storage Row Data" mean? Besides that, for Tron, there is only one MPT and just one stateRoot? |
Ethereum has implemented the path-based-storage-model to solve the problem of dynamic pruning of the world state tree for ordinary full nodes. In this change (or considering the future), ordinary full nodes also generate StateRoot. Should we consider using the path-based-storage-model? |
@halibobo1205 Can you show more details about the 25 database? what are they? |
How can I enable state if I want to run archive node from a fullnode which have synced for a while, for example, the current max block number is 100, and I enable state, does the block 100 generate the stateRoot? How does the stateRoot generate? If not, it means that the block 101 will have the stateRoot, how does the stateRoot generate? |
Hi @halibobo1205 , so you mean that maybe stateRoot doesn't participate in consensus, just implemented for historical queries, right? |
Hi @Federico2014 , is there any schedule about the progress? As I know, Geth is actively working on replacing the Merkle-Patricia Trie with the Verkle Tree. Testnet support is already in place, and the mainnet upgrade is anticipated to occur with the Osaka upgrade in late 2025 or 2026. I mean, using a Verkle tree might be a long-term solution, right? |
My view is that the implementation of archive node on TRON should only serve historical data query as well as based on which certain interfaces that were difficult to implement before, such as Based on this, we should focus on the overall performance and stability of the archive node. |
From this point of view, |
Hi @halibobo1205 , what is the key for MPT in Tron?
|
What is the key for MPT in Tron? public enum StateType {
UNDEFINED((byte) 0x00, "undefined"),
Account((byte) 0x01, "account"),
Code((byte) 0x02, "code"),
Contract((byte) 0x03, "contract"),
StorageRow((byte) 0x04, "storage-row"),
....
private final byte value;
@Getter
private final String name;
StateType(byte value, String name) {
this.value = value;
this.name = name;
}
public byte value() {
return this.value;
}
public static byte[] encodeKey(StateType type, byte[] key) {
byte[] p = new byte[]{type.value};
return Bytes.concat(p, key);
}
} |
Historical states store all accumulated changes. |
Hi @halibobo1205 @bladehan1 , both the We can conduct further research to determine if we can draw inspiration from them to enhance the implementation of MPT in Tron. If my understanding is incorrect, please let me know. |
Hi @halibobo1205 , about the For example, we enable the state feature from block 100, and there is an account_A which balance is 10 and account_B which balance is 5. So for block 100, 101 and 102, how does the stateRoot calculate? |
Good idea, I think we can do some more research about the |
Hi @halibobo1205 , you have mentioned that there maybe performance impact for archive node, do you have do some tests about it? |
Hi @halibobo1205 , what is the key for the trie node? For ETH, the traditional MPT is using the hash of trie node as key and the collapsed trie node as value. |
They're the same. |
performance impactThe Test Setup Performance Results
Scenario-Specific Observations
Key Findings and Implications Conversely, the significant drop in performance for random blocks, QPS is at 50/s, with a 0.73% error rate, further underscores the interface's sensitivity to query types rather than just load. All random block QPS tests at 50/s, with a 0.73% error rate, further underscore the interface's sensitivity to query types rather than just load. |
Hi @halibobo1205 , the low QPS might be barely acceptable. As hard drive capacities continue to grow, could this impact transaction and block processing? Has there been any related testing? |
@waynercheung |
Facing significant scaling and performance challenges: Current State
Key Technical Challenges
These challenges stem from the combination of high transaction throughput, complex account structures, and the limitations of the current state storage architecture. |
@halibobo1205 , so if we want to run archive node, we need to start a new node when the disk is larger than 500G? |
@waynercheung Based on the MPT implementation approach, creating a new node when the state data exceeds 500GB is a balanced compromise between block processing and API query performance. Further exploration and optimization of new solutions will be required moving forward. |
Erigon represents a fundamental rethinking of Ethereum's core infrastructure, not only addressing current technical challenges but also laying the foundation for future expansion. Through its innovative flat storage model and Change Sets architecture, Erigon significantly improves node performance and efficiency while reducing the hardware requirements for running a archive node. Erigon Storage Design: Store state in plain KV DB MDBX.
Erigon Disk-spacereth Database designerDiagram
CanonicalHeaders {
u64 BlockNumber "PK"
B256 HeaderHash "Value for CanonicalHeaders"
}
HeaderNumbers {
B256 BlockHash "PK"
u64 BlockNumber
}
Headers {
u64 BlockNumber "PK"
Header Data
}
BlockBodyIndices {
u64 BlockNumber "PK"
u64 first_tx_num
u64 tx_count
}
BlockOmmers {
u64 BlockNumber "PK"
Header[] Ommers
}
BlockWithdrawals {
u64 BlockNumber "PK"
Withdrawal[] Withdrawals
}
Transactions {
u64 TxNumber "PK"
TransactionSigned Data
}
TransactionHashNumbers {
B256 TxHash "PK"
u64 TxNumber
}
TransactionBlocks {
u64 MaxTxNumber "PK"
u64 BlockNumber
}
Receipts {
u64 TxNumber "PK"
Receipt Data
}
Bytecodes {
B256 CodeHash "PK"
Bytes Code
}
PlainAccountState {
Address Account "PK"
Account Data
}
PlainStorageState {
Address Account "PK"
B256 StorageKey "PK"
U256 StorageValue
}
AccountsHistoryIndex {
B256 Account "PK"
BlockNumberList BlockNumberList "List of transitions where account was changed"
}
StoragesHistoryIndex {
B256 Account "PK"
B256 StorageKey "PK"
BlockNumberList BlockNumberList "List of transitions where account storage entry was changed"
}
AccountChangeSets {
u64 BlockNumber "PK"
B256 Account "PK"
ChangeSet AccountChangeSets "Account before transition"
}
StorageChangeSets {
u64 BlockNumber "PK"
B256 Account "PK"
B256 StorageKey "PK"
ChangeSet StorageChangeSets "Storage entry before transition"
}
HashedAccounts {
B256 HashedAddress "PK"
Account Data
}
HashedStorages {
B256 HashedAddress "PK"
B256 HashedStorageKey "PK"
U256 StorageValue
}
AccountsTrie {
StoredNibbles Nibbles "PK"
BranchNodeCompact Node
}
StoragesTrie {
B256 HashedAddress "PK"
StoredNibblesSubKey NibblesSubKey "PK"
StorageTrieEntry Node
}
TransactionSenders {
u64 TxNumber "PK"
Address Sender
}
TransactionHashNumbers ||--|| Transactions : "hash -> tx id"
TransactionBlocks ||--|{ Transactions : "tx id -> block number"
BlockBodyIndices ||--o{ Transactions : "block number -> tx ids"
Headers ||--o{ AccountChangeSets : "each block has zero or more changesets"
Headers ||--o{ StorageChangeSets : "each block has zero or more changesets"
AccountsHistoryIndex }|--|{ AccountChangeSets : index
AccountsHistoryIndex }|--|{ StorageChangeSets : index
Headers ||--o| BlockOmmers : "each block has 0 or more ommers"
BlockBodyIndices ||--|| Headers : "index"
HeaderNumbers |o--|| Headers : "block hash -> block number"
CanonicalHeaders |o--|| Headers : "canonical chain block number -> block hash"
Transactions ||--|| Receipts : "each tx has a receipt"
PlainAccountState }o--o| Bytecodes : "an account can have a bytecode"
PlainAccountState ||--o{ PlainStorageState : "an account has 0 or more storage slots"
Transactions ||--|| TransactionSenders : "a tx has exactly 1 sender"
PlainAccountState ||--|| HashedAccounts : "hashed representation"
PlainStorageState ||--|| HashedStorages : "hashed representation"
|
TRON's database architecture is distributed across multiple specialized databases. When writing to these various databases, any data changes caused by a block are pre-written to a WAL (Write-Ahead Logging) database, commonly referred to as a checkpoint database. In this system, keys are composed of the database name plus the original key, while values consist of the operation type plus the original value. Current Tron State DatabaseWe have identified approximately 20 state databases that could leverage the ChangeSet approach. This classification may be subject to future changes.
ChangeSet Simulation Results (Original Data Size)
Storage Efficiency ProjectionThe current total transaction volume is 10,372,202,496(2025-5-15 from tronscan) Note: These are simulation results and may not represent the final production implementation outcomes. Further optimizations and adjustments are expected during actual implementation. |
Please, collect user cases before writing archive node. Not everybody needs history. And not everybody needs full history. I wrote a trading bot using alloy for Ethereum (and I plan to add TRON later), and in my experience I never needed history earlier than week ago. So, again: ask users what exactly they need, how much history they need, etc. Moreover, I can tolerate full lack of history. The only thing I truly need is basic (For users, who experience alloy/foundry/TRON problems: here is some workaround: alloy-rs/alloy#2371 . Also, as a quick workaround you can write your own JSON-RPC proxy, it is, in fact, very easy thing to do. I wrote my own JSON-RPC proxy [limited to features I need], which makes TRON more Ethereum-compatible. I can share code, if you want.) |
Some typical application scenarios of archive nodes
|
@u59149403 @bladehan1 The Archive node is currently under active discussion. Its primary goals are compatibility with Ethereum (ETH) interfaces and support for historical state queries. As mentioned, we agree it's important to gather more detailed user requirements, including (but not limited to) how long historical data should be retained and which specific interfaces need to be supported. On the topic of historical data storage, the Ethereum community is also exploring related optimizations (e.g., EIP-4444), which we are closely following and evaluating for potential alignment. Please feel free to share any specific use cases or expectations — that would be very helpful in shaping the final design. |
I implemented JSON-RPC proxy for making TRON more Ethereum-compatible. Currently very few features are supported. Check it here: https://github.com/u59149403/tjrp |
Thank you for being so interested. It's still in the planning stages. |
Background
Currently, Java-tron does not support Archive Node. An archive node can query historical state data from the blockchain. Unlike a fullnode, an archive node not only saves the most recent blockchain state data but also completely records all historical state data from the genesis block to the latest block. This allows users to directly query state data such as account balances and contract storage at any block height without needing to recalculate from the genesis block (replay transactions), greatly improving the efficiency of historical state data queries.
Rationale
Why should this feature exist?
Fullnodes only store the current latest block state (used to verify new blocks), while archive nodes additionally store state snapshots of each historical block (e.g., balances for each address, contract storage data, etc.). Archive nodes can support historical state queries. For example, querying the balance of an address at block height 10,000 cannot be directly provided by a fullnode, but an archive node can do this.
What are the use-cases?
Specification
Support Ethereum-compatible interfaces, including but not limited to:
Implementation
Archive nodes need a world state trie, which TRON needs to implement.
Ethereum state trie implementation
Ethereum supported a state trie in its initial version, implementing core state trie logic:
TRON's current situation
The block header contains 2 roots: txTrieRoot and accountStateRoot. TxTrieRoot is consistent with Ethereum's transactionRoot, but accountState only includes balance (the proposal is not enabled) and does not include contract data (storageRoot, codeHash) like ETH does.
Supports historical account balance queries: After enabling balance.history.lookup, account balances at specific heights can be queried via wallet/getaccountbalance.
State data is quite dispersed and not fully encapsulated in accounts (e.g., contract data, TRC10 are stored separately in different DBs). After organization, 25 databases need state statistics, including account data, TRC10 data, contract data, voting data, delegation data, etc. All state data needs to be organized and collected to generate the stateRoot
Implementation considerations for Archive nodes:
stateRoot generation
TRON's state data is quite dispersed, involving many state databases. Unlike ETH, which generates root directly based on accounts, TRON needs to collect dispersed state data to generate a stateRoot.
Should stateRoot participate in consensus?
Consensus issues:
Compatibility with all ETH state query interfaces
debug_traceCall
API #5778 )Performance impact
Archive nodes significantly impact block execution and transaction execution efficiency, especially when historical state data is large. The implementation must ensure the normal operation of block synchronization and transaction execution.
Storage impact
The transaction volume of TRON is far greater than that of ETH, with the archive node data expected to be 3-4 times that of ETH, estimated at 80 T+. Current single disk storage will be limited, requiring consideration of using multiple segmented Archive Nodes combined into a complete historical query collection.
Based on the above analysis, stateRoot will not participate in consensus to reduce the impact on SRs and the ordinary fullnodes during archive node implementation. Segmented multi-disk storage of state data will be used to solve single-disk storage limitations. Additionally, since archive-related functionality implementation involves significant changes affecting many functions, integration into the main branch is not currently considered, and development will first proceed on an independent branch.
The text was updated successfully, but these errors were encountered: