Contract Upgrades & Constraints in V3
With Blockchain and smart contracts being such uncharted territory, it’s inevitable that at some point we’ll find our contracts contain bugs (remember the DAO or Parity Wallet hack, anyone?). Or perhaps we didn’t capture all the functional or business requirements, or the regulatory landscape changed.
As such, we’ll need to look to our old friend, the upgrade.
I want to spend most of this post talking about the new upgrade functionality in Corda V3, but it’s almost obligatory to first talk about how Bitcoin and Ethereum carry out upgrades.
Bitcoin being purely a distributed ledger for recording balances with very primitive smart contract functionality, the protocol is decided entirely by the core code that everyone runs. As and when the development team release upgrades, users of Bitcoin vote on the code change by either updating to the new version or not. If enough users update, that becomes the new accepted network. Upgrading this way is perhaps, in theory, the simplest method in the blockchain world as everyone is fundamentally running the same software. In practice, it’s one of the hardest.
Ethereum with its ‘Smarter’ contracts is somewhat closer to Corda. Once a smart contract is deployed on the Ethereum blockchain, it is final and cannot change — essentially enforcing the idea that “code is law”. Typical workarounds for this make use of a versioning system, whereby another contract sits in front of the actual contract containing the business logic and is used to forward requests to the correct version of the contract.
Now, if you’re reading this, you’re probably familiar with states and contracts in Corda and how how Corda uses contracts to separate the rules of how a state can change over time from its definition. I would probably take a punt you also know Corda states only evolve (or to be more precise, consumed/created) once verification has taken place within a transaction. Right?
For many reasons, multiple implementations of a contract with the same method signature and enclosing class can exist at any one time. For example, on finding a bug within the current implementation of a cash contract in a two-party network with Alice and Bob. Alice wishes to upgrade to a fixed version immediately while Bob must first run through a series of internal testing procedures which could take days; we now have two versions on the network (Thanks, Bob!).
In contrast to other blockchains, Corda has two built-in capabilities to allow for easy contract upgrades, by leveraging the ‘contract constraints’ functionality. If ‘contract constraints’ are a new concept to you, think of them as a means to constrain which implementations of a contract from the universe of contract implementations can be used. The upgrade routes are termed explicit and implicit.
The explicit upgrade is the heavyweight method of upgrading, and one Corda has supported for a while. Typically, you will have employed the hash constraint in your code. This is where exactly one hash of a CorDapp JAR containing the contract and state is specified within the transaction, and any transactions consuming those states can only use the CorDapp (and consequently contract version) referenced by that hash. As such, the only way to upgrade is via the explicit upgrade route.
The explicit upgrade allows you to upgrade states regardless of their constraints. All nodes involved in the state with the contract are required to authorise manually and then sign the upgrade via the ContractUpgradeFlow.
If you’re interested in learning more about the explicit form of upgrade, take a look at the tutorial here
Explicitly upgrading where we have hash constraints is powerful functionality. But let’s extend our example to a network with hundreds, or even thousands of nodes, it starts to become somewhat cumbersome — you need to start thinking about coordinating between numerous entities as every node needs to be part of the upgrade flow.
What if we could defer which implementations of a contract on a business network are valid? Corda V3 introduces precisely this functionality with the zone constraint allowing for implicit upgrades of contracts. We no longer need
to force a simultaneous update or wait for all nodes to agree, each node can now upgrade individually.
The zone operator maintains a mapping of contract class names to approved hashes of JARs that can contain that class known as the zone whitelist. Each node receives this as part of the network parameters and will receive notifications when a new version of a contract class is made available.
Any versions of contract class on the zone whitelist are deemed to be compatible and any nodes running the CorDapp containing the contract can be allowed to continue to transact. This gives ultimate flexibility as a node can effectively hold off updating to a new version until it’s ready to upgrade.
There is no requirement to support multiple versions of a CorDapp; it could be the case that one version replaces another. The node then has the option to upgrade “just in time” when a transaction needs to take place.
Should a node run an unsupported contract, it will effectively stop being part of the network, as they are ineligible to transact. Currently, the zone whitelist is append-only, so adding logic to your flows to only transact with nodes using the latest version of a contract on the whitelist is one way of enforcing upgrades at the individual node level.
Both zone and hash constraints form the new default constraint. If the zone operators whitelist already contains our contract class, we default to zone constraints. Otherwise, the transaction is hash constrained.
A quick usages lookup of the AttachmentConstraint interface shows it’s implemented by AutomaticHashConstraint, WhitelistedByZoneAttachmentConstraint, HashAttachmentConstraint and AlwaysAcceptAttachmentConstraint — providing more flexibility than any other platform.
So, what’s still to come?
The zone constraints feature is part of the journey to what will be the most flexible, smoothest upgrade functionality within the blockchain space — the Signature constraint. A specified identity signs a JAR, deeming compatible all versions signed by the same signature.
A word of advice
For old versions of contracts, the advice right now is to keep hold of them as they may be needed to verify backdated transactions and you’ll want the correct version to do this. Support will be added to streamline the process of archiving these and pulling the correct version for signing in a future release.