As blockchains have evolved, the mechanism for representing ownership has evolved. 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 straightforward Ledger model.
When the Libra blockchain was announced, the primary interest around the project lay in the political implications of a blockchain established by Facebook, but those of us who dug into the technical details found some pretty interesting new ideas.
In particular, the Libra team defined a new programming model for their MoveVM based around a new ownership model inspired by Linear Types: Resources.
Resources are a new way of representing asset ownership directly in the programming language. 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.
Resources extend this idea so that we can leverage some of the mechanisms for managing metaphoric “ownership” in previous programming languages and use it to manage true ownership of native digital assets.
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.
The Move type system provides special safety guarantees for resources. Move resources can never be duplicated, reused, or discarded. A resource type can only be created or destroyed by the module that defines the type. These guarantees are enforced statically by the Move virtual machine [...]
The Libra currency is implemented as a resource type [and] has no special status in the language; every Move resource enjoys the same protections.
Those last two points are very important:
1. The special status of Resource objects must be enforced by the runtime (“the Move virtual machine”); if they were just a compiler abstraction it would be easy for malicious code to break the value guarantees.
2. 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!
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 {
struct Kitty {}
priv let kitties: {Int: Kitty}
fun transfer(kittyId: Int, newOwner: AccountId) {
if (msg.sender == kitties[kittyId].owner) {
kitties[kittyId].owner = newOwner
}
}
}
transaction(signer: Account) {
// tells the central ledger to assign ownership of
// myKittyId to a different account
centralKittyLedger.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 {
// Accounts store a collection in their account storage
resource KittyCollection {
// Each collection has functions to
// move stored resources in and out
fun withdraw(kittyId: int): CryptoKitty
fun deposit(kitty: CryptoKitty)
}
// The resource objects that can be stored in the collection
resource CryptoKitty {}
}
transaction(signer: Account) {
// Removes the Kitty from signer's collection, and stores it
// temporarily on the stack.
let theKitty <- signer.kittyCollection.withdraw(kittyId: myKittyId)
// Moves the Kitty into the receiver's account
let receiver = getAccount(receiverAccountId)
receiver.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.
In short, 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.
As noted in the introduction, smart contracts are uniquely suited to managing ownership of valuable assets, but most programming languages – even those designed specifically for smart contracts – don’t have any native abstractions for managing ownership. Including such an abstraction at the protocol level is obviously a big win.
But there are several other secondary benefits that come from using Resources, each of which are quite significant on their own:
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.
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.
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.
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!
Last year, the Flow development 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 did their initial announcement, including the technical details of the MoveVM.
We were so struck by the power of Resource Types that it’s one of the defining features of Cadence, Flow’s smart contract programming language. Resources unlock richer composability options than EVM or WASM, and are a perfect fit for digital assets (especially NFTs!)
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 behavior. 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