This repository is intended to help myself understand how blockchain works by writing it from scratch. The basis for running the following scenario from scratch was taken from Jeiwan/blockchain_go. From there, I will further deepen each technique one by one.
In blockchain it’s blocks that store valuable information. For example, transactions, version, current timestamp and the hash of the previous block.
Blockchain is just a database with certain structure: it’s an ordered, back-linked list.
“Do hard work and prove” mechanism is called proof-of-work. For incorporate a block into the blockchain in a secure way. To maintain the stability of the whole blockchain database.
Hashing is used to guarantee the consistency of a block. Contains the hash of the previous block, thus making it impossible (or, at least, quite difficult) to modify a block in the chain.
Bitcoin uses Hashcash. In the original Hashcash implementation, the requirement sounds like “first 20 bits of a hash must be zeros”.
Blockchain is a distributed database. Which database do we need? Actually, any of them.
Since blockchain is a public and open database, we don’t want to store sensitive information about wallet owners. There are, no accounts, no balances, no addresses, no coins, no senders and receivers.
A transaction is a combination of inputs and outputs.
It’s outputs that store “coins”. And storing means locking them with a puzzle, which is stored in the ScriptPubKey. One important thing about outputs is that they are indivisible, which means that you cannot reference a part of its value.
An input references a previous output. ScriptSig is a script which provides data to be used in an output’s ScriptPubKey. If the data is correct, the output can be unlocked, and its value can be used to generate new outputs. This is the mechanism that guarantees that users cannot spend coins belonging to other people.
When we check balance, we need to find all unspent transaction outputs (UTXO). Unspent means that these outputs weren’t referenced in any inputs. We don’t need all of them, but only those that can be unlocked by the key we own.
If we want to send some coins to someone else, we need to create a new transaction, put it in a block, and mine the block. Before creating new outputs, we first have to find all unspent outputs and ensure that they store enough value.
This is where transactions are stored before being packed in a block.
Public-key cryptography algorithms use pairs of keys: public keys and private keys. Public keys are not sensitive and can be disclosed to anyone. In contrast, private keys shouldn’t be disclosed.
By applying a signing algorithm to data (i.e., signing the data), one gets a signature, which can later be verified. Digital signing happens with the usage of a private key, and verification requires a public key.
Public and private keys are sequences of random bytes. Since it’s private keys that are used to identify owners of coins, there’s a required condition: the randomness algorithm must produce truly random bytes. Elliptic curves is a complex mathematical concept.
Bitcoin uses the Base58 algorithm to convert public keys into human readable format.
The reward is just a coinbase transaction. When a mining node starts mining a new block, it takes transactions from the queue and prepends a coinbase transaction to them. The coinbase transaction’s only output contains miner’s public key hash.
As of September 18, 2017, Bitcoin has 485,860 blocks and the entire data size over 140 Gb. This means that validating transactions would require iterating over many blocks.
The solution to this problem is to have an index that stores only unused outputs, which is the role of the UTXO set. This is a cache and is later used to calculate balances and validate new transactions The UTXO set is approximately 2.7 Gb as of September 2017.
For SPV to be possible, there should be a way to check if a block contains certain transaction without downloading the whole block. And this is where Merkle tree comes into play.
Merkle trees are used to obtain transactions hash, which is then saved in block headers and is considered by the proof-of-work system.
While previous features are crucial, it’s not enough. What makes these features really shine, and what make cryptocurrencies possible, is network. Blockchain network is a community of programs that follow the same rules, and it’s this following the rules that makes the network alive.
Blockchain network is a P2P (Peer-to-Peer) network, which is decentralized.
Their only goal is to mine new blocks as fast as possible. Miners are only possible in blockchains that use Proof-of-Work. In Proof-of-Stake blockchains, there’s no mining.
These nodes validate blocks mined by miners and verify transactions. They must have the whole copy of blockchain. Also, such nodes perform such routing operations, like helping other nodes to discover each other.
SPV makes wallet applications possible. It's a light node that doesn’t download the whole blockchain and doesn’t verify blocks and transactions. Instead, it finds transactions in blocks (to verify payments) and is linked to a full node to retrieve just necessary data.
Nodes communicate by the means of messages. When a new node is run, it gets several nodes from a DNS seed, and sends them version message.
getblocks means “show me what blocks you have”. It requests a list of block hashes.
Bitcoin uses inv to show other nodes what blocks or transactions current node has. It doesn’t contain whole blocks and transactions, just their hashes.
getdata is a request for certain block or transaction, and it can contain only one block/transaction ID.
It’s these messages that actually transfer the data. When we received a new block, we put it into our blockchain. If there’re more blocks to download, we request them from the same node we downloaded the previous block. When we finally downloaded all the blocks, the UTXO set is reindexed.
- First, set NODE_ID to 3000 (export NODE_ID=3000) in the first terminal window. I’ll use badges like NODE 3000, NODE 3001 or NODE 3002 before next paragraphs, for you to know what node to perform actions on.
- Open a new window as
central node
.- Set NODE_ID.
$ export NODE_ID=3000
- Create new wallet.
$ go_blockchain createwallet
- Use the created address.
$ go_blockchain createblockchain -address CENTREAL_NODE
- We need to save the block and use it in other nodes.
$ cp blockchain_3000.db blockchain_genesis.db
- Open a new window as
wallet node
.- Set NODE_ID.
$ export NODE_ID=3001
- Create new wallet as WALLET_1, WALLET_2, WALLET_3, WALLET_4.
Repeat the command four time to generate each wallet.
$ go_blockchain createwallet
- In the
central node
window.- Send some coins to the wallet addresses.
-mine
flag means that the block will be immediately mined by the same node. We have to have this flag because initially there are no miner nodes in the network.
- Send some coins to the wallet addresses.
$ go_blockchain send -from CENTREAL_NODE -to WALLET_1 -amount 10 -mine
$ go_blockchain send -from CENTREAL_NODE -to WALLET_2 -amount 10 -mine
- Start the node.
$ go_blockchain startnode
- In the
wallet node
window.- Start the node’s blockchain with the genesis block saved above.
$ cp blockchain_genesis.db blockchain_3001.db
- Start the node.
$ go_blockchain startnode
- It’ll download all the blocks from the central node. To check that everything’s ok, stop the node and check the balances.
$ go_blockchain getbalance -address WALLET_1
Balance of 'WALLET_1': 10
$ go_blockchain getbalance -address WALLET_2
Balance of 'WALLET_2': 10
- Also, you can check the balance of the CENTRAL_NODE address, because the
wallet node
now has its blockchain.
$ go_blockchain getbalance -address CENTRAL_NODE
Balance of 'CENTRAL_NODE': 10
- Open a new window as
miner node
.- Set NODE_ID.
$ export NODE_ID=3002
- Initialize the blockchain.
$ cp blockchain_genesis.db blockchain_3002.db
- Create new wallet.
$ go_blockchain createwallet
- Start the node with the created address.
$ go_blockchain startnode -miner MINER_WALLET
- In the
wallet node
window.- Send some coins.
$ go_blockchain send -from WALLET_1 -to WALLET_3 -amount 1
$ go_blockchain send -from WALLET_2 -to WALLET_4 -amount 1
- Quickly! Look into the
miner node
window. See it mining a new block!
- In
central node
window. Also, check the output of the central node.
- In
wallet node
window.- Start the node. It’ll download the newly mined block!
$ go_blockchain startnode
- In
wallet node
window.- Stop
wallet node
and check balances.
- Stop
$ go_blockchain getbalance -address WALLET_1
Balance of 'WALLET_1': 9
$ go_blockchain getbalance -address WALLET_2
Balance of 'WALLET_2': 9
$ go_blockchain getbalance -address WALLET_3
Balance of 'WALLET_3': 1
$ go_blockchain getbalance -address WALLET_4
Balance of 'WALLET_4': 1
$ go_blockchain getbalance -address MINER_WALLET
Balance of 'MINER_WALLET': 10