A blockchain toy project, in Python
Project description
toychain
toychain is a very simplistic blockchain node modeling in Python.
While the code is my own adaptation, the implementation is from the very good tutorial by Daniel van Flymen.
This adaptation uses FastAPI as a web framework, and uvicorn as ASGI server instead of the Flask app from van Flymen's tutorial.
Running
This repository uses Poetry as a build tool.
Get a local copy through VCS and to set yourself up with poetry install.
The poetry run node command is predefined to start up a node, by default at localhost:5000.
Additionally, you can specify the host and port on which to run the node with the --host and --port flags.
You can then use the same command to spin up several nodes on different ports.
Docker
It is possible to run nodes as docker containers.
To do so, clone the repository then build the image with docker build -t blockchain .
You can then run the container by mapping the node's port to a desired one at localhost on your machine.
To map the node to port 5000, run:
$ docker run --init --rm -p 5000:5000 blockchain
To emulate additional nodes, vary the public port number:
$ docker run --init --rm -p 5001:5000 blockchain
$ docker run --init --rm -p 5002:5000 blockchain
$ docker run --init --rm -p 5003:5000 blockchain
You can then play around by POSTing to /nodes/register to add all your running instances to one another's networks, POSTing transactions, mining new blocks, and resolving the blockchain.
Functionality
The Chain
The blockchain is a simple list of blocks.
A block in the chain consists of a dictionnary with the following keys:
- the
indexat which it is located in the chain, - a
timestampof when the block was added to the chain, - the list of
transactionsrecorded in the block, - the
proofof validity for itself, - a
previous_hashtag referencing the hash of the previous block in the chain, for immutability.
A simple example block (with a single transaction) as a json payload would look like this:
block = {
"index": 1,
"timestamp": 1506057125.900785,
"transactions": [
{
"sender": "8527147fe1f5426f9dd545de4b27ee00",
"recipient": "a77f5cdfa2934df3954a5c7c7da5df1f",
"amount": 5,
}
],
"proof": 324984774000,
"previous_hash": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
}
The Node Implementation
The blockchain functionality is provided by a single class, BlockChain, in the toychain.blockchain module.
An instance of the BlockChain class is used to run a node.
Each node stores a full blockchain, the current transactions (not yet written in the chain), and the list of other nodes in the network.
It can:
- Add a transaction to the list of current transactions,
- Add a new (validated) block to the chain,
- Run the proof of work algorithm (here simple, for the sake of computation time),
- Validate the
proofof a block, - Register other nodes on the network,
- Infer an arbitrary node's blockchain's validity,
- Resolve conflict through a consensus algorithm, checking all nodes' chains in the network and adopting the longest valid one.
A node is ran as a REST API using the FastAPI web framework, and is attributed a UUID at startup.
The implementation is in the toychain.node module, and the available endpoints of a node are:
GETendpoint/mineto trigger the addition of a new block to the chain,POSTendpoint/transactions/newto add a transaction to the node's list,GETendpoint/chainto pull the full chain,POSTendpoint/nodes/registerto register other nodes' addresses as part of the network,GETendpoint/nodes/resolve: to trigger a run of the consensus algorithm and resolve conflicts: the longest valid chain of all nodes in the network is used as reference, replacing the local one, and is returned.
Once the server is running (for instance with python -m toychain), an automatic documentation for those is served at the /docs and /redoc endpoints.
Let's consider our node is running at localhost:5000.
POSTing a transaction to the node's transactions/new endpoint with cURL would be done as follows:
curl -X POST -H "Content-Type: application/json" -d '{
"sender": "d4ee26eee15148ee92c6cd394edd974e",
"recipient": "someone-other-address",
"amount": 5
}' "http://localhost:5000/transactions/new"
Let's now consider that we have started a second node at localhost:5000.
POSTing a payload to register this new node to the first one's network with cURL would be done as follows:
curl -X POST -H "Content-Type: application/json" -d '{
"nodes": ["http://127.0.0.1:5001"]
}' "http://localhost:5000/nodes/register"
If you would rather use httpie, those commands would be, respectively:
echo '{ "sender": "d4ee26eee15148ee92c6cd394edd974e", "recipient": "someone-other-address", "amount": 5 }' | http POST http://localhost:5000/transactions/new
echo '{ "nodes": ["http://127.0.0.1:5001"] }' | http POST http://localhost:5000/nodes/register
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file toychain-0.2.0.tar.gz.
File metadata
- Download URL: toychain-0.2.0.tar.gz
- Upload date:
- Size: 8.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.0.5 CPython/3.7.6 Darwin/19.5.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
373e0afd92fd9c04ae9ceecf9b265b1635c4b56f4996f73fb35dbc94854fc4d1
|
|
| MD5 |
cc0acc11718d8ee43fba1436f3010f5c
|
|
| BLAKE2b-256 |
ebb3da6ce069a67351910eac116a60727f16efc206c42c9aff31ed4d183610c6
|
File details
Details for the file toychain-0.2.0-py3-none-any.whl.
File metadata
- Download URL: toychain-0.2.0-py3-none-any.whl
- Upload date:
- Size: 8.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.0.5 CPython/3.7.6 Darwin/19.5.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
95a584f69d319b57e16e0558d0e5db7006632c799e86daa0a44759fd9c7c1cad
|
|
| MD5 |
8bc16dc5e8bc09ae082ad7032b0d9251
|
|
| BLAKE2b-256 |
a02fd63856e30ed34ef4390dfc8bd4d916da937de8631d5544eef59f50acb5bf
|