Architecture
ethereumjs-lib implements almost everything covered in the Yellow Paper. Above are the core modules. Tries, RLP and secp256k1 were implemented for Ethereum but are independent modules. Blooms are also a candidate for separation. The general pattern to follow is to implement everything together then pull out the modules that could be used independently as needed.
Block
, Blockheader
, Transaction
, Account
are Schema Modules. In general they provide:
- Basic Validation, doesn't Validate Against the state
- Serialization
- easy to access different properties of each object
Blockchain
(dead link) is kinda like an ORM,
- provides lookup operation for blocks.
- keeps track of meta information
Merkle-Patricia-Trie Checkpointing
For an overview on how to use merkle tries see this post. For Ethereum we need a checkpoint mechanism for the state. If you are familiar with git you can think of checkpoint as commits. After you make a checkpoint then make some more changes to the trie you should be able to revert back to the checkpoint you made. Ideally we should be able to do the something like the following.
var trie = new Trie();
trie.checkpoint();
//save some stuff
trie.checkpoint();
//save more stuff
….
trie.revert();
trie.revert(); //at the same state we started
This mechanism is used in Ethereum when processing contracts.
Above is the sequence diagram for a contract. Each time the contract makes a call to another contract it needs to create a checkpoint. If the contract errs the state trie needs to be reverted to the point it was at before the contract was running.
The Trie accomplishes this by creating a cache and only storing to that cache after a checkpoint is made. When more checkpoints are created the current root of the trie is push to a checkpoint array.
On each revert the previous root is popped from the checkpoint array and is set to the current root. After each commit the penultimate element of the checkpoint array is removed. Here is pseudocode:
trie._checkpoints = []
trie._cache = new Trie();
//to create a checkpoint
trie._checkpoints.push(trie._cache.root)
//to revert a checkpoint
trie._cache.root = trie._checkpoints.pop()
//to commit a checkpoint
var root = trie._checkpoints.pop()
trie._checkpoints[trie._checkpoints.length - 1] = root;
We try to encapsulate everything that manipulates the state trie in the VM functions.
- Runs each Tx in the Block
- Rewards the coinbase
- Generates/Checks Blooms
- Generates/Checks ReceiptTries
- Checks the sending account balance
- increments the nonce
- Subtracts the TxFee
- exposes an onTx hook
- Loads the receiving account
- runs runCode or runPreComiled
- creates new accounts
- transfers the
value
- handles suicides
- handle OOG
- Runs the contract code
- make calls back to runCal
- exposes an onStep hook
Ethereum uses the DEVp2p protocol for it's network. The protocols supports any number of subprotocols. The networking modules handles this by loading subprotocol definitions. The ability to load definitions at run time allow third party protocols to reuse the p2p network.
node-ethereum Overview
Since ethereumjs-lib implements everything that is in the Yellow Paper, node-ethereum does everything else. It takes care of setting up and initiating all the underlying modules.
node-ethereum services
Node-ethereum provides several services
- networking
- JSON RPC
- Block Processing
- key Management
It also starts two child processes.
- Web front-end server. Which simply servers a single control webpage
- DB server.
These processes can optionally be started independently, allowing for flexible configuration for the architecture. For example you can start one instance of the db server and have several instances of node-ethereum connect to it.