-
Notifications
You must be signed in to change notification settings - Fork 37
Home
The purpose of this wiki is to guide those who are contributors/looking to understand the codebase through our architecture and folder structure. The general structure of the project is listed below, but there are pages in this wiki for each of the major components as well.
Let's cover the folders and locations of important bits of code, along with a small description of what they do. A basic outline of the folder structure is as follows:
├── ultradark/
│ ├── apps/
│ │ ├── miner/
│ │ ├── wallet/
│ ├── config/
│ ├── core/
│ ├── .utxo/
│ └── .chaindata/
The core/
directory contains all of the logic for the blockchain. This stores the modules which define the way Blocks, Transactions, Ledgers, and other important parts of the UltraDark blockchain work. Many other parts of the codebase use the modules within this directory.
The miner/
directory contains all the files needed in order to run a miner on your machine. This folder contains defines a mix task within it, which is where the command mix miner
comes from. This application inherits most of its functionality from the modules in core/
.
The wallet/
directory contains the wallet functionality, which allows for the creation of transactions (among other things).
The .chaindata/
directory is a hidden directory within the project root. This directory is the storage directory of LevelDB, which is the key/value store we use to manage the blockchain. Every block in the blockchain is stored in this directory, in binary format. The same goes for the .utxo
hidden directory, except it's purpose is to store transaction outputs that haven't been spent yet (which speeds up validation considerably, versus if the blockchain was searched for the UTXO for the verification of each transaction)
Let's look at a high-level, step-by-step description of how the blockchain works. Assuming you run mix miner
, what happens is:
- A pre-defined mix task in
apps/miner/lib/mix/tasks/miner.ex
calls theMiner.initialize
function, which is defined inapps/miner/lib/miner.ex
. - This function initialized LevelDB (by calling
Ledger.initialize
), which will create a .chaindata directory in the root of the project. This then callsBlockchain.initialize
, which either generates a genesis block, or retrieves the existing chain from LevelDB. - Then, the
main
function is called. This function takes the first block in the chain (our chain is reverse sorted, meaning the latest blocks will be in the front of the list) and uses that to create a new block. This new block will reference the previous blocks hash as its prev_hash, and it will have an index that is 1 higher than the previous block. - Once the new block is generated, The coinbase is generated for this block. A block must have a coinbase to be considered valid.
- This coinbase is then added to the list of transactions within the block. The coinbase must be the first transaction in the block for the block to be considered valid. The coinbase is the payment to the miner, so the amount of the coinbase output will be the sum of all the transaction fees within the block plus the block reward.
- This block is then taken and passed to the
Block.mine
function, which constructs a block header based on a few properties of the block, and returns a base16 encoded sha256 digest of this block header. This is the block's hash. - The
Block.mine
function will continue to loop until the hash of the block is lower than the target. The target is calculated based on the difficulty of the block. Since each sha256 hash represents a 256 bit number, the numerical value represented by the hash is compared to the target. If it is lower, the valid hash has been found! - Once the hash has been found, the block goes through a series of validations, and if the block is deemed valid, it is added to the chain. This is then passed back into the
main
function, and the same process happens in order to create the next block.