create account

Programming - Implementing a Blockchain using Python and Flask [Dev Blog 10] by drifter1

View this thread on: hive.blogpeakd.comecency.com
· @drifter1 · (edited)
$0.43
Programming - Implementing a Blockchain using Python and Flask [Dev Blog 10]
<img src="https://cdn.pixabay.com/photo/2017/12/14/14/23/blockchain-3019121_960_720.png">
<p>[<a href="https://pixabay.com/illustrations/blockchain-block-chain-technology-3019121/">Image 1</a>]</p>
<h2>Introduction</h2>
<p>
Hey it's a me again <a href="https://peakd.com/@drifter1">drifter1</a>!
</p>
<p>
And with that, I'm basically mostly done with the project I had in mind!
Other topics such as the explorer, wallet, light nodes etc. will be topics that we will covered in a monthly basis.
As I will have to get into the thesis now, and I also don't want to leave the other series on the blog behind...
</p>
<p>
During the previous days, I got into the miner (finally) and also made the request bodies far smaller by introducing block headers instead of the complete block, and a transactions header instead of the transactions array.
Therefore, transactions are basically retrieved one-by-one now, and not as a complete package.
And instead of the complete block, the miner only posts the block header, and the full node then "reconstructs" the block using the local unconfirmed transactions.
</p>
<p>
There has been some cleanup as well, such as making a node update script and introducing more node settings to the common Node_Settings structure.
The miner also comes with it's own Miner_Settings structure, as it only has to update the nodes, and contacts full nodes for the rest.
Lastly, because of the miner, the testing.py script now simply acts as an example of posting a transaction.
</p>
<p>
So, without further ado, let's get more in-depth!
</p>
<hr>
<h2>GitHub Repository</h2>
<ul>
    <li><a href="https://github.com/drifter1/blockchain">https://github.com/drifter1/blockchain</a></li>
</ul>
<hr>
<h2>Transactions Header</h2>
<p>
The transactions header, is what is returned instead of the complete unconfirmed transactions array.
So, it basically has a transaction_count field and an array of transaction headers.
A transaction header is a stripped down version of a transaction with only the following fields:
<ul>
    <li>timestamp</li>
    <li>value</li>
    <li>fee</li>
    <li>hash</li>
</ul>
</p>
<p>
In JSON a transactions header looks like this:
<pre><code>{
    "transaction_count": transaction_count,
    "transaction_headers": [
        {
            "timestamp": timestamp1,
            "value": value1,
            "fee": fee1,
            "hash": hash1
        },
        {
            "timestamp": timestamp2,
            "value": value2,
            "fee": fee2,
            "hash": hash2
        },<br>
        ...<br>
    ]
}</code></pre>
</p>
<hr>
<h2>Block Header</h2>
<p>
A block header is a stripped-down version of a block, so it contains all the fields of the original block, except the transactions.
The transactions are replaced by transaction headers, leading to block headers of the form:
<pre><code>{
    "timestamp": timestamp,
    "height": height,
    "creator": creator,
    "reward": reward,
    "fees": fees,
    "nonce": nonce,
    "transaction_count": 1,
    "transaction_headers" : [
	{
            "timestamp" : timestamp1,
            "value" : value1,
            "fee" : fee1,
            "hash" : hash1,
	}
     ]
    "hash": hash,
    "prev_hash": prev_hash
}</code></pre>
</p>
<p>
So, when a full_node wants to reconstruct a block when validating and creating the block file, as well as when synchronizing with the network, it simply has to re-build the transactions and add them to the block header instead of the transaction headers.
For that purpose, the following function has been defined:
<pre><code>def json_block_header_and_transactions_to_block(json_block_header: dict, json_block_transactions: dict):
    return {
        "timestamp": json_block_header["timestamp"],
        "height": json_block_header["height"],
        "creator": json_block_header["creator"],
        "reward": json_block_header["reward"],
        "fees": json_block_header["fees"],
        "nonce": json_block_header["nonce"],
        "transaction_count": json_block_header["transaction_count"],
        "transactions": json_block_transactions,
        "hash": json_block_header["hash"],
        "prev_hash": json_block_header["prev_hash"]
    }</code></pre>
</p>
<hr>
<h2>Synchronizing Blocks</h2>
<p>
As we mentioned before, blocks and transactions need to be retrieved differently now, as headers are returned in their place.
For example, take a look at a portion of the full node synchronization code:
<pre><code># retrieve block header
json_block_header, status_code = general_retrieve_block_header(
    settings, json_node, height)<br>
transaction_count = json_block_header["transaction_count"]<br>
# retrieve transactions one-by-one
json_block_transactions = []
for tid in range(0, transaction_count):
    json_transaction, status_code = general_retrieve_block_transaction(
        settings, json_node, height, tid)<br>
    json_block_transactions.append(json_transaction)<br>
# construct block
json_block = json_block_header_and_transactions_to_block(
    json_block_header, json_block_transactions)<br>
# create block file
json.dump(obj=json_block, fp=open(
    settings.block_file_path + str(json_block["height"]) + ".json", "w"))
</code></pre>
</p>
<p>
First the block header is retrieved, and afterwards each transaction one-by-one.
The two structures are then "combined" in order to get the complete block, and that block is stored to a local file.
</p>
<p>
Of course, the local endpoint can no longer be called, as the unconfirmed transactions are now needed.
So, updating the utxo and updating the blockchain info, takes place exactly after the block has been created, and before retrieving the next one!
</p>
<p>
And after all blocks have been retrieved, all the unconfirmed transactions also have to be retrieved one-by-one, in order to finish synchronizing:
<pre><code># retrieve unconfirmed transactions header
json_transactions_header, status_code = general_retrieve_transactions_header(
    settings, json_node)<br>
transaction_count = json_transactions_header["transaction_count"]<br>
# retrieve unconfirmed transactions one-by-one
json_transactions = []
for tid in range(0, transaction_count):
    json_transaction, status_code = general_retrieve_transaction(
        settings, json_node, tid)<br>
    json_transactions.append(json_transaction)<br>
# update local file
json.dump(obj=json_transactions, fp=open(
    settings.transactions_path, "w"))</code></pre>
</p>
<hr>
<h2>Basic Miner </h2>
<p>
The miner retrieves a random known node (from a new endpoint for that purpose), and then start retrieving the necessary information for mining:
<pre><code># retrieve blockchain info
json_blockchain_info, status_code = general_retrieve_blockchain_info(
    settings, json_node)<br>
# retrieve unconfirmed transactions
json_transactions = retrieve_unconfirmed_transactions(
    settings, json_node)<br>
# retrieve last block header
json_last_block_header, status_code = general_retrieve_last_block_header(
    settings, json_node)</code></pre>
</p>
<p>
Depending on whether it's the first block or not (status code will be 400 if the last block doesn't exist) some of the headers can now be setup:
<pre><code># if not first block
if status_code == 200:
    block.height = json_last_block_header["height"] + 1
    block.prev_hash = json_last_block_header["hash"]
    block.reward = json_last_block_header["reward"]
else:
    block.reward = json_blockchain_info["block_reward"]<br>
# headers
block.creator = settings.reward_address
block.transaction_count = len(json_transactions) + 1
block.fees = 0
try:
    if len(json_transactions) >= 0:
        for json_transaction in json_transactions:
            block.fees += json_transaction["fee"]
except:
    pass</code></pre>
</p>
<p>
After that, the reward transaction is created using a function for that purpose, and the transactions structure is created:
<pre><code># reward transaction
reward_transaction = create_reward_transaction(
    settings, block.timestamp, block.reward, block.fees)<br>
# add remaining transactions
block.transactions = []
block.transactions.append(reward_transaction)
for json_transaction in json_transactions:
    transaction = json_destruct_transaction(json_transaction)
    block.transactions.append(transaction)</code></pre>
</p>
<p>
And then comes the solving part.
For now, the difficulty is static with 5 hexadecimal 0s (or twenty 0's in bit format).
Solving is pretty straight-forward:
<pre><code>while True:
for nonce in range(0, 1 &lt;&lt; 32):
    block.nonce = "{:08x}".format(nonce)<br>
    if (nonce != 0) and (nonce % (1 &lt;&lt; 20) == 0):
        print("Tried", nonce, "nonces...")<br>
    # calculate hash
    calculate_block_hash(block)<br>
    # check if smaller then target_hash
    if int(block.hash, 16) &lt; int(target_hash, 16):
        return</code></pre>
try various nonces until the hash is less then the target hash.
</p>
<p>
If no nonce in that range works, then the timestamp is reset, which also leads to a recalculation of the reward transaction hash:
<pre><code>block.timestamp = int(time.time())<br>
reward_transaction: Transaction = block.transactions[0]
reward_transaction.timestamp = block.timestamp
calculate_transaction_hash(reward_transaction)
block.transactions[0] = reward_transaction</code></pre>
and the previous procedure is repeated...
</p>
<p>
When the solution is found, then the json block and corresponding json block header is constructed and posted to all known full nodes:
<pre><code># construct JSON block
json_block = json_construct_block(block)<br>
# construct JSON block header
json_block_header = json_block_to_block_header(json_block)<br>
# send block to all known nodes
json_nodes, status_code = local_retrieve_nodes(settings)<br>
for json_node in json_nodes:
    json_ret, status_code = general_create_block(
        settings, json_node, json_block_header)</code></pre>
</p>
<hr>
<h2>RESOURCES:</h2>
<h3>References</h3>
<ol>
    <li><a href="https://www.python.org/">https://www.python.org/</a></li>
    <li><a href="https://flask.palletsprojects.com/en/2.0.x/">https://flask.palletsprojects.com/en/2.0.x/</a></li>
    <li><a href="https://docs.python-requests.org/">https://docs.python-requests.org/</a></li>
    <li><a href="https://pypi.org/project/mnemonic/0.20/">https://pypi.org/project/mnemonic/0.20/</a></li>
    <li><a href="https://pypi.org/project/ecdsa/">https://pypi.org/project/ecdsa/</a></li>
    <li><a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10">https://docs.microsoft.com/en-us/windows/wsl/install-win10</a></li>
    <li><a href="https://www.docker.com/">https://www.docker.com/</a></li>
    <li><a href="https://code.visualstudio.com/">https://code.visualstudio.com/</a></li>
    <li><a href="https://insomnia.rest/">https://insomnia.rest/</a></li>
</ol>
<h3>Images</h3>
<ol>
    <li><a href="https://pixabay.com/illustrations/blockchain-block-chain-technology-3019121/">https://pixabay.com/illustrations/blockchain-block-chain-technology-3019121/</a></li>
</ol>
The rest is screenshots or made using <a href="https://app.diagrams.net">draw.io</a>
<hr>
<h2>Previous dev blogs of the series</h2>
<ul>
    <li><a href="https://peakd.com/hive-169321/@drifter1/programming-implementing-a-blockchain-using-python-and-flask-dev-blog-1">Dev Blog 1</a></li>
    <li><a href="https://peakd.com/hive-169321/@drifter1/programming-implementing-a-blockchain-using-python-and-flask-dev-blog-2">Dev Blog 2</a></li>
    <li><a href="https://peakd.com/hive-169321/@drifter1/programming-implementing-a-blockchain-using-python-and-flask-dev-blog-3">Dev Blog 3</a></li>
    <li><a href="https://peakd.com/hive-169321/@drifter1/programming-implementing-a-blockchain-using-python-and-flask-dev-blog-4">Dev Blog 4</a></li>
    <li><a href="https://peakd.com/hive-169321/@drifter1/programming-implementing-a-blockchain-using-python-and-flask-dev-blog-5">Dev Blog 5</a></li>
    <li><a href="https://peakd.com/hive-169321/@drifter1/programming-implementing-a-blockchain-using-python-and-flask-dev-blog-6">Dev Blog 6</a></li>
    <li><a href="https://peakd.com/hive-169321/@drifter1/programming-implementing-a-blockchain-using-python-and-flask-dev-blog-7">Dev Blog 7</a></li>
    <li><a href="https://peakd.com/hive-169321/@drifter1/programming-implementing-a-blockchain-using-python-and-flask-dev-blog-8">Dev Blog 8</a></li>
    <li><a href="https://peakd.com/hive-169321/@drifter1/programming-implementing-a-blockchain-using-python-and-flask-dev-blog-9">Dev Blog 9</a></li>
</ul>
<hr>
<h2>Final words | Next up</h2>
<p>And this is actually it for today's post!</p>
<p>Next week, I will give you a complete example run of the "finished" project.
After that expect dev blogs about this project to come out more rarely!</p>
<p><img src="https://media.giphy.com/media/ybITzMzIyabIs/giphy.gif" width="500" height="333"/></p>
<p>Keep on drifting!</p>
👍  , , , , , , , , , , , , , , , , , , , , , , ,
properties (23)
authordrifter1
permlinkprogramming-implementing-a-blockchain-using-python-and-flask-dev-blog-10
categoryhive-169321
json_metadata"{"app":"peakd/2021.08.2","format":"markdown","description":"10! Introduction of a basic miner, and requests and responses have been replaced by headers (stripped-down versions)","tags":["programming","development","python","blockchain","flask","dev-blog","mining","miner","solving","headers"],"users":["drifter1"],"image":["https://cdn.pixabay.com/photo/2017/12/14/14/23/blockchain-3019121_960_720.png","https://media.giphy.com/media/ybITzMzIyabIs/giphy.gif"]}"
created2021-09-10 13:24:12
last_update2021-09-10 13:26:57
depth0
children1
last_payout2021-09-17 13:24:12
cashout_time1969-12-31 23:59:59
total_payout_value0.218 HBD
curator_payout_value0.213 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length12,862
author_reputation98,202,866,830,354
root_title"Programming - Implementing a Blockchain using Python and Flask [Dev Blog 10]"
beneficiaries
0.
accounthive-169321
weight200
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id106,168,323
net_rshares412,746,667,102
author_curate_reward""
vote details (24)
@gangstalking ·
Electronic-terrorism, voice to skull and neuro monitoring on Hive and Steem. You can ignore this, but your going to wish you didnt soon. This is happening whether you believe it or not. https://ecency.com/fyrstikken/@fairandbalanced/i-am-the-only-motherfucker-on-the-internet-pointing-to-a-direct-source-for-voice-to-skull-electronic-terrorism
👎  , ,
properties (23)
authorgangstalking
permlinkre-drifter1-programming-implementing-a-blockchain-using-python-and-flask-dev-blog-10-20210910t132702368z
categoryhive-169321
json_metadata{"app":"hive-bot/0.6.3"}
created2021-09-10 13:27:06
last_update2021-09-10 13:27:06
depth1
children0
last_payout2021-09-17 13:27:06
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length343
author_reputation-67,597,107,868,724
root_title"Programming - Implementing a Blockchain using Python and Flask [Dev Blog 10]"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id106,168,460
net_rshares-51,818,725,553
author_curate_reward""
vote details (3)