Currently, we cannot directly manage blockchains on top of IPFS because there is no easy way to broadcast new hashes representing block proposals. IPFS is pull-driven and not push-driven, i.e., you cannot say to the world "hey, look at this new cat picture". As it has to be, clients need to request files explicitly.
Everything else required for blockchains is already present in IPFS:
- The p2p network.
- Transactions as files.
- Blocks as directory of transactions.
- Backpointers as immutable contents inside blocks and transactions.
IPFS provides the infrastructure to distribute these immutable and verifiable transactions and blocks. So, all we need is a way to broadcast hashes representing blocks for a specific blockchain.
I believe two new commands could make blockchain handling a piece of cake:
> ipfs pubsub publish <gen-block> <new-block>
> ipfs pubsub subscribe <gen-block>
<new-block-1>
...
<new-block-2>
...
As illustrated further, the pubsub
system is more general than for building
blockchains.
IPFS can easily represent blockchains which UNIX-like utilities can manipulate.
> echo "This is the genesis block for my awesome blockchain." > genesis
> ipfs add genesis
<gen-block-hash>
There are no restrictions on the format of blocks.
For example, the genesis block could be the hash of the pdf
describing the
blockchain design.
We can represent a block with a directory full of transactions:
> mkdir b1/
> echo "First transaction" > b1/1
> mv genesis b1/previous
> ipfs add -r b1/
<b1-hash>
> ipfs cat <b1-hash>/previous
"This is the genesis block for my awesome blockchain."
This creates a new block b1
with one transaction 1
.
The block b1
points to the previous
block, in this case, the genesis block.
Now, another block b2
linking back to b1
:
> mkdir b2/
> echo "Another transaction" > b2/1
> mv b1 b2/previous
> ipfs add -r b2/
<b2-hash>
> ipfs cat <b2-hash>/previous/previous
"This is the genesis block for my awesome blockchain."
Peers will eventually disagree on the correct path down to the genesis block. The format for blocks and transactions, as well as path validations, depends on the blockchain specification.
As desired here, all design decisions are up to the implementation on top of IPFS: tokens, mining, longest chains, proof-of-whatever, encryption and signatures, timestamps, dealing with attacks (e.g., double spending), etc.
The command-line API requires only two new commands:
> ipfs pubsub publish <gen-block-hash> <new-block-hash>
> ipfs pubsub subscribe <gen-block>
<new-block-1-hash>
...
<new-block-2-hash>
...
> ipfs pubsub publish <gen-block-hash> <new-block-hash>
This reads as
Propose to the network
<new-block-hash>
as the longest chain down to<gen-block-hash>
.
The <gen-block-hash>
has to be sent in advance to clients interested in
interacting with this blockchain.
This is the only hash clients have to pull manually and configure once on their
servers.
The <new-block-hash>
contents supposedly have a path down to the
<gen-block-hash>
.
However, it is not the purpose of IPFS to check if block proposals make sense.
> ipfs pusub subscribe <gen-block-hash>
<new-block-1-hash>
...
<new-block-2-hash>
...
This reads as
Someone in the network proposes
<new-block-*-hash>
as the longest chain down to<gen-block-hash>
.
Again, IPFS has no idea if this proposal makes sense, delegating checks to applications built on top of it.
There are two kinds of applications: protocols and clients.
Both kinds have no understanding of p2p networks, and just manipulate the file system as standard UNIX-like utilities:
> ipfs pusub subscribe <gen-block-X> | <proto-X.exe>
<new-block-a>
...
<new-block-f>
...
The <proto-X.exe>
is an executable external to IPFS which implements a
specific blockchain protocol.
The program would ipfs pin
the chain it thinks is the longest and ipfs unpin
old chains that it decides will never become the longest again.
Pinned chains will tend to remain in the IPFS network, while unpinned chains
might eventually disappear for good.
In this example, the program also outputs new blocks attached to the longest chain as they appear. Now, clients built on top of a specific protocol can rely on its outputs as valid longest chains:
ipfs pusub subscribe <gen-block-altcoin> | <altcoin.exe> | <ship-product.exe>
Bootstrapping requires no additional commands:
> <proto.exe> /ipfs/<new-block-z>
# this may take long...
> ipfs pusub subscribe <gen-block> | <proto.exe> | <client.exe>
# ok, start listening...
The <proto.exe>
has to parse the whole chain backwards to check its validity,
then it starts listening for new blocks and redirecting longest chains to
<client.exe>
.
Although this document focuses on blockchains, it should be possible to build
on top of ipfs pubsub
any kind of protocol that uses the publish-subscribe
pattern: blogs, social networks, games, and even old services such as e-mail.
> echo "This is a tweet!" | ipfs add -q | ipfs pubsub publish <gen-block-twitter>
> ipfs pubsub subscribe <gen-block-twitter> | ipfs cat
"This is a tweet!"
These services usually do not require a strict timeline and can be much simpler than blockchains.
The real work IPFS nodes would need to do behind the scenes is to continuously broadcast information in the p2p network. This new subsystem could use a different port that would remain closed in nodes not interested in participating in blockchains.
Also, the <gen-block>
arguments of ipfs pubsub
makes easy for nodes to
ignore specific blockchains completely.
Each node could manually provide a white list of chains of interest in
~/.ipfs/config
.
These policies preserve the overall pull nature of IPFS, i.e., there is no
way to force a node to subscribe for blockchain traffic or handle arbitrary
<gen-block>
hashes.
Looking at the IPFS Draft document (Section 3.4.4) seems like hacking with the
want_list
could provide a fast track to a working prototype.
A special bit flag could fix a <gen-block>
so that any traffic matching
ipfs publish <gen-block>
would be sent to the node.
For more scalability, peers sharing common pubsub
interests should connect
directly to one another.