Blockchain (Without The Hype)
A technical guide about blockchain to understand what exactly it is and isn't
I wanted to be super transparent about this: as of writing this article, I am working at a startup that is in the web3 space. Despite this, I think I tried to be as objective as possible when approaching this topic as these are 100% my own thoughts.
Since this article is quite long, I hope this table of contents helps keep things organized:
- Satoshi Who?
- A Chain Of Blocks
- Crypto
currenciesgraphy - Can We Get Along?
- The Network
- The Protocol
- Let’s Get Building
- Final Thoughts
Chances are if you have been keeping up with current technology and software, you surely would have heard words like “NFTs”, “cryptocurrencies” and “tokens”. These are a part of an overall decentralization wave that has come to the mainstream over the past 5 years. There are constantly new innovations happening in this space with NFTs and Web3 being some of the biggest ones at the moment. These innovations have been accompanied by massive hype cycles that you have probably witnessed firsthand if you happen to be active on Twitter or certain forums. Enthusiasts often discuss and highlight exciting potential applications in the supply chain and finance industries while hailing decentralized technologies as the next big thing that everyone ought to embrace.
But, as cryptocurrencies and related assets have plummeted recently and continue to go through high levels of volatility, the whole decentralized space has gotten a negative perception. This is mostly due to bad actors in this new space, and this new technology being in its infancy. A lot of the other concerns are also very well founded as many questions have arisen regarding the technical aspects of technology with one of the biggest ones being how these technologies will scale. Blockchain has become a classic case of overhyping an exciting technology where people don't understand what the technology does and start to claim things that the technology doesn't actually do.
People also begin to think of terms like crypto, Bitcoin, Web3, and blockchain as one and the same. While all these are closely related they refer to different concepts.
The negative perception that cryptocurrencies like Bitcoin have gotten for their financial performance has extended to the blockchain itself, which is the underlying technology behind a lot of these decentralized efforts. However, my goal is to highlight exactly what blockchain is and more importantly what it is not. And I hope to do this in the most fun(damental) way possible … building our own blockchain!
Satoshi Who?
Before we get into the technology itself, I wanted to give a quick history lesson on the blockchain to understand how and why it came into being. While there were some key ideas that were discussed in the 1980s and 1990s that are similar and have likely inspired what has now become the blockchain. The main idea that was popular in the 90s was to use technology to create immutable timestamps for digital files. The blockchain is mainly accredited to Satoshi Nakamoto (a fictitious name for a person or group of people; their identity is still unknown to this day;) through the white paper: Bitcoin: A Peer-to-Peer Electronic Cash System which was released in 2008. In this process, Bitcoin became the first popular application of the blockchain.
While I definitely recommend that you go read the paper as I think this is a super influential paper and introduces lots of great ideas that I won't be able to fully cover in this article, I think a key thing to keep in mind when talking about decentralization is that this white-paper was released during the height of a global recession which led to many people criticizing the centralized financial systems that were in place at the time and continue to be very prevalent today. I think understanding this context should give people a better understanding of why this decentralization movement started. Debating the merits of a push towards decentralization is a can of worms for another day.
A Chain Of Blocks
At its core blockchain is a distributed and decentralized digital ledger.
Let’s break that down. A ledger in this context is a book that is used to keep track of financial accounts and account transactions. A blockchain is a digital ledger since it is stored in a computer instead of a physical book. I think it’s easiest to just imagine the blockchain as a database.
One core way that the blockchain differs from a traditional database is that data is not stored in a table with columns/rows or in a document collection. Pieces of data like the transactions between accounts are stored in a block. Each block has a unique hash which is created through a hashing function and can be thought of as a unique ID for a particular block. We will go over hashing and hashes in more detail in a later section. Each block also stores the previous block’s hash which links blocks together. The first block in a blockchain is known as the genesis block (it usually uses a 0 in place of the previous hash).
This results in a “chain” of sequentially ordered blocks. Hence, the name blockchain.
As a result of structuring data this way, the blockchain naturally maintains a timeline of how the state of the blockchain was modified with each block, making it easy for it to obtain an exact state of the blockchain at any given time up to the present. A good way of understanding this is to imagine that the blockchain initializes with a base “state” (this could look like having millions of accounts each with 0 currency in it) and the point is to use a standard protocol that notes the change from the previous state of the blockchain to the current state (ex. +50 in Alice’s account and -50 in Bob’s account). In essence, this public timeline of transactions is the blockchain. Additionally, what this also means is that you can only add new transactions to the blockchain, since changing a previous transaction would change the block’s hash and thus change every single subsequent block resulting in you having to convince everyone that all these new blocks should be used over the existing blocks (which is difficult since other parties usually have no interest and inherent incentive in your activity). This will make more sense when we go over consensus.
Cryptocurrenciesgraphy
Now all this sounds good, but since everyone has equal access to the blockchain, who decides what blocks are accepted into the blockchain and which ones are rejected? Despite anyone being able to write data to the blockchain, we don’t want a situation where Alice (an arbitrary user) adds a transaction where Bob (another arbitrary user) will send Alice $500 on the blockchain.
In the real world, we use a signature to indicate that we agree to the things listed in the document. Borrowing from this idea, we can use a digital signature to indicate that the sender has signed off on a particular message.
This relies on private/secret key and public key cryptography. I won’t be going into the weeds of how this works but the core idea is the private key is used to sign the message and the public key is used to verify who sent the message (the message in the context of the blockchain would be a message containing all the parameters of a transaction like to, from, amount, etc). Thus we have the following functions:
- Sign(data, private_key)
- Verify(data, signature, public_key) returns True or False if the public key was the sender or not
- We just check if the signed was indeed the public key of the sender
- Note: the signer here just refers to a way of cryptographically determining which public key could have possibly been associated with the given message and signature
The only way to crack this cryptography scheme is to check every single signature possible which is usually 256 bits. This means 2^256 tried since we just literally try every single combination of 0s and 1s possible. Due to the amount of computational work required and as a byproduct, the time it takes to fake a digital signature, this scheme is considered secure.
Also, the “data” parameter for signing and verifying can obviously be the message itself. However, the problem with only using the message is that one could just get the signature of a previously signed message and use that for a duplicate message from the same sender and it would work (ex. Bob could use the signature of a transaction where “Alice sends Bob $50” multiple times since the signature of all these transactions would be the same as the signer and message are same). Hence, we probably want to include another piece of information like a timestamp along with the message itself to create the “data” property to protect against another user just duplicating the same transactions from the original sender.
Can We Get Along?
The blockchain is a database that is decentralized and distributed as it isn't hosted or updated by a single individual or organization. Instead, everyone participating in the network stores, updates, and validates the database. Everyone in the network has an equal level of permissions as it relates to the database which includes reading and writing to the database in a standardized manner. Blockchains are commonly public, hence the network in that case is any computer that has access to the internet (known as permissionless blockchains). However, permissioned blockchains also exist where only designated people are part of the network (a level of decentralization is maintained by distributing the database amongst the designated participants).
An intuitive way of going about this would be to have everyone host the database which means everyone keeps a copy of the blockchain. This seems perfectly reasonable until you realize that any person can manipulate their copy of the blockchain by adding their own fraudulent blocks, which means that everyone’s copy of the blockchain may be different.
Like in any such system, the participants of the blockchain must reach some sort of consensus regarding what is the collectively agreed upon truth (what information to trust/is correct).
This is especially hard in a decentralized context since there shouldn’t be a centralized figure that governs the blockchain which can claim that their copy of the blockchain is the correct one. Instead, each blockchain uses a built-in consensus mechanism that allows the respective blockchain to be governed programmatically rather than by any single entity.
The most popular form of consensus mechanism in the blockchain is called Proof of Work (PoW) with the core idea being that the network trusts whichever copy of the blockchain has the computational effort put into it. How do we do this? By relying once again on cryptography. In fact, a core theme of the blockchain is effectively replacing a “middleman” or “governing entity” with cryptography.
Hashing It Up
I think it would be useful to first dive into the cryptography aspect before trying to connect its pieces with the blockchain. The important idea here is a cryptographic hashing function is used to convert some input data into an output of predictable size. Usually, the output of a hash is a string representing a hexadecimal number (padded with leading 0s to make it of a fixed size) which is commonly known as a hex-string. The catch is that the output produced by a given input for such a hash function is random meaning that a change in a single character of the input would drastically change the output in an unpredictable way. This means that given a hash, it is almost impossible to figure out the input that generated that hash. The only way to go about figuring out the input is to literally calculate the hash of every possible input until you get the desired hash.
For example, if we use the SHA-256 hashing algorithm (a common standard used on the web for encryption) for sample input, you can see the results for yourself:
Hello World
->a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e
Hello World!
->7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069
From SHA-256 hash calculator | Xorbin
However, given the input, it is super easy to verify the hash since we can just apply the hash function again which will produce the same output hash.
Putting In That Work
Where PoW comes into the picture is how we use a hash function to roughly indicate the amount of computational work put into getting a certain hash. For example, if we say that in our blockchain, the hash of each block must start off with 32 zeroes then a user likely needs to go through 2^32 inputs to get the desired hash (roughly equaling 10 billion tries). Verifying that a user went through potentially billions of tries to try and get a desirable hash is easy as we can just get the input from the user and run the hashing function on the input and see for ourselves.
There is obviously a chance that some user generates a desirable hash in 10 tries but the chance of such a thing happening is astronomically low. The blockchain just has to play the numbers game.
The amount of leading 0s required for a valid block determines the difficulty level for a new block to be added to the blockchain as it effectively adds more parameters for the output which is effectively random.
The last piece of the puzzle is what constitutes the input that generates the hash for a block. As we have built up the blockchain thus far, we know that we must have at least a list of transactions and the hash of the previous block as the input data. However, if we were just to use these two pieces of data, we would be using the same input data every time and thus get the same block hash. Hence, we need a dynamic input property that we can change at every run when trying to get a desirable hash. We can use a positive integer for such a case which is referred to as the “nonce” value which stands for “number used only once”.
You may have heard the term “miner” which simply refers to a user who tries to add blocks to the blockchain by trying to “mine” the correct nonce by using beefy computers. As a reward for mining these blocks, the blockchain gets a miner’s tip, hence creating an incentive for miners to spend the necessary computational power required to mine blocks. Additionally, a person who wants to make a transaction can include a transaction fee which is a further incentive for a miner to include that transaction into a block which will then be added to the blockchain.
Since so much computational work is required to generate the hash for each block, it is hard to alter/tamper with previous blocks which contain older transactions since it would require one to recalculate the hash for all the subsequent blocks while the rest of the network is adding new blocks during this recalculating process. The person trying to recalculate the hashes for the previous blocks will just lose this race since it likely has less computational power than the rest of the network combined and it starts off behind the rest of the network since its recalculating old blocks on top of all the new ones that are pouring in. The network trusts the longest chain of blocks where each block has the necessary computational work put into it. Hence, a big threat in the blockchain is the 51% attack where if a single entity has greater than 50% of the computational resources of the entire network, then it can effectively control the entire blockchain.
Proof Of Stake
Another consensus mechanism that is slowly catching more steam is Proof of Stake or PoS. As opposed to PoW where miners try to scramble to obtain the most powerful computer(s) in order to crack the correct nonce value for a block, PoS relies on users of the network “staking” their own currencies as means of becoming a “validator” for adding blocks to the chain. What this means is that multiple users can offer up their own funds of the native currency to get the chance to verify the transactions within a block and add it to the blockchain. Hence, in PoS the users that add to the blockchain are called “validators” since they verify the transactions and this can be double-checked by the rest of the network.
But who decides this pool of validators? Well, it is randomly selected from all users in the network that have a balance of native currency that exceeds a particular threshold (some blockchains make it so that staking more currency gives you a higher chance of being selected). Keep in mind, that if a user is selected as a validator and incorrectly verifies a transaction (detected when it is cross-checked by the majority of the validators in the pool), they lose their staked currency.
One of the main reasons to keep an eye out for PoS is because of how it reduces the amount of collective computational power that goes into keeping the blockchain going. Since miners get incentives for getting to the nonce value the fastest, individuals will try to use as much computational power to get to the nonce as fast as possible since it is a “winner takes all”.
However, since in PoS the validator is randomly determined, it isn't incentivizing individual users to just keep getting beefier machines but instead actually owning more of the native currency and by extension, having a shared interest in the network (since they don’t want to lose their balance as well). It also helps that Ethereum (one of the biggest blockchain networks) is switching to PoS signaling may others will likely follow suit. As you guess though, if you own 51% of all the native currency in supply, you can effectively control the entire blockchain and allow fraudulent transactions.
The Network
The blockchain is a peer2peer (P2P) network meaning that the workload is distributed across the network. This is so that there isn’t just a single entity responsible for mining. The blockchain works through a network of “nodes”. A node just represents a machine that is connected to the network. A node can listen for transaction requests sent by other nodes and also broadcast blocks to other nodes.
If you are a miner, you want to listen for people making new transactions, add them to your block, verify the transactions using the digital signature and if they have enough balance, then you add that to a running list of transactions that will make up a new block. Once you have hit a certain threshold, you will package up all the transactions and attempt to calculate the nonce value which gives you a desirable hash for the block. Once you obtain that, you will broadcast this new block to the rest of the network.
However, some nodes on the network probably aren’t interested in mining and thus have no interest in listening for the individual transactions emitted by other nodes in the network. Instead, they just want to listen for new blocks so that they can keep their local copy of the blockchain up to date. Also, as you can imagine some nodes may not be interested in keeping a full copy of the blockchain as it requires a lot of storage capacity (for context, the Ethereum blockchain is about 800 GB and continually growing). For this reason, there are different types of nodes in the network to best suit the needs of the user.
Another key idea is that there is a transaction pool (also known as mempool) where transactions accumulate once a miner has validated the individual transaction (which doesn't take much computational power) but has not been mined/included in a block (which takes up a lot of computational power). To be added to the mempool, the transaction must be checked for validity in terms of the sender by checking the digital signature and also when determining whether or not the sender actually has enough balance to make the transaction in terms of both, the value of the transaction and the mining fee.
The way data is communicated between nodes is also interesting due to the P2P nature of the network. Since there is no central entity, each node can emit a message. Other nodes that are subscribed to that particular node, known as neighboring nodes, will get their message. Once they get the message, they will broadcast it to their neighbor nodes. This is how data travels from one node to another in a decentralized manner.
This also means that you can choose to not subscribe to particular nodes. For example, if you emit an invalid transaction, your neighboring nodes will pick up your transaction and validate it, if it is invalid then it will just send you a message stating that the transaction is valid. However, if you keep only emitting invalid transactions, your neighboring nodes may unsubscribe to transactions emitted by you and block you off. You are still technically part of the network but no node is connected to you so any transactions you emit won't be propagated to other nodes making it completely useless to do.
The Protocol
Before getting into making our own blockchain, I think it would be good to recap and clarify what exactly is going to constitute our particular blockchain protocol, which we will call FakeCoin (FC):
- Each transaction is signed by a user’s private key
- A user is not allowed to spend more than they have
- Since we have no way of settling debts in our system, this is essential
- We can check this by using the verify function to see who sent the transaction and then go through all previous transactions in the ledge to check their current balance
- Each block will contain only 2 transactions
- A transaction can only be a transfer of value from a user to another
- We will be using proof work for the consensus protocol. The proof of work “difficulty” will be that the block hash must begin with 4 zeroes
- The network can trust the longest chain of blocks.
- There will be no optional miner tip for each transaction
- There will be a default block reward of 10 FC.
Let’s Get Building
Finally, the fun part - building our own blockchain in Python! One big missing from the code implementation is that we will not make it an entire network, but just a one-node blockchain. Keep in mind that doing this makes it so that our code inherently doesn’t take advantage of any decentralization features since there is no PoW needed as there is no consensus to be reached as your copy of the blockchain will be the only one. I will likely cover this in a future article since having a P2P network as is the case in a blockchain is fascinating. We will be making Python classes, so if you aren’t familiar with the concept of classes in general or classes in Python, I would recommend going over that first since that would make the code a lot more intuitive.
Signing and Verifying Transactions
One of the key building blocks of our blockchain is transactions as they represent changes in the state of the blockchain. We can first create the transaction using the basic properties of from
, to
, and amount
. The next step would be to sign the transaction by first getting the sha256 hash of the data (including the time) and then using the rsa.sign()
method. As for verifying the transactions, we once again use the RSA library with the rsa.verify()
method.
import rsa from hashlib import sha256 import json from datetime import datetime class Transaction: def __init__(self, from_account, to_account, amount): # create initial data object and store time self.data = {"from": from_account, "to": to_account, "amount": amount} self.time = str(int(datetime.utcnow().timestamp())) self.signature = None # signature remains None until it is signed def sign_transaction(self, privateKey): # we need to add the time to the transaction object txn_object = self.data txn_object["time"] = self.time txn_string = json.dumps(txn_object, sort_keys=True) # convert object into string (easier to get bytes) raw_hash_hex = sha256(txn_string.encode()).hexdigest() # hash the object string padded_hex = f"{int(raw_hash_hex, 16):#0{66}x}" shortened_hex = padded_hex[:34] # shorten the hash since its too much data # use RSA sign and indicate that data was hashed using SHA-256 raw_signature = rsa.sign(bytes(shortened_hex.encode()), privateKey, "SHA-256").hex() clean_signature = f"{int(raw_signature, 16):#0{130}x}" # pad the hex string to 130 ('0x' + 128 hex characters) self.signature = clean_signature def verify_transaction(self, publicKey): try: # repreat the same process for getting message as used in signing txn_object = self.data txn_object["time"] = self.time txn_string = json.dumps(txn_object, sort_keys=True) raw_hash_hex = sha256(txn_string.encode()).hexdigest() padded_hex = f"{int(raw_hash_hex, 16):#0{66}x}" shortened_hex = padded_hex[:34] # use in-built RSA verify method on the message/signature rsa.verify(bytes(shortened_hex.encode()), bytes.fromhex(self.signature[2:]), publicKey) # the function raises an exception if the publicKey doesnt match # if no exception, return True return True except: # if exception is raised by rsa.verify(), we return False return False
Additionally, we have to create a new class for a block reward transaction since it has no from
, time
or signature
property. This will only have a to
property and amount
to show a gain in the miner’s balance:
class BlockReward: def __init__(self, to_account, amount): self.data = {"to": to_account, "amount": amount} # there is no 'from' since the reward is from the network itself # there is also no "signature" since there is no sender
Defining A Block
A block contains: a list of transactions, the previous block’s hash, a timestamp and a nonce (PoW value). Hence, we can create a function to create a block, whereby we would return the hash of the block given all the required inputs:
def create_block_hash(transactions, previous_hash, timestamp, nonce): # we want to convert json object into bytes transaction_strings = [] # we start with empty array for transaction in transactions: # then we convert json object to string and get its bytes transaction_strings += json.dumps(transaction.__dict__, sort_keys=True).encode('utf-8') # add up all the bytes and then get the SHA-256 hash raw_hash_hex = sha256(bytes(previous_hash.encode('utf-8')) + bytes(nonce) + bytes(transaction_strings) + bytes(timestamp.encode('utf-8'))).hexdigest() # just get the hex number and convert to int and then convert it back to hex string with padding to make the string always have a length of 66 # this means '0x' + 64 hex characters decimal_hash = int(raw_hash_hex, 16) padded_hex = f"{decimal_hash:#0{66}x}" return padded_hex
Additionally, we have a simple block class:
class Block: def __init__(self, previous_block_hash, transactions): self.previous_block_hash = previous_block_hash self.transactions = transactions self.timestamp = str(int(datetime.utcnow().timestamp())) # get current UTC datetime and convert it into string int
Adding a Block
Although we have defined a block, we need to add the Proof of Work aspect and general details. As for the nonce value, we can simply just start at 0 and run a loop until the hash begins with 4 zeroes. We can also use the inbuilt datetime library to get the current time (in UTC for standardization) and convert that into an integer string.
We can use another helper function to just loop over nonce values until we get a valid hash:
def calculate_nonce(transactions, previous_hash, timestamp): nonce = 0 block_hash = create_block_hash(transactions, previous_hash, timestamp, nonce) while(block_hash[2:6] != "0000"): nonce += 1 block_hash = create_block_hash(transactions, previous_hash, timestamp, nonce) return nonce
The last missing part is the block reward for the miner. We can simply pass in the miner’s account and add the block reward as the first transaction in the block. This gives us our completed block class:
from datetime import datetime class Block: def __init__(self, previous_block_hash, transactions): self.previous_block_hash = previous_block_hash self.transactions = transactions self.timestamp = str(int(datetime.utcnow().timestamp())) # get current UTC datetime and convert it into string int # calculates the blockhash by using helper functions def mine_block(self,miner_public_address): # add BlockReward as first "transaction" self.transactions = [BlockReward(miner_public_address, "10")] + self.transactions self.nonce = calculate_nonce(self.transactions, self.previous_block_hash, self.timestamp) self.block_hash = create_block_hash(self.transactions, self.previous_block_hash, self.timestamp, self.nonce)
Chaining it All Together
Now we can piece everything together in a blockchain class which just keeps track of two arrays: pending_transactions
and blocks
. One of the core aspects of the class would be to emit new transactions to the blockchain:
def emit_new_transaction(self, transaction): self.pending_transactions.append(transaction)
And a function to add a new block to the blockchain:
def new_block(self, miner_public_address): # if this is the first block, we let previous has be just 0x000...00 if len(self.blocks) == 0: previous_block_hash = f"{0:#0{66}x}" else: previous_block_hash = self.blocks[-1].block_hash # create new block and mine it new_block = Block(previous_block_hash, self.pending_transactions) new_block.mine_block(miner_public_address) # once it is mined, we add the block to the list of confirmed blocks and clear out pending transactions self.blocks.append(new_block) self.pending_transactions = []
Lastly, we need to implement a function to get the current balance for an account:
def get_current_balance(self, public_address): # set initial balance at 0 since it is the default balance balance = 0 # iterate through each transaction in each block for block in self.blocks: for transaction in block.transactions: txn_data = transaction.data # if public address is 'to', then we increment the amount if 'to' in txn_data and public_address == txn_data['to']: balance += int(txn_data['amount']) # if public address is in 'from', then we decrement the amount elif 'from' in txn_data and public_address == txn_data['from']: balance -= int(txn_data['amount']) return balance
With this functionality, we can improve our transaction emit function to add some checks before adding a transaction to our “mempool” or pending transaction list.
def emit_new_transaction(self, transaction): # first we check if the sender has enough balance to make the transactions current_balance = self.get_current_balance(transaction.__dict__['data']['from']) if int(transaction.__dict__['data']['amount']) > current_balance: raise Exception("Not enough balance to make transaction") # We max out the amount of pending transactions at 2 if len(self.pending_transactions) >= 2: raise Exception("Too many transactions for one block. Block limit is 2 so please mine a block before ") # if all checks pass, we add the transaction to pending transaction list self.pending_transactions.append(transaction)
This completes our blockchain class:
from block import Block class Blockchain: def __init__(self): self.blocks = [] self.pending_transactions = [] def get_current_balance(self, public_address): # set initial balance at 0 since it is the default balance balance = 0 # iterate through each transaction in each block for block in self.blocks: for transaction in block.transactions: txn_data = transaction.data # if public address is 'to', then we increment the amount if 'to' in txn_data and public_address == txn_data['to']: balance += int(txn_data['amount']) # if public address is in 'from', then we decrement the amount elif 'from' in txn_data and public_address == txn_data['from']: balance -= int(txn_data['amount']) return balance def emit_new_transaction(self, transaction): # first we check if the sender has enough balance to make the transactions current_balance = self.get_current_balance(transaction.__dict__['data']['from']) if int(transaction.__dict__['data']['amount']) > current_balance: raise Exception("Not enough balance to make transaction") # We max out the amount of pending transactions at 2 if len(self.pending_transactions) >= 2: raise Exception("Too many transactions for one block. Block limit is 2 so please mine a block before ") # if all checks pass, we add the transaction to pending transaction list self.pending_transactions.append(transaction) def new_block(self, miner_public_address): # if this is the first block, we let previous has be just 0x000...00 if len(self.blocks) == 0: previous_block_hash = f"{0:#0{66}x}" else: previous_block_hash = self.blocks[-1].block_hash # create new block and mine it new_block = Block(previous_block_hash, self.pending_transactions) new_block.mine_block(miner_public_address) # once it is mined, we add the block to the list of confirmed blocks and clear out pending transactions self.blocks.append(new_block) self.pending_transactions = []
Accounts
The last part is just figuring out how to create accounts that will interact within the blockchain. For this, we will use a RSA public key, private key as we used RSA to implement the signing and verifying logic for transactions. In addition to these keys given to us by RSA, we will also generate a public_address
for each account. This can be thought of as a unique username of every user on the network. We get this directly from the RSA public key by simply making it a hex-string.
import rsa def create_account(): rsaPublicKey, rsaPrivateKey = rsa.newkeys(512) # get random RSA key pair public_address = hex(rsaPublicKey.n) # convert public key to hex string return public_address, rsaPublicKey, rsaPrivateKey
Full Final Code
To avoid making this article even longer than it already is, I put all the code in the GitHub repository below.
Final Thoughts
By understanding more of what blockchain is and building one yourself, I think it becomes abundantly clear that using blockchain doesn't make sense everywhere. For example, if you had a network where you trusted all the participants, then doing PoW is just a waste of time and computational power. Also, as you can imagine, the storage size of the blockchain grows rapidly since everyone stores all the blocks, and may become too much for some people looking to participate in the network.
Lastly, we only discussed simple transactions from Person A to B. But, that inherently limits what you can do with your blockchain. You may have heard of the term “smart contracts” which add a new layer of functionality to the blockchain by allowing you to execute code on the blockchain (popularized by Ethereum). As a developer, I think smart contracts are much more interesting and promising than the simple currency transaction blockchain that we just built. But covering that would be beyond the scope of this article and considering this is already a 30 min read, that is saying something :).