Blockchain programming is about data transformations under strong constraints. Strong constraints are a new concept, not present in traditional centralized software. It refers to the fact that in decentralized software constraints are so strong that not even the creators and maintainers of the system are able to break them. In order to implement economic and governance systems — and that’s what these systems are for — we must be able to implement strong and granular constraints at every level of our programming stack.

One difficulty in programming such systems is that it is hard to introduce modularity and reuse economically-enabled code. The main issue is that most of today’s models are unable to decouple access control from the implementation of the economic primitives. This makes either the security model or the primitives or their combination non-reusable. The goal of this post is to show how to decouple them, while preserving a reliable ability to implement highly secure economically-enabled software.

To understand the full scope of the problem, let’s examine the process of transferring tokens from owner A to owner B. There are several strong requirements that its implementation must fulfill:

The requirement that the ownership of the wallet be proven before the transfer takes place (cryptographic signature). The requirement that custody of the funds be established, in other words, that A came into possession of the tokens legally. The requirement that the transfer does not in any way modify the total amount of tokens in existence (supply invariance). The requirement that A has not performed a double-spend of his/her tokens (absence of invisible database conflicts). The requirement that A is unable to overwhelm the network with back and forth transfers of tokens without incurring a prohibitive cost.

In the effort to generalize these kinds of constraints, we notice that they fall into four broad categories: access security, local transaction validity, global transaction validity, and throughput security. Let’s go through them one by one. (Footnote: some thought leaders identify fairness as an important and separate category. I am folding it, rather sloppily, into throughput security, but admit that it may require additional thinking down the road.)

Access security forms a broad set of requirements that establish who or what can perform certain actions. Here we imagine the following examples:

Direct authentication: I use my private key to sign a transaction Delegation: I nominate another person to sign a transaction for me Autonomous Economic Agents: I nominate a piece of code to cause an action to be performed on my behalf Decentralized Agents: only a specific piece of code can cause an action

It is really important to notice that access security is orthogonal to any other constraints we are discussing here. Consequently, in programming blockchain systems it is important to decouple code that implements data mutations from what which implements access control. We will return to this topic later.

Local transaction validity is the general category of constraints that ensure that data is transformed according to a set of rules based on locally observable data. In this class are such constraints as:

Supply invariance of a fixed-supply token Accurate vote counting in triggering a governance action Accurate changes in reputation as a result of some user activity Creation and destruction of cryptocurrencies according to their underlying economic rules; and many others

Local validity is not hard. It relies on the correctness of code that performs data transformations, as long as access to the implementation data is protected. Proving code correctness is extremely important but is out of scope of this post. However, we note that reliable data hiding is an essential feature required to implement such constraints.

Ensuring global transaction validity is the goal of the consensus layer of a decentralized network. The primary purpose of this layer is to ensure that it is not possible to observe a partial state of a distributed database that contains an unresolved conflict.

Finally, throughput security is the requirement very specific to decentralized systems. In open networks, denial-of-service attacks must be reliably prevented. There are many ways this can be done:

Paying for transactions Transaction throttling based on token stake Transaction throttling based on a strong identity system

We notice that all of these requirements must necessarily apply to all participants in the network. Specifically, if we count witnesses (a.k.a “miners” or “validators”) as a special class of users of a decentralized network, we must conclude that miner-generated transactions are subject to all the same constraints as anyone else’s.

MUFL Programming Model

The ADAPT white paper presents a barebones example of implementing a wallet in MUFL. Please refer to the code and explanations in Section 4.7 (p. 25) for low-level details. This post will provide a high-level overview of our approach from the perspective of strong-constraint programming.

Layers that implement different types of constraints in ADAPT’s token and wallet infrastructure

The picture above shows all the layers involved in ADAPT’s implementation of token and wallet constraints. The consensus layer is marked in a different color because it’s a basic component of the toolkit providing general protection against hidden database conflicts (as any consensus layer should), while all other parts of this structure are implemented in MUFL.

Our goal with this architecture is to achieve modularity required for broad reuse of economic primitives. We will see that implementing different classes of constraints within different modules makes such reuse very easy.

The implementation of the token supply provides no access control mechanism. Rather, it implements a generic cell that holds tokens and exposes two types of references: a deposit reference and a withdrawal reference, which provide different types of access to cells.

Cells expose a deposit reference (a public address) and withdrawal reference (a database-only value)

Token supply is implemented as a MUFL module and exposes the TRANSFER function, which requires that the source of the transfer be a valid withdrawal reference. No operations on cells can be performed other than through these two types of references. We can easily imagine this model extended to support a variety of use cases. For example, a seller may provide their customer with a reference allowing the customer to rate the seller; an accounting mechanism may require that there exist several different kinds of deposit references permitting different types of deposit transactions (one for the supplier, one for the customer); an implementation of a voting mechanism may provide a reference that allows one to vote; and so on.

The key mechanism that allows this to work securely is that the withdrawal reference is a value that cannot be supplied from outside of the database. Consequently, wrapping it into a data structure that hides it behind a layer of code implementing access security is sufficient, as long as such hiding is reliable. To illustrate this, we now describe gates.

Gate exposes access to a withdrawal reference but only through signature verification

Here, the gate structure controls a function that calls the TRANSFER method giving it the hidden withdrawal reference to a cell. We notice that gates are completely generic. They can provide access to cell’s TRANSFER method, or to a social network’s POST method.

On the other hand, we can wrap cells inside many different types of gates, including multi-signature gates, or vote-based governance gates.

Using different types of gates to protect different cells

Finally, at the highest layer, the transaction interface is created and exposed by the database to the users through the node’s RPC interface. The transaction interface will require a transaction fee, or some other mechanism to protect from abuse, and will expose the gated variant of the TRANSFER method.

Transfer transaction exposed on the RPC interface of the database

In MUFL all of these mechanisms are implemented as modules that hide all the data that has to be protected. Even though the data can be seen by anyone, this is not sufficient for someone to abuse it. The withdrawal references are implemented as a special datatype which cannot be constructed from a number or from binary. Consequently, one cannot take possession of a withdrawal reference to someone else’s cell, unless it was provided by code inside the database.

The modules implementing different primitives are sufficiently decoupled from each other. This allows for very effective code reuse at the level of economic and security primitives.

Conclusion

I hope that the reader can now form a good sense of our approach. ADAPT is designed to provide generic cryptoeconomic programming at all layers of the blockchain development stack. The transactions committed by witnesses, their rewards, the transaction fees required from users — all these primitives can be implemented and deployed using this framework. With ADAPT, application developers are no longer constrained by one specific economic mechanism built into the lowest layer of the network. Instead, we strive to open decentralized programming as the unlimited design space that it can be.