Skip to content

Parity ethereum code walkthrough 1

ledgerwatch edited this page Jan 9, 2020 · 2 revisions

Parity-ethereum code walkthrough

parity-ethereum/parity/main.rs Function main either starts another executable (it is called 'hypervised mode' and is called via run_parity function), or directly executes parity in the current process via main_direct function. Function main_direct gathers configuration from command line arguments (conf), sets up logging (logger) and eventually invokes start function.

parity-ethereum/parity/lib.rs Function start is a wrapper around execute function with an extra functionality that it prints out deprecated command line arguments. Function execute can optionally start deadlock detection thread, and then executes one of the specified commands:

  1. Run
  2. Version
  3. Hash
  4. Account
  5. Import Presale Wallet
  6. Blockchain
  7. Signer Token
  8. Signer Sign
  9. Signer List
  10. Signer Reject
  11. Snapshot
  12. Export Hardcoded Sync

Presumably, we are interested in the Run command.

parity-ethereum/parity/run.rs

Function execute executes either light client or full client depending on the option cmd.light (where cmd is the instance of RunCmd). We are currently interested in the full client, so the function invoked in this case is execute_impl. Function execute_impl first assembles all configuration parameters and then create client service by invoking ClientService::start.

parity-ethereum/ethcore/service/src/service.rs

Function start in ClientService implementation creates a new client via Client::new, passing the handle to blockchain_db

parity-ethereum/ethcore/src/client/client.rs

Function new of type Client creates a trie factory. It also create VM factory, presumable for creating instances of EVM to execute transactions. And accountdb is also wrapped into Factories together with the trie factory and VM factory.

The database (db) passed into this constructor new, is treated as a key-value store. First, it is wrapped into a journal_db, which has settings related to pruning and selects the column ::db::COL_STATE (presumably the column used to store the Ethereum state). Journal db takes care of the history of the state. Next, the journal_db is further wrapped into StateDB, using StateDB::new, which has a setting related to the state cache size.

The same database handle, db, is cloned two times and is used to create a blockchain db instance via BlockChain::new, and trace db instance, via TraceDB::new.

As a part of creating the Client, another object is created, of the type Importer (defined in the same file). This type is the entry point for block importing operations. Interestingly, the block importer does not hold any direct or indirect references to the database, therefore all the functions that require access to the database, always have the client passed as a parameter. Importer then uses one of the state_db, chain, or trace_db properties of the client to get access to the corresponding database wrapper.

parity-ethereum/ethcore/state-db/src/lib.rs

Here we have the definition of StateDB

parity-ethereum/ethcore/evm

EVM implementation

parity-ethereum/ethcore/trie-vm-factories/src/lib.rs This file defines Factories, which is tuple of 3 factories:

  1. Trie factory
  2. VM factory
  3. Account factory

trie/trie-db/src/lib.rs

This is where TrieFactory is defined. Note that this is a separate repo, and not part of Parity-Ethereum. A factory can create 3 types of tries: Generic, Secure, and Fat. Parameter Layout defines the parameterisation of more generic trie implementation to specifics of Ethereum. It is in the file parity-ethereum/util/patricia-trie-ethereum/src/lib.rs

Depending on the configuration options, either Secure or Fat variant is instantiated. Both secure and fat dbs are wrappers around TrieDb, which is the database representation of particia trie with a cache. Secure db applies hash function to all the keys before inserting or removing. Fat db additionally maintains the set of preimages.

trie/trie-db/src/triedbmut.rs

This is where most of the trie modification and lookup logic resides. Interestingly, nodes of the trie reference each other not via pointers (like in go-ethereum, for example), but via "handles", which are indices in one big vector of nodes, encapsulate into NodeStorage. Node storage also maintains a "free list", which essentially turns it into a heap allocator. Looks like there is no limitation on how many trie nodes are kept in memory, which leads me to believe that tries are created only for the processing of one block. That explains the "factory" approach.

Clone this wiki locally