For enabling mining, we need to develop a mining function. The mining functionality needs to generate a digest on a given message string and provide a proof-of-work. Let us discuss this in this chapter.
We will write a utility function called sha256 for creating a digest on a given message −
def sha256(message): return hashlib.sha256(message.encode('ascii')).hexdigest()
The sha256 function takes a message as a parameter, encodes it to ASCII, generates a hexadecimal digest and returns the value to the caller.
We now develop the mine function that implements our own mining strategy. Our strategy in this case would be to generate a hash on the given message that is prefixed with a given number of 1’s. The given number of 1’s is specified as a parameter to mine function specified as the difficulty level.
For example, if you specify a difficulty level of 2, the generated hash on a given message should start with two 1’s - like 11xxxxxxxx. If the difficulty level is 3, the generated hash should start with three 1’s - like 111xxxxxxxx. Given these requirements, we will now develop the mining function as shown in the steps given below.
The mining function takes two parameters - the message and the difficulty level.
def mine(message, difficulty=1):
The difficulty level needs to be greater or equal to 1, we ensure this with the following assert statement −
assert difficulty >= 1
We create a prefix variable using the set difficulty level.
prefix = '1' * difficulty
Note if the difficulty level is 2 the prefix would be “11” and if the difficulty level is 3, the prefix would be “111”, and so on. We will check if this prefix exists in the generated digest of the message. To digest the message itself, we use the following two lines of code −
for i in range(1000): digest = sha256(str(hash(message)) + str(i))
We keep on adding a new number i to the message hash in each iteration and generate a new digest on the combined message. As the input to the sha256 function changes in every iteration, the digest value would also change. We check if this digest value has above-set prefix.
if digest.startswith(prefix):
If the condition is satisfied, we will terminate the for loop and return the digest value to the caller.
The entire mine code is shown here −
def mine(message, difficulty=1): assert difficulty >= 1 prefix = '1' * difficulty for i in range(1000): digest = sha256(str(hash(message)) + str(i)) if digest.startswith(prefix): print ("after " + str(i) + " iterations found nonce: "+ digest) return digest
For your understanding, we have added the print statement that prints the digest value and the number of iterations it took to meet the condition before returning from the function.
To test our mining function, simply execute the following statement −
mine ("test message", 2)
When you run the above code, you will see the output similar to the one below −
after 138 iterations found nonce: 11008a740eb2fa6bf8d55baecda42a41993ca65ce66b2d3889477e6bfad1484c
Note that the generated digest starts with “11”. If you change the difficulty level to 3, the generated digest will start with “111”, and of course, it will probably require more number of iterations. As you can see, a miner with more processing power will be able to mine a given message earlier. That’s how the miners compete with each other for earning their revenues.
Now, we are ready to add more blocks to our blockchain. Let us learn this in our next chapter.