MSc Exercise for Introduction to Computer Security
A simulation of a blockchain messaging platform based on the Bitcoin protocol.
Exchange privately and securely messages and images. Become a miner and collect rewards
or just a plebeian client and send messages without supporting the platform.
University of Thessaly
This project is inspired from Bitcoin: A Peer-to-Peer Electronic Cash System. A simplified version of the aformentioned paper, but with a message payload, was developed as part of an MSc Exercise for Introduction to Computer Security. We implemented the following:
- Inspired from Bitcoin:
- Proof of Work Algorithm, with fixed difficulty
- Lite and Heavy Nodes
- Transactions and Signatures
- Peer Network, but simplified and fixed (for DEMO Purposes)
- Incentive, Exchanging CPU time for Blabbers (Uncuffed currency)
- Saving Disk Space, with lite nodes storing only a lite-hash, while miners store the full tree. (No implementation of the Merkle Tree)
- Payment Verification and Value Splitting, using UTXOs
- Privacy, by using the public key for communication purposes
- Chat features:
- Message and Image sharing, by attaching the message in the transactions payload
- Encrypting communication over the blockchain asymmetric encryption
- Web-based platform for easier demonstration
- Expandable if necessary in the future to add more message types
- Transfer of Wealth. The bigger the messages, the more
Blabbers
you'll need to transmit it.
Built with plenty of Python and pure 💖
Being the instructor, or just a random stranger, interested in this project is not easy. This is why we
have created a quick Getting Started
guide to help you try out this project.
-
Download or Clone the repo
git clone https://github.com/WckdAwe/Uncuffed.git
-
Extract it on your desired location
-
Create a Virtual Environment and install all Dependencies
py -m venv venv pip install -r requirements.txt
-
Start the project and enter a port
py Uncuffed.py
For demonstration purposes choose on of the ports inside data/node_list.json, ex. 5000, 5001 or 5002. The chosen port must be inside this node_list in order for the peers to communicate.
-
Open a browser and navigate to home with the appropriate port, ex. http://127.0.0.1:5000/
You must repeat the process depending on the number of nodes you want to have in your system.
We are going to deep dive on how the Uncuffed project works by explaining how generic blockchain projects work, and then seeing what we do slightly different.
A blockchain is a list of a continuously ever-growing list of blocks, which are linked together using a cryptographic hash.
Each block contains a set of publicly viewable transactions which are stored permanently on the blockchain. Additionally, each block may also include some meta-data required for the calculations such as previous hashes, timestamps, difficulty, height etc.
All blockchain projects implement in one way or another the following characteristics:
- Secure by Design: They are resistant to the modification of data, and become immutable when sufficient time passes as the blockchain stabilizes
- Decentralized: Designed to work in Peer-to-Peer models, with no central point of failure
- Permissionless: Originally, they are designed open for the world. No secrets. No access-control needed.
While using this project you will encounter two types of users/nodes, Clients and Miners.
Lite Nodes or Clients, can be used to send or receive messages in exchange for currency, Blabbers
.
When receiving Transactions
and Blocks
by other nodes they simply store information that they may deem necessary,
and echo the Transaction or Block before they discard it. This way clients help to propagate incoming messages to other
nodes.
Full Nodes or Miners, are more important to the network. These nodes store the whole blockchain, validate incoming transactions or blocks and spend a considerable amount of their CPU power and storage to sustain the network. As result, these nodes are also the ones awarded currency when they successfully validate and attach blocks to the blockchain. Miners can be considered the labor-force of the network.
The image contains a simplified and easier to understand block structure. The following characteristics remain the same between different blockchain projects.
- Every block is unique. Each block is identifiable by its unique hash or index.
- Every block follows a strict chronological order. To be validated each new block must have a timestamp higher than the previous one.
- Every block is linked with the previous one. Each block has a field containing the
previous_block_hash
. To be validated not only the index and timestamp must be higher than the previous block, but more importantly theprevious_block_hash
must be valid! - Every block must contain at least one transaction.
- Every block must be validated by peers.
Proof of Work is the core mechanism that allows Uncuffed (and many other blockchain projects), to come to a consensus decentralized and agree on account balances, transactions, messages etc. It is a solution to a puzzle set by the blockchain to the miners. Solving this puzzle means a Miner can be allowed to append his block to the blockchain and be awarded for his contribution.
As soon as a new valid block is received in a Miner's blockchain he starts generating a new Proof of Work. The Miner uses the data from the previous block, usually the previous proof-of-work to solve the puzzle.
What is the puzzle you may ask? To generate a random set of characters, the proof of work, where the first X
(where X is difficulty)
characters must match a specific pattern. In the example above we can see a difficulty 4 puzzle, meaning the first 4 characters must match
a specific pattern, in this case 1111
.
This concept works fantastically because in order to generate those random characters, using the data from the previous block (in this case the previous proof), it may take many thousands or millions of calculations depending on the difficulty.
def verify_proof(prev_proof, proof, difficulty=2) -> bool:
guess = f'{prev_proof}{proof}'.encode()
guess_hash = SHA256.new(guess).hexdigest()
return guess_hash[:difficulty] == "1" * difficulty
def proof_of_work(prev_proof: int, difficulty=2) -> int:
proof = 0
while verify_proof(prev_proof, proof, difficulty) is False:
proof += 1
return proof
To verify though that this proof is actually correct, we only need two variables;
The previous proof
and the proof
a miner claims to have found.
Meaning, a Miner may take a huge amount of processing time in order to find a valid proof-of-work that solves the puzzle, but as soon as he does any miner or client can verify that proof with a single and fast calculation.
In our case the puzzle is solved with fixed difficulty of 2, which is quite easy to be fair. But easy is what we are looking for in order to update the user's chat faster.
Usually, transactions signal the transfer of wealth from a user to another. This remains
the case for Uncuffed, but with the additional payload of PLAINTEXT
or ENCRYPTED
messages.
Similarly to a block, each transaction is uniquely identifiable by its hash. This hash is generated using the transactions contents, such as the sender, receiver, value and timestamp.
The term UTXO refers to the amount of digital currency someone has left after executing a transaction.
Each single transaction entity contains two lists; Transaction Inputs
and Transactions Outputs
The first one, Transaction Inputs
, is easier to understand. It can be considered as a Pointer
to a specific Transaction Output
.
The Transacton Output
stores the transfer of wealth, meaning it is a "coupon" saying that the
X User has received Y amount of money
. Each transaction output can be considered either SPENT
or
UNSPENT
, aka UTXO (Unspent Transaction Output)
or STXO (Spent Transaction Output)
.
The simplest way to demonstrate this is with an example.
In a classical banking system, Vasilis may have 3.005.000 Euros, which were
acquired by many years of hard work. That money is stored as a single variable, money
,
in a database of a centralized bank. If Vasilis wanted to transfer
3.000.000 Euros to Elon Musk he could just write a check of 3.000.000 Euros and immediately his
bank account would update that money
variable with his new balance.
In our decentralized banking system, this concept wouldn't be sustainable. As such lets consider we know the exact transactions where Vasilis got his 3.005.000 balance. Specifically Vasilis received as single transactions:
- 2.000.000 from an Index Fund
- 995.000 from his job
- 10.000 from a friend
Now for Vasilis to send that amount of money to Elon Musk he would have to collect all
these 'coupons' that say he has X amount of money (a.k.a Unspent Transaction Outputs
) in a
single list (Input List
).
Now Vasilis wants to send 3.000.000 to Elon Musk, but he has 3.005.000 Euros. As such he creates
a new list (Output List
), where he sends 3.000.000 to Elon Musk and returns 5.000 to himself.
The transaction is complete.
Although this might seem a complicated system, in actuality, this system allows every Miner to keep track of the UTXOs and quickly verify if a single transaction is valid or invalid without having to ask a central authority!
We spoke of validation many times, but what exactly do we validate and how?
Validations occur on each element of the Blockchain, from the Blockchain itself all the way to each single Transaction Input and Output.
We demonstrate the whole validation process starting from the whole Blockchain. If someone wanted to validate a single element presented here, all he would need to do is follow the graph and validate the ones right to it.
To be valid,
- A Blockchain, must validate all each blocks
- A Block:
- Must have the correct ascending block order in the blockchain
- Must have a valid hash
- Must have at least one transaction_
- Must follow a strictly chronological order with its timestamps
- Must have a valid proof
- Must have a valid miner reward, which is the sum of the transaction fees + mining reward.
This transaction is called a
Coinbase Transaction
- Each Block's Transactions must be valid.
- Each Transaction:
- Must have a valid signature, signed by the sender to verify ownership
- Must have valid transaction Inputs and Outputs, meaning you can't send more than you have
- Must have a valid transaction fee going to a miner (0 or above)
- Each Transaction Input must exist in a block and in order to be valid it must also exist in the UTXO list
- Each Transaction Output must have a sender and a positive wealth exchange value
By reading everything above you must have by now at least some basic knowledge over how generic blockchains works and more importantly, how this blockchain works.
The last thing that we have to clarify about this project is how are these messages
sent all over the network. Each message is inserted as a message payload
inside a
Transaction Output
.
def __init__(self, recipient_address: str, value: int, message: Optional[AMessage]):
self.recipient_address: str = recipient_address
self.value: int = value
self.message: Optional[AMessage] = message
The bigger the message, the more Blabbers
you are going to need in order to transmit
the message. This is done by the following simple calculation,
which in essence Maximum between 1 and (message size in bits) / 64
:
@staticmethod
def calculate_minimum_blabbers(message: AMessage) -> int:
return max(1, int(get_deep_size(message) >> 6)
The message payload is the combination of an enumerator EMessageType
and
the actual string message message
.
In this submitted version we support the following messages:
Plain Text Message
, Encrypted Message
, Image Message
and
Encrypted Image Message
. Each one of these has its own dedicated class.
The Plain Text Message
simple attaches the user's message in the message
variable.
The Encrypted Message
encrypts using the RSA Algorithm (with ECB style) prior to attaching
the user's message in the message
variable. While this is not optimal
for an actual blockchain, it is more than enough for this presentation.
The Image Message
takes the uploaded image and converts it to a base64 string
prior to sending the message, while the Encrypted Image
does exactly the same thing
but also uses the RSA Algorithm to encrypt it prior to sending it.
Upon receiving a message, a Client checks if the message is for him. If the message is plaintext it just stores it directly. In the case where the message is Encrypted, the client users his private key to decrypt the message and then stores it to his chat.
The following URLs are used by the system in order to function. Most likely you are not going to mess with these. All the data transmitted are JSON serialized and encoded prior to any transmission.
/api/broadcasts/new_block
/api/broadcasts/new_transaction
/api/blockchain/
/api/blockchain/length
/api/blockchain/blocks
/api/transactions/pending
/api/transactions/UTXO
/api/nodes/list
/api/nodes/info
/api/nodes/register
You are most likely going to encounter all of these urls while you are demoing this project.
/ - GET: HOME
/selector - GET/POST: Selection between Client or Miner
/chat - POST: Send messages through the blockchain
/chat/{other_addr}/ - GET/POST: See and Send messages through he blockchain
All HTML templates used for the Web Interface are store inside
Uncuffed/templates
.
All local data are stored inside the data
folder. Looking inside that folder
you are going to find the following.
/chats - FOLDER: Contains all communications made between you and others
/chats/{id}.json - FILE: Stores all communications between you and that address
/uploads - FOLDER (UNUSED)
/blockchain.json - FILE: Stores the whole blockchain
/node.json - FILE: Stores node specific information, such as your UTXOs and STXOs
/node_list.json - FILE: Stores all other nodes you are broadcasting messages to
/wallet.der - FILE: Private key of your wallet
Distributed under the MIT License. See LICENSE.md
for more information.
All the information on this repository is provided in good faith, however we make no representation or warranty of any kind, express or implied, regarding the accuracy, adequacy, validity, reliability, availability or completeness of any information.
- Stack Overflow, The programmer's forever companion
- Medium | Building a blockchain with < 60 Lines of code
- IBM | Getting started with blockchain
- 101Blockchains | Building a blockchain in Python
- Investopedia | UTXO
- All image creators. Unfortunately I don't have the time to credit each one individually. If you wish for your images to be removed please contact me. They are only used for educational purposes of this project.