Package: toychain
toychaintoychain is a very simplistic blockchain node modeling in Python.
While the code is my own adaptation, the implementation is from the very goodtutorialby Daniel van Flymen.
This adaptation usesFastAPIas a web framework, anduvicornas ASGI server instead of theFlaskapp from van Flymen's tutorial.RunningThis repository usesPoetryas a build tool.
Get a local copy through VCS and to set yourself up withpoetry install.Thepoetry run nodecommand is predefined to start up a node, by default atlocalhost:5000.
Additionally, you can specify the host and port on which to run the node with the--hostand--portflags.
You can then use the same command to spin up several nodes on different ports.DockerIt is possible to run nodes as docker containers.
To do so, clone the repository then build the image withdocker build -t blockchain .You can then run the container by mapping the node's port to a desired one atlocalhoston your machine.
To map the node to port 5000, run:$dockerrun--init--rm-p5000:5000blockchainTo emulate additional nodes, vary the public port number:$dockerrun--init--rm-p5001:5000blockchain
$dockerrun--init--rm-p5002:5000blockchain
$dockerrun--init--rm-p5003:5000blockchainYou can then play around by POSTing to/nodes/registerto add all your running instances to one another's networks, POSTing transactions, mining new blocks, and resolving the blockchain.FunctionalityThe ChainThe blockchain is a simple list of blocks.
Ablockin the chain consists of a dictionnary with the following keys:theindexat which it is located in the chain,atimestampof when the block was added to the chain,the list oftransactionsrecorded in the block,theproofof validity for itself,aprevious_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 ImplementationThe blockchain functionality is provided by a single class,BlockChain, in thetoychain.blockchainmodule.
An instance of theBlockChainclass 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 theproofof 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 theFastAPIweb framework, and is attributed aUUIDat startup.
The implementation is in thetoychain.nodemodule, 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 withpython -m toychain), an automatic documentation for those is served at the/docsand/redocendpoints.Let's consider our node is running atlocalhost:5000.
POSTing a transaction to the node'stransactions/newendpoint with cURL would be done as follows:curl-XPOST-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 atlocalhost:5000.
POSTing a payload to register this new node to the first one's network with cURL would be done as follows:curl-XPOST-H"Content-Type: application/json"-d'{"nodes": ["http://127.0.0.1:5001"]}'"http://localhost:5000/nodes/register"If you would rather usehttpie, those commands would be, respectively:echo'{ "sender": "d4ee26eee15148ee92c6cd394edd974e", "recipient": "someone-other-address", "amount": 5 }'|httpPOSThttp://localhost:5000/transactions/newecho'{ "nodes": ["http://127.0.0.1:5001"] }'|httpPOSThttp://localhost:5000/nodes/register