Cryptocurrency Development in Python Using Blockchain Lists (Part 1)

Ali Fakhry
CodeX
Published in
8 min readAug 12, 2021

--

Image by WorldSpectrum from Pixabay
Image by WorldSpectrum from Pixabay

As blockchain and cryptocurrency development has continued to excel in the past few years, more people are wanting to develop cryptocurrencies of their own. The reasons behind this vary; financial flexibility for a unique startup, rewarding users utilizing one’s newly launched software, and the ability to practice developing blockchains.

Regardless of the reasons, this article will describe the initial steps to developing one’s own cryptocurrency.

Python is a dynamically typed language; thus, this means that the variable is only determined during runtime, rather than being statically typed. Additionally, Python is a strongly typed language as all variables have a type when performing actions — even though the user does not input said type.

This is one example of Python’s simplicity. Python allows many tasks to be performed with one simple function. Due to this, Python is a recommended language for first-time blockchain development.

Yet, for more developed projects, I would recommend the use of C++. Compared to Python, C++ allows for the manipulation of pointers and accessing pointers, which are essential for developing more complicated linked lists.

However, as this is a simple introduction, Python will work sufficiently well.

In this article, the code that is being written will be locally executed. For future articles, the blockchain will have to be logged in a system or via the cloud to allow others to access said cryptocurrency.

By doing so, others will be able to mine the currency and add it to the current blockchain.

The code will be written between two files:

cryptoFunctions.py

mainBlock.py

The mainBlock.py file will have two classes:

  • BlockValue
  • main

The cryptoFunctions.py file will have no classes but multiple definitions.

These include the following:

  • getPreviousHash()
  • returnBlock()
  • setHash()
  • runData()
  • mine()
  • newTransactionData()
  • verifyChain()

The first thing we want to do is import the requisite libraries and files.

import time
import CryptoFunctions

The time import is used for timestamps for block creations. We also have to import CryptoFunctions so that we can use the definitions included within that file.

Next, we need to initialize the global variables.

diff = 3
nonce = 0
number = 1
arrayOfValues = []
currentArray = []
currentIndex = 0
previous_hash = "0" *64

The primary variable listed above — diff — represents the phrase “difficulty.” The difficulty is the number of 0s at the offset of a hash that the blockchain is attempting to find.

When attempting to find the requisite hash, the data will be made up of stagnant data such as the current time when the mining commenced and the sender and receiver of said cryptocurrency.

Yet, what can continue to manipulate the encryption is the iteration of the nonce. If the nonce is changed, the overall hash of the block will change. Thus, the nonce will need to be incremented until the block meets the required difficulty. For each block, the nonce value will start as 0 — which is declared in the global variable.

The third global variable — number — represents the current block.

The other four global variables are just placeholders.

The next set of global variables will represent our “transaction information” that will be locally set.

senderData = ["Sender_One", "Sender_Two", "Sender_Three", "Sender_Four", "Sender_Five"]recipientData = ["Recieve_One", "Recieve_Two", "Recieve_Three", "Recieve_Four", "Recieve_Five"]amountData = ["1", "2", "3", "4", "5"]transactionIndex = 0

The first global variable represents “data” that will locally represent data sent through a cryptocurrency currency. This is represented as the variable “senderData.”

Similarly, the second variable — recipientData — holds locally set data that mimics real examples of “recipient data.”

The next variable is “amountData” which represents possible numbers of cryptocurrencies sent between the sender and the recipient.

Lastly, transactionIndex represents where the transaction data must be pulled from.

Next, let’s initialize the second file, CryptoFunctions.py. Within this file, we will have one import.

from hashlib import sha256

The method that we will apply to encrypt the blockchains is sha256 — which is an industry-standard encryption method.

Next, we will continue to develop upon the primary file by writing the BlockValue class.

The BlockValue class will have two definitions, “__init__” and “new_data.”

class BlockValue:def __init__()def new_data()

The arguments that will be taken into each function will be as follows.

def __init__(self, index, transactions, timestamp, previous_hash, number, transactionIndexUpdated):def new_data(self):
  • The index argument will be used to track the index of the larger array, “arrayOfValues.”
  • The transactions are a placeholder but are an array that will be composed of the current sender, recipient, and numeric data for each block.
  • The timestamp argument will be used to identify the time of formation.
  • The previous_hash argument is used for verifying a continuous chain. The previous_chain of the next chain should be the same hash as the current block hash.
  • The number argument is used to identify which block that is currently being referenced, not in terms of an index value but rather numerically.
  • The transactionIndexUpdated argument is used to identify which index the transaction data is being pulled from.

Within the __init__ definition the code will be set as follows:

def __init__(self, index, transactions, timestamp, previous_hash, number, transactionIndexUpdated):self.index = index
self.transactions = transactions
self.timestamp = timestamp
self.previous_hash = previous_hash
self.nonce = 0
self.number = number
self.transactionIndex = transactionIndexUpdated

Within the main function, we will pass in these self-based variables so that we can pull them when we need to use them later.

The second part of the BlockValue class — new_data() — will be represented as such:

def new_data(self):
timestamp = time.time()
transactions = CryptoFunctions.newTransactionData(
senderData[self.transactionIndex],
recipientData[self.transactionIndex],
amountData[self.transactionIndex], [])
currentArray, number = CryptoFunctions.runData(self.index,
transactions, timestamp, previous_hash, nonce, self.number,
arrayOfValues,diff)

return currentArray, number
  • The timestamp function will be used to retrieve the time in which the block was fully formulated.
  • The transactions will retrieve the current transaction data from the senderData, recipientData, and the amountData using the “newTransactionData” function.

Now, we need to go over the various functions provided in the CryptoFunctions file.

The first function will be “getPreviousHash(currentIndex, arrayOfValues)” which takes in the arguments “currentIndex” and “arrayOfValues.”

def getPreviousHash(currentIndex, arrayOfValues):
if (currentIndex == 0):
return "0" * 64
else:
array = arrayOfValues[currentIndex - 1]
return array[1]

If the currentIndex is equal to 0, that means that there is no previous block, thus return the initial block being “0” * 64. All hashes are initialized with 64 numbers.

If there is a previous block, the previous hash can be retrieved at the index of 1 in the previous block within the larger “arrayOfValues.” That is due to the fact that hash values are stored at the index of 1.

The second function will return the current block. The function — returnBlock — will take in two arguments — arrayIndex and arrayOfValues.

def returnBlock(arrayIndex, arrayOfValues):
arraySubsection = arrayOfValues[arrayIndex]
newValue = ("Block Number: " + str(arraySubsection[0]) +
"\nHash: " + str(arraySubsection[1]) + "\nPrevious: "
+ str(arraySubsection[2]) + "\nTransactional Data: " +
str(arraySubsection[3]) + "\nNonce: " +
str(arraySubsection[4]) + "\nTimestamp: " +
str(arraySubsection[5]) + "\n")
return (newValue)

This function is used to return the various values within each block. Each block is organized with the block number at the index of 0, the hash will be initialized at the index of 1, the previous hash will be stated at the index of 2, the transaction data (sender, receiver, and the amount) will be stated at the index of 3, the nonce value will be initialized at the index of 4, and the timestamp of the block formation will be presented at the index of 5.

The third function that will be declared will be used to return the hash value taking in five arguments — transactions, timestamp, previous_hash, nonce,
number — that will be initialized as “*args.”

def setHash(*args):
hashing_text = ""
hashing = sha256()
for arg in args:
hashing_text += str(arg)
hashing.update(hashing_text.encode('utf-8'))
return hashing.hexdigest()

The first thing this function will do is initialize the sha256() encryption method. Then, through iteration, it will encrypt what has been pushed through the arguments.

After the resulting hashing values are updated, the encryption will be returned.

The fourth function will implement mining to this currency that is being developed. The hash will most likely not fit the difficulty. Thus, it must be mined repeatedly using the mine function.

The arguments that will be taken will be transactions, nonce, hash, number, previous_hash, timestamp, currentIndex, and diff.

def mine(transactions, nonce, hash, number, previous_hash, timestamp, currentIndex, diff):while (hash[:diff] != "0" * diff):
nonce += 1
hash = setHash(transactions,timestamp,previous_hash, nonce,
number)
return (nonce, hash)

As explained previously, the nonce value can be increased to further alter the hash to meet the requisite difficulty. That is what is occurring within this function.

Once the difficulty is met, the appropriate nonce and hash values will be returned.

The fifth function that will be used is is to set the current sender, recipient, and data amount into one singular value.

The function, newTransactionData, will have the arguments sender, recipient, amount, and values.

def newTransactionData(sender, recipient, amount, values):

values.extend([sender, recipient, amount])
return values

The sixth function is used to verify the block values within the chain. This simple function will just be used to check if that the current hash is equal to the previous hash of the next block.

The arguments that will be taken are arrayOfValues and lengthData.

def verifyChain (arrayOfValues, lengthData):
if (lengthData > 1):
for x in range(lengthData-1):
currentArray = arrayOfValues[x]
secondaryArray = arrayOfValues[x+1]
if (currentArray[1] == secondaryArray[2]):
continue
else:
return(False)
return (True)

Within the CryptoFunctions file, the primary function that will be executed is runData. This function will take in the arguments currentIndex, transactions, timestamp, previous_hash, nonce, number, arrayOfValues, and diff.

This function is used to organize the various variables that will be used to formulate the arrayOfValues. Additionally, this function will be used to organize the hashing and mining methods.

def runData(currentIndex, transactions, timestamp, previous_hash, nonce, number, arrayOfValues, diff):previous_hash = getPreviousHash(currentIndex, arrayOfValues)hash = setHash(transactions, timestamp, previous_hash, nonce, 
number)
nonce, hash = mine(transactions, nonce, hash, number,
previous_hash, timestamp, currentIndex, diff)
arrayOfValues = [number, hash, previous_hash, transactions,
nonce, timestamp]
number += 1return arrayOfValues, number
  • As seen in the BlockValue class, after retrieving the transactions data, the BlockValue function will execute “runData” using all the valid arguments.
  • Within this definition, the first method that occurs is that the previous_hash data is pulled using the getPreviousHash method.
  • Then, the hash is retrieved using the resulting valid arguments.
  • The hash will most likely not fit the difficulty. Thus, it must be mined repeatedly using the mine function.
  • After the previous_hash, nonce, and hash values are retrieved, the arrayOfValues can be updated.
  • The block number should also be updated for previous iterations.
  • Lastly, return arrayOfValues and the updated number.

Now, let’s finalize the Nevalinna file.

class main:for value in range(len(senderData)):

genisisBlock = BlockValue(currentIndex, [], time.time(),
"0" * 64, number, transactionIndex)

currentArray, number = genisisBlock.new_data()
arrayOfValues.append(currentArray)print(CryptoFunctions.returnBlock(currentIndex,
arrayOfValues))
currentIndex += 1transactionIndex += 1print(CryptoFunctions.verifyChain(arrayOfValues,
len(senderData)))
  • Within the main class, we will iterate based on the number of values within the senderData range.
  • The primary block without any modification will be set under the conditions of genisisBlock.
  • The arrayOfValues will append the currentArray to make the collection of lists.
  • At the end of each iteration print the currentArray.
if __name__ == '__main__':
main()

Add the main initialization at the start.

Conclusion:

As of now, this program allows us to run a locally formulated cryptocurrency program. Thus, blocks can be mined and all the encryption needed will be stated.

Yet, users outside of this locally ran program will not be able to mine the currency. This will be further explained in a future update.

As of now, you can see this program within this GitHub location:

https://github.com/AliFakhry/Nevalinna

--

--

Ali Fakhry
CodeX

Reinforcement learning, artificial intelligence, and software. NYU.