Blockchain functionality
The block linking process consists of several elements, such as creating a structure from the information, calculating the hash of the block, and appending it to the blockchain.
Let's break down each of these functionalities into blockchain methods:
class Blockchain(object): """A class representing list of blocks""" def __init__(self): self._chain = [self.get_genesis_block()] self.timestamp = int(datetime.now().timestamp()
The preceding class is a collection of class methods that create a valid blockchain using a hash function. The constructor of the Blockchain will initialize a chain by appending a genesis block, which is the first block of the blockchain, and doesn't have any reference to a previous block:
def get_genesis_block(self): """creates first block of the chain""" return Block(0, "0", 1465154705, "my genesis block!!", "816534932c2b7154836da6afc367695e6337db8a921823784c14378abed4f7d7"
)
A genesis block is a hardcoded block that is appended to the beginning of the blockchain. It is created with static contents. The preceding genesis block has a hardcoded hash value that is created using SHA-256, as follows:
SHA256.new(data=(str(0) + "0"+ str(1465154705) +"my genesis
block!!").encode()).hexdigest() def calculate_hash(self, index, previous_hash, timestamp, data): """calculates SHA256 hash value""" hash_object = SHA256.new(data=(str(index) + previous_hash +
str(timestamp) + data).encode()) return hash_object.hexdigest()
calculate_hash is a crucial method in the blockchain because this method creates a hash value that binds all the blocks together. An SHA-256 hash value is created using the PyCryptodome package, as shown in the previous chapter. This method concatenates the block index, the hash value of the previous block, the timestamp, and the data required to create a string that needs to be hashed. The SHA256 hash function generates a digest that is the hash value of that block.
We need to find the hash value of the previous block during the creation of the next block. The following function identifies the last block appended to the chain:
def get_latest_block(self): """gets the last block from the blockchain""" try: return self._chain[-1] except IndexError as e: return None
The following function will build a block by constructing all the attributes that are required to create a Block object. It will also calculate the hash value for the current block. A new Block object consisting of the block structure will finally be created:
def create_block(self, block_data): """creates a new block with the given block data""" previous_block = self.get_latest_block() next_index = previous_block.index + 1 next_timestamp = self.timestamp next_hash = self.calculate_hash(next_index,
previous_block.hash, next_timestamp, block_data) return Block(next_index, previous_block.hash, next_timestamp,
block_data, next_hash)
The following functions are used to add, reset, and read the blocks of the blockchain. The add_block method and the chain attribute are the only class members that need to be exposed to the user:
def add_block(self, data): """appends a new block to the blockchain""" self._chain.append(self.create_block(data)) @property def chain(self): """created a dict containing list of block objects to view""" return self.dict(self._chain) def dict(self, chain): """converts list of block objects to dictionary""" return json.loads(json.dumps(chain, default=lambda o:
o.__dict__)) def reset(self): """resets the blockchain blocks except genesis block""" self._chain = [self._chain[0]]