Important Notice: Following tutorial requires at least basic ES6 knowledge.
Ethereum was released three years ago — and it quickly became a de facto standard of decentralized trust management by introducing a powerful smart contracting platform. Together with its spiritual predecessor Bitcoin it also changed how the financial world works — by introducing a completely new way of sharing funds.
But despite of its popularity, the way how blockchain works under the hood is still a mystery for many developers. From my standpoint blockchain solutions seem to be pretty interesting and I want to share my modest knowledge with others — that’s why this article exists.
In this tutorial I’m going to show some core blockchain concepts by writing a small Ethereum-like cryptocurrency — it’s going to contain basic wallet, block, state and transaction management. Article is divided into five sections — each describing one major topic:
- Wallet: First section will tell you about wallets in terms of cryptocurrency and how they are used to maintain authenticity.
- Transaction: In this section I will describe basic transaction structures and how they are signed and verified.
- Block: This section will be dedicated to transaction blocks — how they are linked and mined.
- State: In this section we are going to implement a small Ethereum-like state container.
- Blockhain: Final section will tie everything up and create a proper block and transaction verification mechanisms.
Each section will end with a live example, so you could instantly check all the progress we made so far. Lets get started.
Wallet is a pair of keys that are used during authorization — without any kind of information about current account state. It consists of two keys: the public one is associated with your address and the private one which must be kept in secret. Those keys are involved in the process known as the asymmetric cryptography — you can encrypt something using one key and decrypt it with another one from the same pair, but you can’t use a single key for both operations.
This mechanism can be used for ensuring authenticity — imagine you have some kind of a bank account associated with your public key. You can encrypt your orders with a private key and attach resulting string to the order itself — basically creating a digital signature. Bank teller can easily verify incoming assignments by decrypting signature with corresponding public key and comparing resulting string with the original message — and mark it as a fraud if they do not match.
Here goes our first piece of code — it contains wallet class declaration with the following methods:
- Constructor: Consumes options object and uses it to establish base wallet structure with two fields present — private and public key.
- Wallet Creator: Generates a new asymmetric key pair and turns it into a brand new wallet with properly populated fields.
- Wallet Converters: Utility that converts raw HEX keys stored in the wallet into a PEM-encoded certificates used by Node. Long mysterious constant is an ASN-encoded binary header used to tag private and public parts of the resulting key.
Now we can try using it in action — by doing a couple of signatures, both valid and not:
First live example is available here —so you play around with different data sets and wallets.
Transaction is a data structure that holds all required information about fund transfer from one account to another. Valid transaction must contain a digital signature of its sender — in order to ensure that it was actually authorized. Another thing that every transaction has is hash — a string of a fixed size that can be retrieved from its body whenever you need it.
Hash is a form of a digital object checksum — you can create the same object in a different ways billion times, but its hash will always remain the same if the fields are the same. But even the smallest change — and it will result an a completely different hash.
Also, every transaction contains a nonce —an integer number that must be incremented by one every time sender makes a new transfer. Without it malefactor receiver can capture a valid transaction and use it multiple times — basically, stealing all money.
Code stated above defines a basic transaction structure — we may take a closer look to its fields first:
- From: Sender address, used as a public key during verification process.
- To: Receiver wallet.
- Value: Amount of the Goo that is going to be taken from the sender account and sent to the receiver.
- Nonce: Number of successful transactions made by the sender before, used to prevent multiple usage of the same transaction.
- Signature: Digital signature made by signing transaction body hash with a sender private key.
But what about instance methods? Here they go:
- Hash: Returns current transaction hash based on its fields (excepting signature value).
- Sign: Consumes sender private key and returns a new transaction with a digital signature based on it.
- Test: Uses sender public key to verify transaction signature status and returns it.
Now we can utilise our code by simulating a few possible use cases — making both valid and fraud transactions and check them:
Second example is available here — it contains transactions code and a small playground with Bob and Alice.
Block is a bunch of transactions packed in a specific way. Blocks are lined into the blockchain — immutable list of historically ordered blocks. Immutability is achieved by storing block ancestor hash within its body, so that linkage will break something will change in the middle of the chain — basically, marking it compromised.
In order to add a new block into the end of the chain it needs to be mined — which means that you need to spend some time and computing power with some amount of cryptocurrency as a reward. Mining prevents network from block overflow by limiting number of blocks that can be added at the same moment.
Goal of mining is to find an appropriate block configuration — so its hash would start from a specific number of zeroes. That is performed by a nonce lookup — a completely meaningless value in a block which can be adjusted in order to try to satisfy the mining condition.
What happens in the class declared above? Same as for transaction, we may start our examination from the constructor properties first:
- Parent Hash: Stores parent block hash — basically, that fields turns block list into the actual blockchain.
- State Hash: Contains the hash of the latest state just before block was pushed into the chain.
- Miner: Address that should be associated with the block miner, reward is going to be sent here.
- Nonce: A completely meaningless value produced by mining — block hash starts with a required number of zeroes with it.
- Transactions: List of transactions packed into the block and ordered by their nonce.
Hashing, mining and basic status check are done using instance methods:
- Hash: Converts block header and list of its transactions into the string and returns hash based on that information.
- Mine: Synchronous miner implementation — increments nonce value until the proof of work condition is met. Returns block with an appropriate nonce as a result.
- Test: Returns mining status defined by counting number of leading zeroes in its hash. Does not check transactions, needs to be performed manually.
Now we can simulate a simple blockchain without a state — by storing linked blocks in a list:
Third example is available here —so you can try different block combinations and attempt writing own chain verification mechanism.
State is a representation of the current system status — it holds all the required information about balances and is deterministically based on the blockchain history. Generally, it is a key-value storage, where key is a wallet address and value is a data structure holding account balance and number of transactions sent.
In terms of Ether state is a pretty complicated structure packed with a patricia trie, but our implementation is fairly primitive:
- Constructor: Establishes state key-value dictionary for storing available account data.
- Hash: Iterates over existing accounts and uses them to get the current state hash.
- With (for transactions): Consumes transaction and returns updated state — with a proper coin amount transferred from one account to another.
- With (for blocks): Consumes block and returns updated state — with appropriate reward sent to the the miner account. Does not process block transactions.
That is enough to try out basic state mutations — both with transactions and blocks:
Fourth example is available here — it is pretty simple, feel free to experiment with different wallets and values.
Now we can tie everything up into a nice class, that is going to contain state, history and consensus algorithms, providing a handy high-level interface. I am going to be short here, just take a look at the code itself:
Unlike previous classes, this one contains static methods — both of them are pure functions that perform verification and return an updated state:
- Transaction verifier: Checks sender balance and nonce, ensures that transactions are correctly ordered and that current signature is valid.
- Block verifier: Compares block parent and state hashes with current ones, checks mining status and runs verification process for its transactions.
What about instance methods — all of them provide handy access to the chain values, like its current state and history:
- Constructor: Determines primary blockchain properties — block history and current state.
- Balance: Returns current account balance if it exists, returns zero balance otherwise.
- Push: Verifies block and pushes it into the current blockchain by updating its state and history.
- Mine: A handy wrapper for the previous method that automatically mines block and pushes it into the blockchain.
Now we are ready for our final example — it will tie everything together into a complete picture:
Final playground is available here — with all the code we made so far, so you can experiment and try different scenarios.
We have a small fully functional cryptocurrency now. Lets take a closer look at the results:
- Goothereum contains utilities for generating wallets.
- Goothereum can create, sign and verify transactions.
- Goothereum is able to pack transactions into blocks and mine them.
- Goothereum can track state changes and present them in a handy way.
The main purpose of this article was to demonstrate core principles of blockchain cryptocurrencies, so Goothereum lacks many things from real Ethereum:
- Goothereum doesn’t support any kind of smart-contracting at all.
- Goothereum doesn’t use merke trees for hashing state.
- Goothereum uses public key as wallet address instead of hashing it.
- Goothereum has no network functionality — at least, yet.
As you can clearly see, that’s a great field for new features and improvements — repository is available here, so do not hesitate to fork and experiment. Do not hesitate to write comments if you find any critical errors.
I want to thank the whole Ethereum Team for making an amazing innovatve platform for smart contracting and decentralized computing.
I promise to write a second part of this article if it gets popular — it is going be dedicated to the peer to peer networking — so please share and recommend if you enjoy it. Thanks for reading!