By Ary Bandana, DIGITS Staff Writer
In this article, we’re going to try and create our own blockchain. So most people have heard of a blockchain because of Bitcoin. Bitcoin uses a blockchain to store and verify transactions to prevent double-spending attacks. But blockchain doesn’t have to store transactions data only it can store any data we want. For this project, we’re gonna be storing text strings in our blockchain. here you can see the output of our program.
And this shows our whole blockchain. The genesis block is the very beginning of the blockchain. And then after the genesis block, we have the first block and the subsequent block after it. And you can see each block has different numbers of hashes that were required to find it. So to find the first block, we had to do two point five million hashes. The Genesis block obviously has zero because that block was built into our blockchain.
import datetime import hashlib class Block: blockNo = 0 data = None next = None hash = None nonce = 0 previous_hash = 0x0 timestamp = datetime.datetime.now()
So let’s learn how to create one of these. So our blockchain has just two classes. It has a Block class and a Blockchain class. So whenever we mine a block, we’re adding it to the blockchain. And that’s where this Block class comes in. Every block is an instance of the Block class. So every block has a number. It has the data we want to store and it has a pointer to the next block called next. Every block also has a hash and it stores the hash of the previous block. This is what makes the blockchain immutable. Because to change the hash of a block on the blockchain it requires you to change the hash of every subsequent block because of this previous hash. The previous hashes are what ties them together. The timestamp isn’t really relevant to our blockchain. But if this blockchain was part of a network, we would use the timestamps to synchronize all of the blockchains.
def __init__(self, data): self.data = data def hash(self): h = hashlib.sha256() h.update( str(self.nonce).encode('utf-8') + str(self.data).encode('utf-8') + str(self.previous_hash).encode('utf-8') + str(self.timestamp).encode('utf-8') + str(self.blockNo).encode('utf-8') ) return h.hexdigest()
So you can see when we create a block, all we do is we store its data but where things get more interesting is where we calculate the hash of the block. So to calculate the block’s hash, we get the nonce, the data, the previous hash, the timestamp, and the block number. We add them all together in one big string and we run the SHA 256 function. And that gives us the block’s hash. So that’s the structure of a block.
def __str__(self): return "Block Hash: " + str(self.hash()) + "\nBlockNo: " + str(self.blockNo) + "\nBlock Data: " + str(self.data) + "\nHashes: " + str(self.nonce) + "\n--------------"
And finally, this function just means that when we print out a block, we can see it in a format like in the first picture.
blockchain = Blockchain() for n in range(10): blockchain.mine(Block("Block " + str(n+1))) while blockchain.head != None: print(blockchain.head) blockchain.head = blockchain.head.next
let’s see what the blockchain itself looks like. So we have a blockchain class and you can see down here we instantiate our blockchain class when we have this for loop here. And what this does is it just generates 10 random blocks. That’s why you can see the output of 10 blocks. That’s just this for loop that is continuously mining until it mines 10 blocks. And then underneath here, all we’re doing is printing out each block in the blockchain.
self.block.next = block self.block = self.block.next
So a blockchain is pretty much just a linked list, except all the items on the list are tied together because of this previous hash. So you can see whenever we calculate the hash of the block, we include the hash of the previous block. And this is what ties all the blocks together. If I was to change the hash of any block, every block that comes after, it is going to have a different hash. So because a blockchain is just a linked list to add something to the list, all we need is these two lines here. Every block has a pointer to the next block. So we set the next pointer equal to whatever block we want to add, and that adds a block to the end of the list. And the next line just moves the next pointer up so that we can keep adding new blocks.
def add(self, block): block.previous_hash = self.block.hash() block.blockNo = self.block.blockNo + 1
But because this is a blockchain, we have to set the previous hash equal to the block that’s currently at the top of our list. So self.block refers to the block at the top of our link list. Because we’re running this before we add a new block it refers to the previous block. Then the new block we have to set it equal to the current block number plus 1.
block = Block("Genesis") dummy = head = block
So the very first block is the genesis block that’s here. It’s built into the blockchain and then this dummy equals head equals block is of a python specific thing when you’re implementing a linked list. You have to remember where the start of the list is. And you call that the head. So it’s not enough just to say head equals block to set it equal to the genesis block because, in Python, objects are all passed by reference. So the head and block variable would be the exact same if I was to change a block, it would change the head variable. So in Python, there’s a trick you can just type in the word dummy or any variable equals head equals block. And that will mean had does not point to the same object as a block. And that gets around this sort of python quirk for implementing a linked list.
def mine(self, block): for n in range(self.maxNonce): if int(block.hash(), 16) <= self.target: self.add(block) print(block) break else: block.nonce += 1
So the interesting stuff is in mining. So every block has a hash and every hash is just a number. These are just hexadecimal numbers. So the way you determine whether a blocks hash should be put into the blockchain is you check whether the value of the block’s hash is less than a specific target number. And this is exactly what bitcoin does.
diff = 20 maxNonce = 2**32 target = 2 ** (256-diff)
So the nonce has to be less than the target number. And if it is, then we accept the block. And that’s why we can change the difficulty. So we set the target equal to 2 to the power of 256. But by adjusting the difficulty, which is up there, we’ve set it to 20 at the moment. So that will be to the power of 256 minus 20 or 2 to the power of 236. So if we’re increasing the difficulty number what we’re doing is we’re decreasing the target range. So the acceptable range has become smaller. So it gets more difficult to mine a block because it blocks hash has to be less than or equal to the target range to be accepted. That’s how bitcoin controls the rate at which new blocks are mined. And the nonce number is what we use to change our guess. So whenever we’re trying to figure out the hash that will be accepted, we have to change our data somehow. So we have to change the hash of the block. The nonce, just a number that we increment every single time we make a guess by changing that one number we’re getting a completely different hash.
def mine(self, block): for n in range(self.maxNonce): if int(block.hash(), 16) <= self.target: self.add(block) print(block) break else: block.nonce += 1
So this for loop just guess is pretty much from zero to the maximum nonce. And every time we loop through, what we’re doing is we’re checking the current blocks hash is less than or equal to the target. We have to convert it to an integer because we’re using the comparison operator in Python. If the block’s hash is less than or equal to the target, then we can add the block to the blockchain and we print out the block and we stop mining because we’ve got the block we were looking for. Otherwise, what we do is we increase the nonce by one and we go again. And that’s pretty much it for our blockchain.
import datetime import hashlib class Block: blockNo = 0 data = None next = None hash = None nonce = 0 previous_hash = 0x0 timestamp = datetime.datetime.now() def __init__(self, data): self.data = data def hash(self): h = hashlib.sha256() h.update( str(self.nonce).encode('utf-8') + str(self.data).encode('utf-8') + str(self.previous_hash).encode('utf-8') + str(self.timestamp).encode('utf-8') + str(self.blockNo).encode('utf-8') ) return h.hexdigest() def __str__(self): return "Block Hash: " + str(self.hash()) + "\nBlockNo: " + str(self.blockNo) + "\nBlock Data: " + str(self.data) + "\nHashes: " + str(self.nonce) + "\n--------------" class Blockchain: diff = 20 maxNonce = 2**32 target = 2 ** (256-diff) block = Block("Genesis") dummy = head = block def add(self, block): block.previous_hash = self.block.hash() block.blockNo = self.block.blockNo + 1 self.block.next = block self.block = self.block.next def mine(self, block): for n in range(self.maxNonce): if int(block.hash(), 16) <= self.target: self.add(block) print(block) break else: block.nonce += 1 blockchain = Blockchain() for n in range(10): blockchain.mine(Block("Block " + str(n+1))) while blockchain.head != None: print(blockchain.head) blockchain.head = blockchain.head.next
Leave a Reply