├── run_app.py
├── screenshots
└── blocknet-home.png
├── app
├── __init__.py
├── templates
│ ├── index.html
│ └── base.html
├── view.py
└── static
│ └── stylesheet.css
├── LICENSE
├── .gitignore
├── node_server.py
└── README.md
/run_app.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 |
4 | from app import app
5 |
6 | app.run(debug=True)
7 |
--------------------------------------------------------------------------------
/screenshots/blocknet-home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matthewmuccio/BlockNet/HEAD/screenshots/blocknet-home.png
--------------------------------------------------------------------------------
/app/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 |
4 | from flask import Flask
5 |
6 | app = Flask(__name__)
7 |
8 | from app import view
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Matthew Muccio
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 | {% extends "base.html" %}
3 |
4 | {% block content %}
5 |
19 |
20 | {% with messages = get_flashed_messages() %}
21 | {% if messages %}
22 |
23 | {% for message in messages %}
24 |
{{ message }}
25 | {% endfor %}
26 |
27 | {% endif %}
28 | {% endwith %}
29 | {% block content %}{% endblock %}
30 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/static/stylesheet.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css?family=Rubik");
2 | @import url("https://fonts.googleapis.com/css?family=Rubik+Mono+One");
3 |
4 | /* My styling. */
5 | html {
6 | font-size: 62.5%;
7 | height: 100%;
8 | }
9 |
10 | body {
11 | font-family: "Rubik", sans-serif;
12 | background-color: #ecf0f1;
13 | margin: 0;
14 | padding: 0;
15 | min-height: 100%;
16 | position: relative;
17 | }
18 |
19 | /* Header and navigation bar styling. */
20 | .navbar {
21 | margin: 0;
22 | padding: 0;
23 | overflow: hidden;
24 | background-color: #3498db;
25 | position: fixed;
26 | width: 100%;
27 | top: 0;
28 | left: 0;
29 | right: 0;
30 | }
31 |
32 | .navbar-item {
33 | display: block;
34 | color: #ffffff;
35 | text-align: center;
36 | padding: 2rem 2rem;
37 | text-decoration: none;
38 | font-size: 1.6rem;
39 | -webkit-transition-duration: 0.3s;
40 | transition-duration: 0.3s;
41 | }
42 |
43 | .navbar-item:hover {
44 | background-color: #2980b9;
45 | }
46 |
47 | .left {
48 | float: left;
49 | }
50 |
51 | .right {
52 | float: right;
53 | }
54 |
55 | .active-link {
56 | background-color: #2980b9;
57 | }
58 |
59 | /* Title, subtitle, and horizontal line. */
60 | .title {
61 | margin-top: 9rem;
62 | text-align: center;
63 | line-height: 1.6rem;
64 | }
65 |
66 | .title-text {
67 | font-family: "Rubik Mono One", sans-serif;
68 | font-size: 4rem;
69 | }
70 |
71 | .subtitle-text {
72 | font-size: 2.4rem;
73 | }
74 |
75 | .hr {
76 | border-style: solid;
77 | border-width: 0.05rem;
78 | border-color: #7f8c8d;
79 | width: 55rem;
80 | }
81 |
82 | /* Buttons, post text area, name input, and body styling. */
83 | .content {
84 | min-height: 100%;
85 | height: 100%;
86 | }
87 |
88 | .btn {
89 | font-family: "Rubik", sans-serif;
90 | font-size: 1.4rem;
91 | color: white;
92 | background-color: #3498db;
93 | padding: 1rem;
94 | border-radius: 0.4rem;
95 | border: none;
96 | -webkit-transition-duration: 0.3s;
97 | transition-duration: 0.3s;
98 | }
99 |
100 | .btn:hover {
101 | background-color: #2980b9;
102 | cursor: pointer;
103 | }
104 |
105 | .post-textarea, .name-input {
106 | font-family: "Rubik", sans-serif;
107 | font-size: 1.4rem;
108 | padding: 1rem;
109 | border-radius: 0.4rem;
110 | border-width: 0.1rem;
111 | border-style: solid;
112 | border-color: #7f8c8d;
113 | }
114 |
115 | .posts {
116 | margin: 2rem 2rem 14rem 2rem;
117 | }
118 |
119 | /* Footer styling. */
120 | .footer {
121 | height: 12rem;
122 | line-height: 1rem;
123 | overflow: hidden;
124 | background-color: #3498db;
125 | width: 100%;
126 | padding: 0;
127 | margin: 0;
128 | position: fixed;
129 | left: 0;
130 | right: 0;
131 | bottom: 0;
132 | }
133 |
134 | .footer-item {
135 | display: block;
136 | margin-top: 3rem;
137 | color: #ffffff;
138 | text-align: center;
139 | text-decoration: none;
140 | font-size: 2rem;
141 | }
142 |
143 | .footer-item a {
144 | text-decoration: none;
145 | color: #ffffff;
146 | -webkit-transition-duration: 0.3s;
147 | transition-duration: 0.3s;
148 | }
149 |
150 | .footer-item a:hover {
151 | color: #2980b9;
152 | }
153 |
154 | /* Font Awesome social media icons (on footer) styling. */
155 | .social-icon-list {
156 | list-style-type: none;
157 | text-align: center;
158 | margin-right: 3.5rem;
159 | }
160 |
161 | .social-icon {
162 | font-size: 3rem;
163 | display: inline;
164 | margin: 0 1rem 0 1rem;
165 | text-decoration: none;
166 | }
167 |
168 | .white-icon {
169 | color: #ffffff;
170 | -webkit-transition-duration: 0.3s;
171 | transition-duration: 0.3s;
172 | }
173 |
174 | .white-icon:hover {
175 | color: #2980b9;
176 | }
177 |
178 | /* Post boxes styling */
179 | .post_box {
180 | background: #fff;
181 | padding: 1.2rem 0 0 1.2rem;
182 | margin-top: 0;
183 | margin-bottom: 0.8rem;
184 | border: 0.1rem solid #7f8c8d;
185 | }
186 |
187 | .post_box-header {
188 | padding-bottom: 1.2rem;
189 | font-size: 1.4rem;
190 | }
191 |
192 | .post_box-avatar {
193 | width: 3.8rem;
194 | height: 3.8rem;
195 | border-radius: 50%;
196 | display: flex;
197 | justify-content: center;
198 | align-items: center;
199 | color: white;
200 | font-size: 2.2rem;
201 | float: left;
202 | margin-right: 1.6rem;
203 | border: 0.1rem solid #fff;
204 | box-shadow: 0 0 0 0.2rem #f00;
205 | }
206 |
207 | .post_box-avatar::after {
208 | content:"";
209 | display:block;
210 | }
211 |
212 | .post_box-name {
213 | font-weight: bold;
214 | }
215 |
216 | .post_box-subtitle {
217 | color: #777;
218 | }
219 |
220 | .post_box-body {
221 | margin-top: 1.6rem;
222 | margin-bottom: 0.8rem;
223 | font-size: 1.4rem;
224 | }
225 |
226 | .post_box-options {
227 | float: right;
228 | }
229 |
230 | .option-btn {
231 | background: #f8f8f8;
232 | border: none;
233 | color: #2c3e50;
234 | padding: 0.7rem;
235 | cursor: pointer;
236 | font-size: 1.4rem;
237 | margin-left: 0.2rem;
238 | margin-right: 0.2rem;
239 | outline: none;
240 | height: 4.2rem;
241 | }
242 |
--------------------------------------------------------------------------------
/node_server.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 |
4 | from hashlib import sha512
5 | import json
6 | import time
7 |
8 | from flask import Flask, request
9 | import requests
10 |
11 |
12 | # A class that represents a Block, which stores one or more pieces of data, in the immutable Blockchain.
13 | class Block:
14 | # One or more pieces of data (author, content, and timestamp) will be stored in a block.
15 | # The blocks containing the data are generated frequently and added to the blockchain, each with a unique ID.
16 | def __init__(self, index, transactions, timestamp, previous_hash):
17 | self.index = index
18 | self.transactions = transactions
19 | self.timestamp = timestamp
20 | self.previous_hash = previous_hash
21 | self.nonce = 0
22 |
23 | # A function that creates the hash of the block contents.
24 | def compute_hash(self):
25 | block_string = json.dumps(self.__dict__, sort_keys=True)
26 | return sha512(block_string.encode()).hexdigest()
27 | # End of Block class.
28 |
29 |
30 | # A class that represents an immutable list of Block objects that are chained together by hashes, a Blockchain.
31 | class Blockchain:
32 | # Difficulty of PoW algorithm.
33 | difficulty = 2
34 | # One or more blocks will be stored and chained together on the Blockchain, starting with the genesis block.
35 | def __init__(self):
36 | self.unconfirmed_transactions = [] # Pieces of data that are not yet added to the Blockchain.
37 | self.chain = [] # The immutable list that represents the actual Blockchain.
38 | self.create_genesis_block()
39 |
40 | # Generates genesis block and appends it to the Blockchain.
41 | # The Block has index 0, previous_hash of 0, and a valid hash.
42 | def create_genesis_block(self):
43 | genesis_block = Block(0, [], time.time(), "0")
44 | genesis_block.hash = genesis_block.compute_hash()
45 | self.chain.append(genesis_block)
46 |
47 | # Verifies the block can be added to the chain, adds it, and returns True or False.
48 | def add_block(self, block, proof):
49 | previous_hash = self.last_block.hash
50 | # Verifies that the previous_hash field of block to be added points to the hash of the latest block,
51 | # and that the PoW that is provided is correct.
52 | if (previous_hash != block.previous_hash or not self.is_valid_proof(block, proof)):
53 | return False
54 | # Adds new block to the chain after verification.
55 | block.hash = proof
56 | self.chain.append(block)
57 | return True
58 |
59 | # Serves as an interface to add the transactions to the blockchain by adding them
60 | # and then figuring out the PoW.
61 | def mine(self):
62 | # If unconfirmed_transactions is empty, no mining to be done.
63 | if not self.unconfirmed_transactions:
64 | return False
65 | last_block = self.last_block
66 | # Creates a new block to be added to the chain.
67 | new_block = Block(last_block.index + 1, \
68 | self.unconfirmed_transactions, \
69 | time.time(), \
70 | last_block.hash)
71 | # Running PoW algorithm to obtain valid hash and consensus.
72 | proof = self.proof_of_work(new_block)
73 | # Verifies block can be added to the chain (previous hash matches, and PoW is valid), and adds it.
74 | self.add_block(new_block, proof)
75 | # Empties the list of unconfirmed transactions since they are now added to the blockchain.
76 | self.unconfirmed_transactions = []
77 | # Announces to the network once a block has been mined, other blocks can simply verify the PoW and add it to their respective chains.
78 | announce_new_block(new_block)
79 | # Returns the index of the block that was just added to the chain.
80 | return new_block.index
81 |
82 | # Proof of work algorithm that tries different values of nonce in order to get a hash
83 | # that satisfies the difficulty criteria.
84 | # Important to note that there is no definite logic to figure out the nonce quickly, simply brute force.
85 | def proof_of_work(self, block):
86 | block.nonce = 0
87 | computed_hash = block.compute_hash()
88 | while not computed_hash.startswith("0" * Blockchain.difficulty):
89 | block.nonce += 1
90 | computed_hash = block.compute_hash()
91 | return computed_hash
92 |
93 | # Adds a new transaction the list of unconfirmed transactions (not yet in the blockchain).
94 | def add_new_transaction(self, transaction):
95 | self.unconfirmed_transactions.append(transaction)
96 |
97 | # Checks if the chain is valid at the current time.
98 | @classmethod
99 | def check_chain_validity(cls, chain):
100 | result = True
101 | previous_hash = "0"
102 | for block in chain:
103 | block_hash = block.hash
104 | # Removes the hash attribute to recompute the hash again using compute_hash.
105 | delattr(block, "hash")
106 | if not cls.is_valid_proof(block, block.hash) or previous_hash != block.previous_hash:
107 | result = False
108 | break
109 | block.hash = block_hash
110 | previous_hash = block_hash
111 | return result
112 |
113 | # Checks if block_hash is a valid hash of the given block, and if it satisfies the difficulty criteria.
114 | @classmethod
115 | def is_valid_proof(cls, block, block_hash):
116 | return (block_hash.startswith("0" * Blockchain.difficulty) and block_hash == block.compute_hash())
117 |
118 | # Returns the current last Block in the Blockchain.
119 | @property
120 | def last_block(self):
121 | return self.chain[-1]
122 | # End of Blockchain class.
123 |
124 |
125 | # Flask web application
126 | # Creates a new Flask web app.
127 | app = Flask(__name__)
128 | # The node's copy of the blockchain.
129 | blockchain = Blockchain()
130 | # A set that stores the addresses to other participating members of the network.
131 | peers = set()
132 |
133 | # Creates a new endpoint, and binds the function to the URL.
134 | @app.route("/new_transaction", methods=["POST"])
135 | # Submits a new transaction, which adds new data to the blockchain.
136 | def new_transaction():
137 | tx_data = request.get_json()
138 | required_fields = ["author", "content"]
139 | for field in required_fields:
140 | if not tx_data.get(field):
141 | return "Invalid transaction data", 404
142 | tx_data["timestamp"] = time.time()
143 | blockchain.add_new_transaction(tx_data)
144 | return "Success", 201
145 |
146 | # Creates a new endpoint, and binds the function to the URL.
147 | @app.route("/chain", methods=["GET"])
148 | # Returns the node's copy of the blockchain in JSON format (to display all confirmed transactions/posts).
149 | def get_chain():
150 | # Ensures that the user's chain is the current (longest) chain.
151 | consensus()
152 | chain_data = []
153 | for block in blockchain.chain:
154 | chain_data.append(block.__dict__)
155 | return json.dumps({"length" : len(chain_data), "chain" : chain_data})
156 |
157 | # Creates a new endpoint, and binds the function to the URL.
158 | @app.route("/mine", methods=["GET"])
159 | # Requests the node to mine the unconfirmed transactions (if any).
160 | def mine_unconfirmed_transactions():
161 | result = blockchain.mine()
162 | if not result:
163 | return "There are no transactions to mine."
164 | return "Block #{0} has been mined.".format(result)
165 |
166 | # Creates a new endpoint, and binds the function to the URL.
167 | @app.route("/add_nodes", methods=["POST"])
168 | # Adds new peers to the network.
169 | def register_new_peers():
170 | nodes = request.get_json()
171 | if not nodes:
172 | return "Invalid data", 400
173 | for node in nodes:
174 | peers.add(node)
175 | return "Success", 201
176 |
177 | # Creates a new endpoint, and binds the function to the URL.
178 | @app.route("/pending_tx")
179 | # Queries unconfirmed transactions.
180 | def get_pending_tx():
181 | return json.dumps(blockchain.unconfirmed_transactions)
182 |
183 | # A simple algorithm to achieve consensus to maintain the integrity of the system.
184 | # If a longer valid chain is found, the chain is replaced with it, and returns True, otherwise nothing happens and returns False.
185 | def consensus():
186 | global blockchain
187 | longest_chain = None
188 | curr_len = len(blockchain.chain)
189 | # Achieve consensus by checking the JSON fields of every node in the network.
190 | for node in peers:
191 | response = requests.get("http://{0}/chain".format(node))
192 | length = response.json()["length"]
193 | chain = response.json()["chain"]
194 | if length > curr_len and blockchain.check_chain_validity(chain):
195 | curr_len = length
196 | longest_chain = chain
197 | if longest_chain:
198 | blockchain = longest_chain
199 | return True
200 | return False
201 |
202 | # Creates a new endpoint, and binds the function to the URL.
203 | @app.route("/add_block", methods=["POST"])
204 | # Adds a block mined by a user to the node's chain.
205 | def validate_and_add_block():
206 | block_data = request.get_json()
207 | block = Block(block_data["index"], \
208 | block_data["transactions"], \
209 | block_data["timestamp", block_data["previous_hash"]])
210 | proof = block_data["hash"]
211 | added = blockchain.add_block(block, proof)
212 | if not added:
213 | return "The block was discarded by the node.", 400
214 | return "The block was added to the chain.", 201
215 |
216 | # Announces to the network once a block has been mined, should always be called after validate_and_add_block().
217 | # Other blocks can simply verify the PoW and add it to their respective chains.
218 | def announce_new_block(block):
219 | for peer in peers:
220 | url = "http://{0}/add_block".format(peer)
221 | requests.post(url, data=json.dumps(block.__dict__, sort_keys=True))
222 |
223 | # Runs the Flask web app.
224 | app.run(port=8000, debug=True)
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | BlockNet
2 | ===================
3 | A simple, fully-functional, decentralized content sharing web application, which implements a public blockchain from scratch, and the Flask web framework with Jinja2 templating.
4 |
5 | 
6 |
7 | Project
8 | -------
9 | - Objective: to build a simple web application using a public blockchain that allows users to share information over a decentralized network.
10 | - Because the content will be stored on the blockchain, it is immutable and permanent (more below).
11 | - An explicit definition of the structure of the data (posts) that will be stored on the blockchain:
12 | - A post is a message posted by any user on the web application, it must have three properties: content, author, and timestamp.
13 |
14 | Process
15 | -------
16 | - Using the Flask web microframework, create endpoints for different functions of the blockchain, such as adding a transaction.
17 | - Then, run the scripts on multiple machines in order to create a decentralized network.
18 | - Build a simple UI with Flask and Jinja2 templating that interacts with the blockchain and stores information for any use case.
19 | - For instance, content sharing, P2P payments, chatting, or e-commerce.
20 |
21 | Background
22 | ----------
23 | Public blockchain
24 | - A public blockchain network is completely decentralized and open to the public.
25 | - No one entity has control over the network and they are secure in that data cannot be changed once validated on the blockchain.
26 | - Anyone can join and participate.
27 | - Examples: Bitcoin, Ethereum
28 |
29 | Private blockchain
30 | - A private blockchain network is primarily used by businesses who need greater privacy, security, and speed of transactions.
31 | - Participants need an invitation to join.
32 | - They operate quite similarly to public blockchains but have access controls that limit who can participate in the network.
33 | - It operates like modern centralized database systems that restrict access to certain users.
34 | - One or more entities control the network.
35 | - Causes users to still have to rely on third-parties to transact.
36 | - Example: Ripple, Hyperledger
37 |
38 | Brief History of Bitcoin
39 | - In 2008, a whitepaper was released by an individual or group under the identifier Satoshi Nakamoto.
40 | - Titled "Bitcoin: A Peer-to-Peer Electronic Cash System."
41 | - The paper combined cryptographic techniques and a peer-to-peer network without the need to trust a centralized authority to make payments from one person to another.
42 | - It also introduced a distributed system of storing data (blockchain).
43 | - We all now know this concept has far wider applicability than just payments, or cryptocurrencies.
44 | - Blockchain technology has exploded across nearly every industry.
45 | - It is now the underlying technology behind:
46 | - Fully digital cryptocurrencies (i.e., Bitcoin)
47 | - Distributed computing technologies (i.e., Ethereum)
48 | - Open-source frameworks (i.e., Hyperledger Fabric)
49 |
50 | Blockchain Basics
51 | -----------------
52 | Blockchain Technology
53 | - In simplest terms, blockchain is a mechanism for storing digital data.
54 | - The data can literally be anything.
55 | - The data can even be files, it doesn't matter.
56 | - In the case of Bitcoin, it is the transactions (transfers of Bitcoin from one account to another).
57 | - The data is stored in the form of blocks, which are chained together using hashes.
58 | - Storing data in BLOCKs + using hashes to CHAIN them together = blockchain
59 |
60 | Characteristics of Blockchain Networks
61 | - All of the "magic" in blockchain comes from the way this data is added and stored in the blockchain.
62 | - This yields some highly desirable and powerful characteristics:
63 | - Immutability of history
64 | - Un-hackability of the system
65 | - Persistence of the data
66 | - No single point of failure
67 |
68 | Development
69 | -----------
70 | 1. Store transactions into blocks
71 | - I will be storing the data in JSON, a widely-used format.
72 | - The generic term "data" is often used interchangeably with the term "transactions" on the Internet.
73 | - The transactions in the application are packed into blocks.
74 | - A block can contain one or many transactions.
75 | - The blocks containing the transactions are generated frequently and added to the blockchain.
76 | - Each block will have a unique ID, since there can be multiple blocks.
77 |
78 | 2. Make the blocks immutable
79 | - I want to detect any kind of tampering in the data stored inside the block.
80 | - In blockchain technology, this is accomplished using a hash function.
81 | - It is a function that takes data of any size and produces data of a fixed sizse from it, which generally works to identify the input.
82 | - The Python Standard Library has a hashlib library with a SHA-256 and SHA-512 hashing function.
83 | - The characteristics of an ideal hash function are:
84 | - It should be computationally easy to compute.
85 | - Even a single bit change in data should make the hash change altogether.
86 | - It should not be possible to guess the input from the output hash.
87 | - I will store the hash of every block in a field inside a Block object to act like a digital fingerprint of data contained in it.
88 | - Note: In most cryptocurrencies, the individual transactions in the block are also hashed, to form a hash tree, and the root of the tree might be used as the hash of the block.
89 | - However, it is not a necessary requirement for the functioning of the blockchain.
90 |
91 | 3. Chain the blocks
92 | - The blocks themselves are now set up.
93 | - The blockchain is a collection of blocks, and I must implement it accordingly.
94 | - I could store all of the blocks in a list (array) in Python, but it would not work.
95 | - It is not sufficient.
96 | - Someone could intentionally replace a block at a previous index in the collection/list.
97 | - In the current (unfinished) implementation, creating a new block with altered transactions, computing the hash, and replacing it with any older block works and it should not.
98 | - I must maintain the immutability and order of the blocks in some way.
99 | - I need a way to ensure that any change in the past blocks invalidates the entire chain.
100 | - One way to do this is to chain the blocks by the hash.
101 | - By chaining, I mean to include the hash of the previous block in the current block.
102 | - If the content of any of the previous blocks change, the hash of the block would change, which would lead to a mismatch with the previous_hash field in the next block.
103 | - If every block will be linked to the previous block by the previous_hash field, I must manually generate the very first block ourselves.
104 | - The very first block is called the genesis block, and it is generated manually or by some unique logic, in most cases.
105 |
106 | 4. Implementing a proof of work algorithm
107 | - Selective endorsement vs. proof of work
108 | - Consensus in a (private) blockchain for business is not achieved through mining, but through a process called selective endorsement.
109 | - The network members control exactly who verifies transactions, much in the same way that business happens today.
110 | - A problem arises: if I change the previous block, I can re-compute the hashes of all the following blocks quite easily and create a different valid blockchain.
111 | - To prevent this, I must make the task of calculating the hash difficult and random.
112 | - Instead of accepting any hash for the block, I will add some constraint to it.
113 | - Let's add a constrant that the hash should start with a certain number of leading zeroes.
114 | - I also know that unless I change the contents of the block, the hash will not change.
115 | - I will introduce a new field in the Block, a nonce.
116 | - A nonce is a number that will continue to change until there is a hash that satisifes the constraint.
117 | - The number of leading zeroes, which will default to 2, decides the difficulty of the PoW algorithm.
118 | - This PoW algorithm is difficult to compute but easy to verify once I figure out the nonce.
119 | - Verifying will just involve running the hash function again.
120 |
121 | 5. Adding blocks to the chain, and mining
122 | - In order to add blocks to the chain, I must first verify two components:
123 | - The PoW that is provided is correct.
124 | - The previous_hash field of the block to be added points to the hash of the latest block in the chain.
125 | - At this point, I must implement a mechanism for mining the blocks.
126 | - The transactions are initially stored in a pool of unconfirmed transactions.
127 | - The process of putting the unconfirmed transactions in a block and computing PoW is known as mining the blocks.
128 | - Once the nonce satisfying the constraints is figured out, I can say that a block has been mined.
129 | - At that point, the block is put into the blockchain.
130 | - In most cryptocurrencies, miners may be awarded some cryptocurrency as a reward for spending their computing power to compute PoW.
131 |
132 | 6. Creating interfaces for the Flask web app
133 | - I must create interfaces for the node to interact with other peers as well as with the application.
134 | - I will be building it with the Flask web framework to create a REST-API to interact with the node.
135 | - I need an endpoint for the app to submit a new transaction.
136 | - It will be used by the app to add new data (posts) to the blockchain.
137 | - I also need an edpoint to return the node's copy of the chain.
138 | - It will be used to query all of the posts to display to the user.
139 | - I also need an endpoint to request the node the mine the unconfirmed transactions (if any).
140 | - It will be used to initiate a command to mine from the app itself.
141 | - I also will add an endpoint to query the unconfirmed transactions.
142 | - At this point, I have a functioning blockchain, where I can create new transactions (posts), and mine them to add them to the blockchain.
143 | - However, the codebase at this point is meant to run on a single computer.
144 | - I will need to add functionality to have multiple nodes to maintain the blockchain.
145 |
146 | 7. Establishing consensus and decentralization
147 | - Even though I am linking blocks with hashes, still cannot trust a single entity.
148 | - I will need multiple nodes to maintain the blockchain.
149 | - I must create an endpoint to let a node know of other peers in the network.
150 | - I must also create an endpoint to add new peers to the network.
151 | - There is a problem with multiple nodes.
152 | - Due to intentional manipulation or unintentional reasons, the copy of chains of a few nodes can differ.
153 | - In that case, there must be an agreement upon some version of the chain.
154 | - This is known as consensus, which must be achieved to maintain the integrity of the entire system
155 | - A simple consensus algorithm could be to agree upon the longest valid chain when the chains of different participants in the network appear to diverge.
156 | - The rationale behind this approach is that the longest chain is a good estimate of the most amount of work done.
157 | - I also need to develop a way for any node to announce to the network that it has mined a block so that everyone can update their blockchain, and move on to mine other transactions.
158 | - This involves creating another endpoint to add a block mined by a user to the node's chain.
159 | - After every block is mined by the node, it should be announced, so that peers can then add it to their chains.
160 | - Other nodes can simply verify the proof of work and add it to their respective chains.
161 |
162 | 8. Building the application
163 | - At this point, the backend is all set up.
164 | - I'll build an interface for the application, which is a view in the codebase.
165 | - Using Flask, I'll use Jinja2 templates to render the web pages and some CSS for styling.
166 | - The application needs to connect to a node in the blockchain network to fetch the data and submit new data.
167 | - There can also be multiple nodes.
168 | - The application has an HTML form to take user input, and then makes a POST request to a connected node to add the transaction into the unconfirmed transactions pool.
169 | - The transaction is then mined by the network, and then finally will be fetched once the website is refreshed.
170 |
--------------------------------------------------------------------------------