8 | 14 |
15 |
{{post.content}}
29 |├── .gitignore ├── LICENSE ├── README.md ├── app ├── __init__.py ├── static │ └── stylesheet.css ├── templates │ ├── base.html │ └── index.html └── view.py ├── node_server.py ├── run_app.py └── screenshots └── blocknet-home.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |{{post.content}}
29 |