In this chapter, we continue forward from the last chapter and begin the construction of the Helium blockchain. Firstly, we will create the interface for the cryptographic functions that are used by Helium, and next we will develop some of Helium’s blockchain functionality.
If you are primarily interested in the theory of blockchains and cryptocurrencies, please feel free to proceed to the next theory chapter.
Python Crypto Packages
We will also need some ancillary Python modules. Firstly, we will be using Python’s re module. re is included in the standard Python distribution, so there is no need to install it. It is Python’s regular expression parser. re examines strings for the presence or absence of specified sub-strings and can also perform text substitutions.
A Base58-encoded string only includes characters from the preceding set.
The Base58 character set differs from the ordinary alphanumeric character set, in that it excludes characters that can be easily confused. The excluded characters are I (capital I), l (lowercase L), O (capital o), and 0 (zero).1
We will also be using the pdb debugger. pdb enables us to set breakpoints in source code, step through our source code line by line, and examine stack frames and the values of variables.2 pdb is part of the Python standard distribution, so there is no need to install it through pip. We will always import the pdb module into our Helium source code files.
rcrypt Module Walkthrough
The following module file, rcrypt.py, is the Helium interface into the cryptographic package pycryptodome. Copy the following rcrypt code into a file named rcrypt.py and save this file in crypt sub-directory.3
A Pytest Primer
Unit tests provide us with assurances of code correctness. Consequently, we should always strive to provide a high amount of coverage of our code with unit tests. Conceptually, a unit test tests a small discrete piece of code. This piece of code is typically a function, but it can also be something small like an expression or a collection of expressions. We will be using the Python Pytest module to perform unit testing on our modules.
If you are not acquainted with the Pytest unit test framework, you can read Appendix 3. Appendix 3 is a comprehensive tutorial on how to use Pytest.
All of the Pytest files for Helium are available in the last appendix of this book.
rcrypt Unit Tests
You should see 33 unit tests passing.
The Python Logger
The second parameter of the logger is optional; it specifies the format of the logger’s output.
SEVERITY_LEVEL is a log level: DEBUG, INFO ... CRITICAL.
Helium Block Structure
In this section, we are going to discuss the structure of a Helium block. From Chapter 6, you will recall that a blockchain is an ordered, immutable collection of blocks that have cryptographic validation. Each Helium block is a container for transactions.
In Python, an integer is a signed integer. Python 3 eliminates the difference between integers and long integers. Integers can have any size and are only limited by memory. Python can perform arithmetic operations on integers of unlimited size.
prevblockhash is the SHA-256 message digest of the previous block header in string hexadecimal format. This value is used to test whether the previous block has been tampered with. The prevblockhash value of the genesis block is the empty string.
The subset of block attributes prevblockhash, version, timestamp, difficulty_bits, nonce, and merkle_root is called the block header. The block header does not include the list of transactions. Because the merkle root is included in the header, we can test the validity of the entire block, inclusive of the transactions, by computing the SHA-256 message digest of the header only.
version is the version number of Helium to which this block pertains. This attribute is set in the Helium configuration module and is “1”.
timestamp is the Unix timestamp (epoch time) of when the block was created. Epoch time is the number of seconds that have elapsed since midnight, January 1, 1970, Greenwich Mean Time or UTC. It marks the birth of Unix.
difficulty_bits is the DIFFICULTY_BITS as defined in the Helium configuration module. This is the value when the block was mined. Note that the DIFFICULTY_BITS parameter is periodically varied as the average time to mine a block is adjusted toward ten minutes.
nonce is a number used in the Helium mining algorithm.
merkle_root is a SHA-256 hexadecimal string. The merkle root lets us ensure that the transactions in the block have not been tampered with. It also lets us determine if a particular transaction is present in the block. We will discuss this value in a subsequent chapter.
height is the height of this block. Blocks in a blockchain are ordered in conformity with the distributed consensus algorithm and can be visualized as being stacked one on top of another. The first block, which is the genesis block, has height 0.
tx is a list of transactions. The number of transactions in the list is constrained by the maximum permissible size of a block. In the Helium configuration module, this value is set to 1 MB.
The blockchain is a list and each list element is a block.5
Helium Blockchain Walkthrough
We are now ready to walk through our Helium blockchain code. Copy the following program code into a file named hblockchain.py and copy this file into the block directory. This is not the complete program code for the blockchain module since transactions and database operations have been abstracted. Since we have not developed the code for transaction and database modules as of yet, we mock operations pertaining to these modules with synthetic values.
The Helium blockchain code uses the json and pickle modules. Both of these modules are part of the Python standard library, and thus, there is no need to install them. JSON is a data interchange standard that can encode Python objects into strings and then decode these strings back into the corresponding objects. Pickle serializes objects by converting objects into a sequence of bytes and then writing this sequence to a file. This process is called serialization. Pickle can deserialize files to recover the original objects. If you have never used Pickle before, Appendix 5 provides a concise introduction to Pickle usage.
The add_block function adds a block to the Helium blockchain. This function receives a block. The function checks whether the block attributes have valid values. In particular, it verifies that the transactions included in the block are valid. In the event that the block is invalid, the function returns with a false value. If the block is valid, the function serializes the block to a raw byte file through the Pickle module. This block is then added to the blockchain list in memory.
The read_block function receives an index into the blockchain and returns the block corresponding to the index. An exception will be thrown if the index is out of bounds.
The blockheader_hash function receives a block and returns the SHA-256 message digest of the block header as a hexadecimal string. Observe again that since the merkle root can be used to test whether the transactions in the block have been tampered with, we do not have to compute the SHA-256 cryptographic hash of the block over the entire block contents. It is sufficient to calculate this value over only the block header since it includes the merkle root.
The validate_block function tests whether a block has valid values. This function also validates the previous block by comparing SHA-256 message digests.
Helium Blockchain Unit Tests
You should see 23 unit tests passing.
Conclusion
In this chapter, we have developed the rcrypt module that encapsulates the cryptographic functions that are used by Helium. We also wrote unit tests for our cryptographic module. We then wrote program code for the Helium blockchain. The module hblockchain encapsulates Helium blockchain functionality. Finally, we wrote some unit tests to validate the blockchain module.
In the next chapter, we turn our attention to the processing of cryptocurrency transactions.