|
@@ -22,32 +22,33 @@ The online services that users currently have access to are incredible. They han
|
|
|
the details of backing up user data and implementing access controls to facilitate
|
|
|
safe sharing. However, because these are closed systems, users are forced to trust that
|
|
|
the operators are benevolent, and they lack any real way of ensuring that the access
|
|
|
-control they prescribe will actually be enforced. There have been several systems proposed
|
|
|
+controls they prescribe will actually be enforced. There have been several systems proposed
|
|
|
as an alternative to the conventional model, but these systems suffer from several shortcommings.
|
|
|
They either assume the need for cloud storage providers (Blockstack) or implement all operations
|
|
|
-using a global blockchain, limiting performance (FileCoin). Blocktree takes a different approach.
|
|
|
+using a global blockchain, limiting performance (Filecoin). Blocktree takes a different approach.
|
|
|
|
|
|
The idea behind blocktree is to organize a user's computers into a cooperative unit, called a
|
|
|
blocktree. The user is said to own the blocktree, and they wield soveriegn authority over it.
|
|
|
The artifact granting them this authority is the root private key for the blocktree. Measures for protecting
|
|
|
this key and delegating its authority are important design considerations of the system.
|
|
|
-The owners of blocktrees are encouraged to collaborate with each to store data by
|
|
|
+The owners of blocktrees are encouraged to collaborate with each other to replicate data by
|
|
|
means of a cryptocurrency known as blockcoin. The blockchain implementing this cryptocurrency
|
|
|
is the source of global state for the system, and allows for the creation of global paths.
|
|
|
|
|
|
-All data stored in blocktree is contained in units called blocks. The blocks in a blocktree, of
|
|
|
-course, form a tree. Each block has a path corresponding to its location in the tree. The first
|
|
|
-component of a fully qualified block tree path is the fingerprint of the root public key of the
|
|
|
+All data stored in blocktree is contained in units called blocks. As the name would suggest, the blocks
|
|
|
+in a blocktree form a tree. Each block has a path corresponding to its location in the tree. The first
|
|
|
+component of a fully qualified blocktree path is the fingerprint of the root public key of the
|
|
|
blocktree. Thus a blocktree path can globally specify a block. If a block is not a leaf,
|
|
|
-then it is called a directory, and the data it contains is managed by the system,
|
|
|
-including the list of blocks which are children of the block. In addition to its payload of data,
|
|
|
+then it is called a directory, and the data it contains is managed by the system.
|
|
|
+This information includes the list of blocks which are children of the directory. In addition
|
|
|
+to its payload of data,
|
|
|
each block has a header containing cryptographic access control mechanisms. These mechanisms ensure
|
|
|
-that only authorized users can read or optionally write to the block.
|
|
|
+that only authorized users can read and optionally write to the block.
|
|
|
|
|
|
Users and nodes in the blocktree system are identified by hashes of their public keys. These hashes
|
|
|
-are referred to as principals, as they are the units used for setting access control policy.
|
|
|
+are referred to as principals, and they are used for setting access control policy.
|
|
|
|
|
|
-This remainder of this paper is as follows:
|
|
|
+This remainder of this paper is organized as follows:
|
|
|
\begin{itemize}
|
|
|
\item A description of the operations of a single blocktree.
|
|
|
\item The definition of a blockchain which provides global state and links individual blocktrees
|
|
@@ -61,12 +62,12 @@ together.
|
|
|
The atomic unit of data storage, confidentiality and authenticity is called a block. A
|
|
|
block contains a payload of data. Confidentiality of this data is achieved by encrypting it using
|
|
|
a symmetric cipher using a random key. This random key is known as the block key.
|
|
|
-The block key is encapsualated using the public key of the principal whose is being given access.
|
|
|
-The resulting cipher text is stored in the header of the block. Thus
|
|
|
+The block key can be encapsulated using the public key of a principal whose is to be given access.
|
|
|
+The resulting ciphertext is stored in the header of the block. Thus
|
|
|
the person possessing the corresponding private key will be able to access the contents of
|
|
|
the block. Blocks are arranged into trees, and the parent of the block also has a block key.
|
|
|
The child's block key is always encapsulated using the parent's key and stored in the block
|
|
|
-header. This ensures that if a principal is given access to a block, they automatically have
|
|
|
+header. This ensures that when a principal is given read access to a block, it automatically has
|
|
|
access to every child of that block. The encapsulated block key is known as a read capability,
|
|
|
or readcap, as it grants the holder the ability to read the block.
|
|
|
|
|
@@ -83,7 +84,7 @@ writecap is approximately an x509 certificate chain. A writecap contains the fol
|
|
|
\end{itemize}
|
|
|
The last item is only excluded in the case of a self-signed writecap, i.e. one that was signed by
|
|
|
the same principal it was issued to. A writecap is considered valid for use on a block if all
|
|
|
-of the following conditions are true:
|
|
|
+of the following conditions are met:
|
|
|
\begin{itemize}
|
|
|
\item The signature on every writecap in the chain is valid.
|
|
|
\item The signing principal matches the principal the next writecap was issued to for every
|
|
@@ -91,50 +92,52 @@ write cap in the chain.
|
|
|
\item The path of the block is contained in the path of every writecap in the chain.
|
|
|
\item The current timestamp is strictly less than the expiration of all the writecaps in the
|
|
|
chain.
|
|
|
-\item The principal corresponding to public key used to sign the last writecap in the chain,
|
|
|
+\item The principal corresponding to the public key used to sign the last writecap in the chain,
|
|
|
is the owner of the blocktree.
|
|
|
\end{itemize}
|
|
|
The intuition behind these rules is that a writecap is only valid if there is a chain of trust
|
|
|
-that leads back to the owner of the block tree. The owner may delegate their trust to any number
|
|
|
+that leads back to the owner of the blocktree. The owner may delegate their trust to any number
|
|
|
of intermediaries by issuing them writecaps. These writecaps are scoped based on the path
|
|
|
specified when they are issued. These intermediaries can then delegate this trust as well.
|
|
|
A block is considered valid if it contains a valid writecap, it was signed using the key
|
|
|
corresponding to the first writecap's public key, and this signature is valid.
|
|
|
|
|
|
Blocks are used for more than just orgnaizing data, they also organize computation. A program
|
|
|
-participating in the blocktree network is referred to as a node. Multiple nodes may be run on
|
|
|
+participating in a blocktree is referred to as a node. Multiple nodes may be run on
|
|
|
a single computer. Every node is attached to the blocktree at a specific path. This information
|
|
|
-is recorded in the block where the node is attached. A node is responsible for the storage of
|
|
|
-the block where it is attached and the blocks that are descended from this block, unless there
|
|
|
-is another node attached to a descendent block.
|
|
|
-In this way data storage can be delegated, allowing
|
|
|
-the system to scale. When more than one node is attached to the same block they form a cluster.
|
|
|
-Each node in the cluster contains a copy of the data that the cluster is reponsible for. They
|
|
|
+is recorded in the directory where the node is attached. A node is responsible for the storage of
|
|
|
+the directory where it is attached and all of the blocks that are recursively contained with in it.
|
|
|
+Of course if there is a child node attached to a subdirectory contained in the directory the node is
|
|
|
+responsible for, then the child node is responsible for the subdirectory.
|
|
|
+In this way data storage can be delegated, allowing the system to scale. When more than one
|
|
|
+node is attached to the same directory they form a cluster.
|
|
|
+Each node in the cluster contains a copy of the data that the cluster is responsible for. They
|
|
|
maintain consistency of this data by running the Raft consensus protocol.
|
|
|
|
|
|
-Every blocktree requires at least one node attached to the root block to function. The nodes
|
|
|
-in the root block contain the user's private key. For security, it is highly recommended that
|
|
|
-this key be stored in a Trusted Platform Module (TPM), and that the TPM be configured to disallow
|
|
|
-unauthenticate key use. As it is envisioned for multiple nodes to run on a single computer,
|
|
|
-thus sharing a single TPM, this last point is particularly important. Even though these nodes
|
|
|
-contain the root key, they do not use it for most operations, and instead use the scheme described
|
|
|
-in the next paragraph to obtain their own credentials.
|
|
|
-
|
|
|
-When a new node is created, it generates a new public-private key pair. The public key of this
|
|
|
+When a new blocktree is created a node generates a key pair to serve use as the root keys.
|
|
|
+It is imperative for the security of the system that the root private key is protected, and it
|
|
|
+is highly recommended that it be stored in a Trusted Platform Module (TPM) and that the TPM
|
|
|
+be configured to disallow unauthenticated use of this key. The node then generates its own key pair
|
|
|
+and uses the root private key to issue itself a writecap for the root of the tree. Once it has
|
|
|
+this writecap, it creates the root block and generates a block key for it. A readcap is added to
|
|
|
+this block for the root public key and the node's public key. Additional
|
|
|
+cryptographic operations are performed using the node's key pair, and only when a new writecap
|
|
|
+needs to be created for an addition root node is the root private key used.
|
|
|
+
|
|
|
+When a new node comes online and wishes to join the blocktree, it generates its own key pair.
|
|
|
+The public key of this
|
|
|
node then needs to be transmitted to another node that's already part of the user's blocktree. The
|
|
|
-mechanism used will depend on the nature of the device on which the node is running, and is
|
|
|
-outside the scope of this description. For example, a phone could scan a QR code which contains
|
|
|
+mechanism used will depend on the nature of the device on which the new node is running.
|
|
|
+For example, a phone could scan a QR code which contains
|
|
|
the IP address of the user's root node, and then transmit its public key to that internet host.
|
|
|
In order for the new node to be added to the user's blocktree, it needs to be issued a writecap
|
|
|
-and the block where it will attach needs to have a readcap added.
|
|
|
+and a readcap must be added to the directory where it will be attached.
|
|
|
This could be accomplished
|
|
|
by providing a user interface on the node which received the public key from the new node.
|
|
|
This interface would show the user the requests that have been received from new nodes attempting
|
|
|
to join their blocktree. The user can then choose to approve or deny the request, and can specify
|
|
|
-the path where the node will attach. If the user chooses to approve the request, they are
|
|
|
-prompted for the root password. This is used to send an authenticated signing request to the TPM on
|
|
|
-the node containing the user's root key. If the password is correct, the TPM will sign the requested
|
|
|
-data, producing a valid writecap, which the node can then send back to the new node.
|
|
|
+the path where the new node will attach. If the user chooses to approve the request, then the writecap
|
|
|
+is signed using the node's key and transmitted to then new node.
|
|
|
|
|
|
The ability to cope with key compromise is an important design consideration in any real-world
|
|
|
cryptosystem. In blocktree the compromise of a node key is handled by re-keying every block under
|
|
@@ -151,45 +154,45 @@ private key in multiple secure cryptographic co-processors is so important.
|
|
|
A concept that has proven to be very useful in the world of filesystems is the symbolic link.
|
|
|
This is a short file that contains the path to another file, and is interpreted by most programs
|
|
|
as being a "link" to that file. Blocktree supports a similar system, where a block can be
|
|
|
-marked as a symbolic link when its body contains a blocktree path. This also provides us with
|
|
|
+marked as a symbolic link and its body contains a blocktree path. This also provides us with
|
|
|
a convenient way of storing readcaps for data that a node would otherwise not have access to.
|
|
|
For instance a symbolic link could be created which points to a block in another user's blocktree.
|
|
|
The other user only knows the public key of the owner of our blocktree, so they issue
|
|
|
a readcap to it. But the root nodes, when given the user's password, can open this readcap and extract
|
|
|
-the block key. This key can then be encapsulated using he public key of the node which
|
|
|
+the block key. This key can then be encapsulated using the public key of the node which
|
|
|
requires access, and placed in the symbolic link. When the node needs to read the data
|
|
|
in the block, it opens the readcap in the symbolic link, follows the link to the block (how
|
|
|
that actually happens will be discussed below) and decrypts its contents.
|
|
|
|
|
|
-While the consistency of an individual block can be maintained using Raft, in order to enable
|
|
|
-transactions which span multiple blocks a distributed locking mechanism is employed. This is
|
|
|
-accomplished by exploiting hierarchical arrangement of nodes in the tree. In order to describe
|
|
|
+While the consistency of individual blocks can be maintained using Raft, a distributed locking
|
|
|
+mechanism is employed to enable transactions which span multiple blocks.
|
|
|
+This is
|
|
|
+accomplished by exploiting the hierarchical arrangement of nodes in the tree. In order to describe
|
|
|
this, its first helpful to define a new term. The \emph{nodetree} of a blocktree is tree obtained
|
|
|
from the blocktree by collapsing all the blocks that a node (or cluster of nodes) is responsible
|
|
|
-for into a single logical block representing the node itself. Thus we can talk about the node
|
|
|
-which is the parent of another node, and by these we mean it is the parent of the node in the
|
|
|
-nodetree. What this means in terms of the blocktree is that it is the first node encountered
|
|
|
-when one traverses the path from the current block back to the root. Now, distributed locking
|
|
|
-works as follows:
|
|
|
+for into a single logical block representing the node itself. Thus we can talk about a node having
|
|
|
+a parent, and by this we mean its parent in the nodetree. In terms of the blocktree, the parent
|
|
|
+of a node is the first node encountered when the path back to the root is traversed.
|
|
|
+Now, distributed locking works as follows:
|
|
|
\begin{itemize}
|
|
|
-\item A node sends a request to lock a block to the current concensus leader in its cluster.
|
|
|
+\item A node sends a request to lock a block to the current consensus leader in its cluster.
|
|
|
If the node is not part of a cluster, then it is the leader. This request contains a timestamp
|
|
|
- for when the lock expires, preventing the situation where a lock is never released.
|
|
|
+ for when the lock expires.
|
|
|
\item If the leader is responsible for the block then it moves on to the next step. Otherwise
|
|
|
it contacts its parent and forwards the lock request and this step is repeated for the parent.
|
|
|
\item The responsible node checks to see if there is already a lock for this block. If there is
|
|
|
then the request fails. Otherwise the request succeeds and a lock is placed on the block. A
|
|
|
- message indicating the result is then pass back up the tree ending at the orignal node. This
|
|
|
+ message indicating the result is then passed back up the tree ending at the original node. This
|
|
|
message includes the principal of the node enforcing the lock.
|
|
|
\item Once the locking node is done making its updates it sends a message directly to the node
|
|
|
- enforcing the lock, causing it to remove the lock.
|
|
|
+ enforcing the lock, causing it to be removed.
|
|
|
\end{itemize}
|
|
|
Locking a block locks the subtree rooted at that block. Thus no writes to any path contained in
|
|
|
the path of the locked block will be allowed, unless they come from locking node. If the locking
|
|
|
node does not send the message
|
|
|
unlocking the block before the lock expires, then the modifications which had been performed by
|
|
|
it are dropped and the block reverts to its prior state. Since the locking node is the leader
|
|
|
-of the consensus cluster that is responsible for the block's, this guarantees that
|
|
|
+of the consensus cluster that is responsible for the block, this guarantees that
|
|
|
writes from other nodes will not be accepted.
|
|
|
|
|
|
\section{Connecting Blocktrees}
|
|
@@ -202,9 +205,10 @@ which is the sum of the fees for each event in the chain and a variable amount o
|
|
|
blockcoin. The amount of new blockcoin created by a chain block is directly proportional to the
|
|
|
amount of data storage events contained in the chain block. Thus the total amount of blockcoin
|
|
|
in circulation has a direct relationship to the amount of data stored in the system, reflecting
|
|
|
-the fact that blockcoin exists to provide and accounting mechanism for data stored in the system.
|
|
|
+the fact that blockcoin exists to provide an accounting mechanism for data.
|
|
|
|
|
|
-When a node writes data to a tree block, and it wishes this block to be globally accessible, then
|
|
|
+When a node writes data to a tree block, and it wishes this block to be globally accessible or
|
|
|
+replicated for redundancy,
|
|
|
it produces what are called fragments. Fragments are the output symbols from an Erasure Coding
|
|
|
algorithm (such as the RaptorQ code). These algorithms are a class of fountain codes which have
|
|
|
the property that only $m$ out of $n$ (where $m < n$) symbols are needed to reconstruct the
|
|
@@ -213,32 +217,32 @@ remain, the original data can be recovered.
|
|
|
|
|
|
Once these fragments have been computed an event is created for each one and published to the
|
|
|
blockchain. This event indicates to other nodes that this node wishes to store a fragment and
|
|
|
-states the amount of blockcoin it will pay and the frequency it will make these payments. When
|
|
|
-another nodes wishes to accept the offer, it directly contacts the first node, who then sends
|
|
|
-it the fragment an publishes and event stating that the fragment is stored with the second
|
|
|
+states the amount of blockcoin it will pay for each of the fragment's maintenance payments. When
|
|
|
+another nodes wishes to accept the offer, it directly contacts the first node, which then sends
|
|
|
+it the fragment and publishes an event stating that the fragment is stored with the second
|
|
|
node. This event includes the path of the block the fragment was computed from, the fragment's
|
|
|
ID (the sequence number from the erasure code), and the principal of the node which stored it.
|
|
|
-Thus any other node in the network can use the information contained in these events to
|
|
|
+Thus any other node in the network can use the information contained in this event to
|
|
|
determine the set of nodes which contain the fragments of any given path.
|
|
|
|
|
|
In order for the node which stored a fragment to receive its next payment, it has to pass
|
|
|
-a time-bound challenge-response protocol initiated by the node that ownes the fragment.
|
|
|
+a time-bound challenge-response protocol initiated by the node that owns the fragment.
|
|
|
The owning node select a leaf in the Merkel tree of the fragment and sends the index of
|
|
|
this leaf to the storing node. The storing node then walks the path from this leaf back to
|
|
|
the root of the Merkle tree, and updates a hash value using the data in each node it traverses.
|
|
|
-It sends this result back to the owning node who then verifies that this value matches its
|
|
|
+It sends this result back to the owning node which verifies that this value matches its
|
|
|
own computation. If it does then the owning node signs a message indicating that the challenge
|
|
|
-passed and that the storing node should be paid. The storing node recives this message and uses
|
|
|
+passed and that the storing node should be paid. The storing node receives this message and uses
|
|
|
it to construct an event, which it signs and publishes to the blocktree. This event causes
|
|
|
-the blockcoin amount specified to be withdrawn from the owning node's account and deposited
|
|
|
-into storing nodes account.
|
|
|
+the blockcoin amount specified to be transferred from the owning node's to the storing
|
|
|
+node's blocktree.
|
|
|
|
|
|
The fact that payments occur over time provides a simple incentive for nodes to be honest and
|
|
|
store the data they agree to. In banking terms, the storing node views the fragment as an
|
|
|
asset, it is a loan of its disk space which provides a series of payments over time.
|
|
|
On the other hand the owning node views the fragment as a liability, it requires payments to
|
|
|
be made over time. In order for a blocktree owner to remain solvent, it must balance its
|
|
|
-liabitlies with its assets, incentivizing it to store data for others so that its own data
|
|
|
+liabilities with its assets, incentivizing it to store data for others so that its own data
|
|
|
will be stored.
|
|
|
|
|
|
In order for nodes to be able to contact other nodes, a mechanism is required for associating
|
|
@@ -246,70 +250,74 @@ an internet protocol (IP) address with a principal. This is done by having nodes
|
|
|
to the blockchain when their IP address changes. This event includes their new IP address,
|
|
|
their public key, and a digital signature computed using their private key. Other nodes can
|
|
|
then verify this signature to ensure that an attacker cannot bind the wrong
|
|
|
-IP address to a principal in order to receive messages it was not meant to have.
|
|
|
+IP address to a principal in order to receive messages not meant for it.
|
|
|
|
|
|
While this event ledger is useful for appending new
|
|
|
-events, and ensuring that previous events
|
|
|
-cannot be changed, another data structure is required to ensure that queries on this data can
|
|
|
-be performed efficiently. In particular, it's important to be able to quickly perform the
|
|
|
+events, and ensuring previous events
|
|
|
+cannot be changed, another data structure is required to enable efficient queries.
|
|
|
+In particular, it's important to be able to quickly perform the
|
|
|
following queries:
|
|
|
\begin{itemize}
|
|
|
\item Find the set of nodes storing the fragments for a given path.
|
|
|
\item Find the IP address of a node or owner given a principal.
|
|
|
\item Find the public key associated with a principal.
|
|
|
\end{itemize}
|
|
|
-To enable these queries a special blocktree is maintained by each node in the network: the global blocktree.
|
|
|
-This tree does not support the usual writing and locking sematics of local blocktrees. It can be thought of as
|
|
|
-a left fold of all of the events in the blockchain, where each event is processed by updating blocks in the
|
|
|
-global tree appropriately. The above queries are facilitate by the following blocks:
|
|
|
+To enable these queries a special blocktree is maintained by each node in the network: the global
|
|
|
+blocktree. This tree does not support the usual writing and locking semantics of local blocktrees.
|
|
|
+In functional programming terms, it can be thought of as a left fold over all of the events in the
|
|
|
+blockchain.
|
|
|
+The above queries are facilitated by the following blocks:
|
|
|
\begin{itemize}
|
|
|
-\item \emph{/global/fragments}: this block contains a hashtable where the key is a path and the value is the list
|
|
|
-of nodes storing the fragments for the block at that path.
|
|
|
-\item \emph{/global/principals}: contains a hashtable where the key is the a principal and the value is the tuple
|
|
|
-contining the public key of that principal, its current IP address, and its current blockcoin balance.
|
|
|
+\item \emph{/global/fragments}: this block contains a hashtable where the key is a path and the
|
|
|
+value is the list of nodes storing the fragments for the block at that path.
|
|
|
+\item \emph{/global/principals}: contains a hashtable where the key is the a principal and the value
|
|
|
+is the tuple containing the public key of that principal, its current IP address, and its current
|
|
|
+blockcoin balance.
|
|
|
\end{itemize}
|
|
|
To compute the entries in these tree blocks, the nodes in the network iterate over all the chain blocks, updating
|
|
|
-their local copy of each tree block approriately. The experienced reader will recognize that this is an event
|
|
|
-sourced architecture. At this time only the two tree blocks are known to be needed, but if new events need to be
|
|
|
+their local copy of each tree block appropriately. The experienced reader will recognize that this is an event
|
|
|
+sourced architecture. Currently only these two tree blocks are known to be needed, but if new events are
|
|
|
added to the system it's easy to see that this system can be used for creating other data structures enabling
|
|
|
-queries that we have yet to envision. One such extension is the registration of globally unique names, which will
|
|
|
-be the focus of future work.
|
|
|
+queries that we have yet to envision.
|
|
|
|
|
|
\section{Programming Environment}
|
|
|
Enabling an excellent developer experience is one of the primary goals of this system (the others being security
|
|
|
-and scaleability). Nodes execute user code that has been compiled into WebAssembly modules. Such code
|
|
|
+and scalability). Nodes execute user code that has been compiled into WebAssembly modules. Such code
|
|
|
running on a blocktree node is referred to as an "app". An app
|
|
|
executes in a sandbox that isolates it from other code, as well as the security critical operations of the node
|
|
|
itself. The sandbox provides the code with an extension of the WebAssembly System Interface (WASI), with extra
|
|
|
-system calls to interact with the particulars of the blocktree system. The stadard WASI filesystem APIs are used
|
|
|
-to interact with the contents of blocktrees. For instance a file descriptor for a remote block can be obtained
|
|
|
-by calling path\_open. Writes and reads of blocks are performed using the privledges of the node on which
|
|
|
-the app is running. The extra system calls fall into three categories:
|
|
|
+system calls to interact with the particulars of the blocktree system.
|
|
|
+The extra system calls fall into three categories:
|
|
|
\begin{itemize}
|
|
|
\item Distributed Locking
|
|
|
\item Messaging
|
|
|
\item Supervision Trees
|
|
|
\item Protocol Contracts (Session Types)
|
|
|
\end{itemize}
|
|
|
+The standard WASI filesystem APIs are used
|
|
|
+to interact with the contents of blocktrees. For instance a file descriptor for a block can be obtained
|
|
|
+by calling path\_open. Writes and reads of blocks are performed using the privileges of the node on which
|
|
|
+the app is running.
|
|
|
|
|
|
-When an app is installed it is given a block under which it can store data that is shared between all nodes
|
|
|
+When an app is installed it is given a directory under which it can store data that is shared between all nodes
|
|
|
in the blocktree. The path of this block is formed by prefixing the path the app was published at
|
|
|
-with the string "/apps". When an app is installed on a particular node, it is run in a block contained
|
|
|
-in the node's block. It can read and write blocks in this block, and to allow it to access shared data,
|
|
|
-a symbolic link is created to the app's block in "/apps".
|
|
|
+with the string ``/apps". When an app is runs on a node, it is confined to a block contained
|
|
|
+in the node's directory. It is only allowed read and write blocks in this block, but to allow it to
|
|
|
+access shared data, a symbolic link is created to the app's shared directory in ``/apps".
|
|
|
|
|
|
% App Publishing
|
|
|
-These apps are, of course, distributed in a blocktree. The path of the block used to publish the app is used to
|
|
|
-identify the app, and must be unique. The block containing the app contains the WebAssembly module itself as
|
|
|
-well as JSON file containing the app's manifest. This manifest defines the app's name as well as the list
|
|
|
-of permissions it requires. This list of persmissions is used to determine which APIs the app will have access to.
|
|
|
+Apps are published by writing them into a blocktree. The path of the directory used to publish an app is used to
|
|
|
+identify it. Only one app per directory is allowed. This directory contains the WebAssembly module itself as
|
|
|
+well as a JSON manifest. This manifest defines the app's user-friendly name as well as the list
|
|
|
+of permissions it requires. This list of permissions is used to determine which APIs the app has access to.
|
|
|
|
|
|
% Privacy Safe vs Unsafe
|
|
|
-Apps are broken into two large categories: those that are privacy safe and those that are not.
|
|
|
+Apps are broken into two categories: those that are privacy safe and those that are not.
|
|
|
An app is privacy unsafe if it requests any permissions which allow it to send data
|
|
|
-to any node that is not part of the blocktree in which it's running. Thus request the ability to
|
|
|
-open a TCP socket would cause an app to be privacy unsafe. Similarly the creation of a protocol handler for
|
|
|
-HTTP would also be privacy unsafe. Privacy unsafe apps can limit the scope of their unsafety by
|
|
|
+outside of the blocktree it's part of. Thus requesting the ability to
|
|
|
+open a TCP socket would cause an app to be privacy unsafe. Similarly, the creation of a protocol
|
|
|
+handler for HTTP would also be privacy unsafe. Privacy unsafe apps can limit the scope of their
|
|
|
+unsafety by
|
|
|
imposing limits on the unsafe APIs that they request. For instance an app which needs to send
|
|
|
blocktree message back to the blocktree it was published in can request the messaging permission
|
|
|
for a path in this tree. Similarly, an app which only wants to open a TCP socket listening on the
|
|
@@ -323,66 +331,62 @@ supply a protocol contract to. This contract is then compiled into a state machi
|
|
|
and a handle is returned to the app. This handle is then used to register callbacks for different
|
|
|
parts of the protocol. This ensures that the protocol is handled by the node itself and that
|
|
|
protocols can be shared between many different apps as a library. For instance, the HTTP protocol
|
|
|
-would be compiled to a particularly simply state maching, with only one state: the listening state.
|
|
|
-This state would expose a hook that where a callback can be registered to handle the request.
|
|
|
+would be compiled to a particularly simply state machine, with only one state: the listening state.
|
|
|
+This state would expose a hook that where a callback can be registered to handle a request.
|
|
|
This state also defines the record format used to pass the request information to the callback
|
|
|
and the record format of the return value that is expected in order to produce the response.
|
|
|
-More complicated (stateful) protocols would more states, each defining their own request and
|
|
|
+More complicated (stateful) protocols would have more states, each defining their own request and
|
|
|
response records, as well as hooks. One nice thing about this setup is that it will enable
|
|
|
optimizations where the state machine and the user callbacks can be compiled into a program
|
|
|
which can be safely run in the node itself, or even in a SmartNIC. This would require that
|
|
|
-the callbacks only use an approved set of APIs, but it can enable much higher performance
|
|
|
-network services.
|
|
|
+the callbacks only use an approved set of APIs, but could enable much higher performance.
|
|
|
|
|
|
% Supervision Trees
|
|
|
Apps can also arrange themselves into supervision trees, in the same way that Erlang
|
|
|
processes are arranged. In this scheme, when a child app crashes, or the node its
|
|
|
-running on dies (which is detected by the node), then the app receives a message. In the
|
|
|
+running on dies (which is detected by other nodes), then the app receives a message. In the
|
|
|
simplest case this can be used to implement a logging system, where crashes and node
|
|
|
-deaths are recorded. More interestingly, this could be used to integrate with a control
|
|
|
-plan. For instance, if a blocktree were running in AWS, when a message is recevied indicating
|
|
|
-that a node has died a new EC2 instance could be started to replace it. Of course these
|
|
|
-are just two of the potential applications of this mechanism. The reliability of Erlang
|
|
|
+deaths are recorded. More interestingly, this can be used to integrate with a control
|
|
|
+plane. For instance, if a blocktree were running in AWS, when a message is received indicating
|
|
|
+that a node has died, a new EC2 instance could be started to replace it. The reliability of Erlang
|
|
|
and other system employing the Actor Model have shown the robustness of this approach.
|
|
|
|
|
|
\section{A Brave New Web}
|
|
|
-In order to explore how this system can be used, the design of several hypothetical systems
|
|
|
-is discussed. It's important to note that blocktree does not try to force all compuatation
|
|
|
-to be local to a user's device, it merely trys to enable this for applications where it
|
|
|
+In order to explore how blocktree can be used, the design of several hypothetical systems
|
|
|
+is discussed. It's important to note that blocktree does not try to force all computation
|
|
|
+to be local to a user's device, it merely tries to enable this for applications where it
|
|
|
is possible.
|
|
|
|
|
|
\subsection{Contacts and Mail}
|
|
|
The first application we'll consider is one which manages a user's contacts. This would expose
|
|
|
-the usual CRUD operations, allowing a user to input the name of a person they know and associate
|
|
|
+the usual create, read, update and delete operations, allowing a user to input the name
|
|
|
+of a person they know and associate
|
|
|
that name with their public key. Once the principal of a person is known, then their public
|
|
|
key can be looked up in the global blocktree. This principal needs to be communicated to the
|
|
|
user via some out-of-band method. They could receive it in an email, a text message, or embedded
|
|
|
-in a QR code. Of course this out-of-band communications needs to be authenticated, otherwise
|
|
|
+in a QR code. Of course this out-of-band communication needs to be authenticated, otherwise
|
|
|
it would be easy to fool the user into associating an attacker's key for the person.
|
|
|
|
|
|
The user now has a way of associating a blocktree with the name of this person. However, the
|
|
|
-root public key of this block tree is not enough to establish secure communications, because
|
|
|
-the root private key is not avialable to every node in the person's blocktree. In particular
|
|
|
-it would be inadvisable for the root private key to be stored on a user's mobile device,
|
|
|
-so a message encrypted using the root public key would not be readable on this device. To
|
|
|
-address this mailbox blocks are created.
|
|
|
-
|
|
|
-For each contact two blocks are created: the inbox and the outbox. The user creates a readcap
|
|
|
-for the person and adds it to the outbox. The inbox is a symbolic link to the user's outbox in
|
|
|
-the blocktree of the person. Thus each person can read messages sent to them using their readcap,
|
|
|
-and they can write messages into their own blocktree where the other party knows how to find them.
|
|
|
-Now to solve the problem outlined above, the person needs to give permission to a node in their
|
|
|
-blocktree in order for it to read messages from the user. It does this by creating a new readcap
|
|
|
-for the node, containing the block key in the readcap it was issued. It then stores that
|
|
|
-readcap in the symbolic link in its blocktree (the inbox for the user). When the person uses
|
|
|
-their mobile to read messages from the user, it looks at the union of the readcaps in the
|
|
|
-symbolic link and the inbox. Once it finds the one for its principal, it decryptes the block key
|
|
|
-and uses it to decypher the contents of the inbox.
|
|
|
-
|
|
|
-In addition to being able to check its inbox for messages, the person also receives a blocktree
|
|
|
-message from the user when a new message is sent. This means that the person doesn't need to
|
|
|
-constantly poll the inbox to see if it has new messages, it can be assured it will be
|
|
|
-notified.
|
|
|
+root public key of this blocktree is not enough to establish secure communications, because
|
|
|
+the root private key is not available to every node in the person's blocktree. In particular
|
|
|
+it would be inadvisable for the root private key to be stored on a user's mobile device. To
|
|
|
+address this mailbox directories are created.
|
|
|
+
|
|
|
+For each contact two directories are created: the inbox and the outbox. The user creates a readcap
|
|
|
+for another user's root key and adds it to the outbox. The inbox is a symbolic link to the user's
|
|
|
+outbox in the blocktree of the other user. Thus each user
|
|
|
+can write messages into their own blocktree at a location where the other party knows how
|
|
|
+to find them. But, in order for a node to read these messages it requires a its own readcap. Only the root
|
|
|
+nodes can issue this readcap as only they have access to the root key. Once permission has been
|
|
|
+granted to a node, a root node can use the root key to decrypt the readcap issued to it, and then
|
|
|
+encrypt it using the public key of the node. The resulting readcap is then stored in the header
|
|
|
+of the inbox.
|
|
|
+
|
|
|
+In addition to being able to check the inbox for mail, a blocktree message is sent to the receiving
|
|
|
+blocktree when mail is sent. This message may contain the entire contents of the mail, if the
|
|
|
+contents are short enough. But if the mail contains a lot of data, for instance a video, then
|
|
|
+the message just serves as a notification that new mail is available.
|
|
|
|
|
|
\subsection{Social Network}
|
|
|
Building a social network on top of the contacts app is fairly straight-forward. Once
|
|
@@ -443,7 +447,7 @@ I hope this example shows that having a standard format for data and the federat
|
|
|
can provide designers with much greater flexibility, even if they do not care about decentralization
|
|
|
or their user's privacy.
|
|
|
|
|
|
-\subsection{The Open Metaverse}
|
|
|
+\subsection{The Metaverse}
|
|
|
As a final example I'd like to consider a platform for recording spacial information. The key insight
|
|
|
that enables this is very general: blocktree enables the creation of distributed tree-like data structures.
|
|
|
For instance, its straight forward to imagine creating a distributed hashtable implemented as a red-black tree.
|