Resource-Oriented Programming: A Better Model for Digital Ownership

September 21, 2020

Engineers often use the term “ownership” as a metaphor for keeping track of which code is responsible for managing some kind of data structure or system resource. This metaphor is most common in programming environments where memory management isn’t fully abstracted away from the programmer, and saying the code “owns” an object is saying that code has to manage and free the memory allocated to that object.

While existing programming environments can be used to keep track of the ownership of assets, they are most typically used in scenarios where they are reflecting ownership rather than defining it directly. Public blockchains like Flow are unique in that they are explicitly designed to manage ownership of digital assets with real scarcity and full access control. Digital assets on public blockchains like Bitcoin or Flow behave like physical assets: they cannot be copied or counterfeited, only moved. 

As blockchains have evolved, the mechanisms for representing ownership have changed. Bitcoin was built using an ownership model defined by “unspent transaction outputs” or UTXOs. While highly efficient, the UTXO model is complex and can create some unusual edge cases, so Ethereum adopted a more flexible ledger model. Unfortunately, the flexibility of the Ethereum model makes it difficult to write safe and secure smart contracts, leading to hacks that have cost the cryptocurrency community $100M+ in lost funds. 


Introducing Cadence, the first high-level resource-oriented programming language 

Last year, the Flow team was investigating the use of Linear Types in the context of blockchains, following academic research into better smart contract languages. At just about the same time, the Libra team came out with their initial announcement, including the technical details of the MoveVM.

The Libra team defined a new programming model for Move based around a new ownership model inspired by Linear Types: resources. Resources are a new way of representing asset ownership and the properties of crypto-enabled digital assets directly in the programming language. From the Move introduction:

The key feature of Move is the ability to define custom resource types. Resource types are used to encode safe digital assets with rich programmability.

We were so struck by the power of Resource-Oriented Programming that it’s one of the defining features of Cadence, the smart contract programming language we developed for Flow. 

As the first high-level resource-oriented programming language, Cadence has a comfortable, ergonomic syntax making it very easy to read. It uses a strong, static type system to minimize runtime errors, and allows all methods, interfaces, and transactions to include pre- and post-conditions to enforce expected behaviour. We think this will result in a language that is easier to learn, significantly easier to audit, and ultimately much more productive than any current alternatives. 

You can experiment with Cadence on the Flow Playground, available today at play.onflow.org.

How does resource-oriented programming work?  

Resources unlock richer composability options than EVM or WASM, and are a perfect fit for digital assets. Labelling something as a “Resource” tells the programming environment that this data structure represents something of tangible value and that all code that interacts with that data structure needs to follow a series of special rules that will maintain the value of that data structure.

So, what are these rules?

  1. Each Resource exists in exactly one place at any given time. Resources can’t be duplicated or accidentally deleted, either through programming error or malicious code.
  2. Ownership of a Resource is defined by where it is stored. There is no central ledger that needs to be consulted to determine ownership.
  3. Access to methods on a Resource is limited to the owner. For example, only the owner of a CryptoKitty can initiate a breeding operation that will lead to the birth of a new Kitty.

It’s not enough for the special status of Resource objects to only be enforced by the compiler. The rules must be enforced while the code is actually being executed on-chain; it would be too easy for an attacker to use a compromised copy of the compiler that bypasses the rules that keep resources secure

However! If you do enforce those rules properly, you can allow the most important asset of the network – the native token – to be safely stored inside data structures controlled by user-submitted code. Powerful!


Show me an example!

The easiest way to think about Resources is to think through an example using a Non-Fungible Token (NFT), such as a CryptoKitty. Each CryptoKitty is indivisible, uncopyable, and can have a single direct owner, which matches directly with the Resource programming construct.

In a Ledger model like Ethereum, all of the CryptoKitties are stored inside a single smart contract as a giant list. The ownership of each Kitty is tracked by storing the account ID of each owner in a central ledger, and the only way to change ownership of a Kitty is to contact that central ledger and ask it to update the account ID associated with that Kitty.

contract KittyLedger {
\tstruct Kitty {}\n
\tpriv let kitties: {Int: Kitty}\n
\tfun transfer(kittyId: Int, newOwner: AccountId) {
\t\tif (msg.sender == kitties[kittyId].owner) {
\t\t\tkitties[kittyId].owner = newOwner
\t\t}
\t}
}\n
transaction(signer: Account) {
\t// tells the central ledger to assign ownership of
\t// myKittyId to a different account
\tcentralKittyLedger.transfer(myKittyId, receiverAccountId)
}


In the Resource Model, the Kitty itself is represented as a Resource object and it is stored directly in the account that owns it. Much like in the physical world, ownership is represented by possession. You don’t need to look in a central ledger to see if you own something, you either have it stored in your account or you don’t. And if you have it, you can transfer it or otherwise control it, and if you don’t have it, there’s no way to capture or alter it.

contract CryptoKitties {
\t// Accounts store a collection in their account storage
\tresource KittyCollection {
\t\t// Each collection has functions to
\t\t// move stored resources in and out
\t\tfun withdraw(kittyId: int): CryptoKitty
\t\tfun deposit(kitty: CryptoKitty)
\t}\n
\t// The resource objects that can be stored in the collection
\tresource CryptoKitty {}
}\n
transaction(signer: Account) {
\t// Removes the Kitty from signer's collection, and stores it
\t// temporarily on the stack.
\tlet theKitty <- signer.kittyCollection.withdraw(kittyId: myKittyId)\n
\t// Moves the Kitty into the receiver's account
\tlet receiver = getAccount(receiverAccountId)
\treceiver.kittyCollection.deposit(kitty: <-theKitty)
}


Note: for the sake of staying focused on the differences between the ledger and direct-ownership models, both examples above ignore issues like access control, defining every variable, and other factors that live code would need to worry about.

Why Resources Matter

Other than the obvious win of including abstractions for managing ownership, there are several other secondary benefits that come from using Resources, each of which are quite significant on their own:

State Rent

Scalable smart contract platforms need some way to to charge “state rent” so that data stored on the blockchain is either paid for or removed from the working set.

With the ledger model, it’s hard to know who should pay this rent. For example, the CryptoKitties contract represents tens of thousands of players with almost two million Kitties and over 111MB of on-chain data. Ethereum provides no way of fairly charging rent to all those Kitty owners.

Using a direct ownership model via Resource Types, each Kitty would be stored inside the account of their owner, alongside that person’s other assets. The responsibility of who needs to pay for that storage is clear. What’s more, individual users (assisted by their client software) can archive unused assets to reduce their costs and reduce load on the network.

Flexible Ownership

Using a ledger model for ownership limits the kinds of owner relationships available. For example, ERC-721 defines an ownership model for NFTs that assumes that only Ethereum addresses can own an NFT. However, the idea of an asset itself owning other assets (like a CryptoKitty owning a nifty pair of sunglasses) is very interesting in some use cases, and required a new specification (ERC-998) to be created. ERC-998 is very powerful, but it’s also much more complicated than ERC-721. Implementing it properly is very difficult, and retroactively applying its features to existing ERC-721 assets is effectively impossible.

The direct-ownership model allows any asset modeled using Resource Types to be securely stored anywhere in the system, including “inside of” other assets, where appropriate. All of the security and value guarantees can be maintained by the runtime system, while unlocking creative flexibility for developers without undue complexity.

Capability-Based Security

Resource Types provide all of the guarantees required to implement the concept of “Capabilities” from the Capability-Based Security model. Capabilities are a powerful mechanism for defining secure systems, and can make it much easier to adhere to the Principle of Least Privilege (a common best-practice in security systems).

Capability-Based Security models are generally considered to be much easier to reason about (which enhances security) while allowing greater flexibility.

Eliminating Reentrancy Bugs

The most famous smart contract bug in Ethereum’s history was due to a reentrancy problem, and Solidity developers need to constantly be vigilant against introducing logic flow that is susceptible to reentrancy attacks.

Fortunately, methods defined on Resource objects can’t be victims of any reentrancy exploits.

This seems like a bold claim! However, it follows naturally from how Resources are defined: Each Resource has a single owner, and only the owner of a Resource can call the methods on it. If a Resource method is “on the stack”, we know that the single ownership reference to that object is already in use; it’s simply not possible for any code that we call from inside that method – however indirectly – to get a second reference to that object to make a reentrant method call.

Of course, working directly with global shared state (bypassing the use of Resource objects) could still create code that is vulnerable to reentrancy bugs. This is why the idiomatic Cadence style is to use Resources for all shared state; smart contract authors who embrace Resources need never think about reentrancy bugs again!

More about resources

To dig deeper into resources and resource-oriented programming, you can:

Read More

Can DeFi defy centralized finance?

Meet the Team: Kim Cope on Bringing the Mainstream to Blockchain

Interact with Flow using Ruby