An important aspect of Corda is the ability to reissue states for assets that have a long transaction history. Let’s take a look at what it is and how it’s implemented!
When using the Unspent Transaction Output (UTXO) model, transactions on Corda can develop a long history of states. Although this can be useful in some scenarios, it can become cumbersome when the backchain becomes computationally expensive to resolve. There is also privacy to consider as the transaction backchain is shared with the new recipient.
From version 4.7 onwards, Corda has a built-in mechanism for handling state reissuance. State reissuance allows a party to go back to the initial issuer of an asset, show that they have the rights to all states relevant to that asset, and request that the asset be recreated without the long backchain.
A great example of this in action is the CandyShop sample.
In this example, there are four parties:
- Alice
- Bob
- The CandyShop
- The Notary
The CandyShop issues 'CandyCoupon'
s as a fungible token for purchasing 'Candy'
from the CandyShop. These 'CandyCoupon'
s are issued to a holder party, in this case, either Alice or Bob.
Alice and Bob can transact 'CandyCoupon'
s between themselves, or they can use them to purchase 'Candy'
states from the CandyShop. A 'CandyCoupon'
represents a number of 'Candy'
s that the coupon can buy. In this example, it is possible to split a coupon into multiple coupons, each representing a smaller number of 'Candy'
s.
There are a number of different transactions that can occur on a '
CandyCoupon'
. As more and more transactions happen, the
history of transactions stored in its backchain gets longer and longer.'CandyCoupon'
You can see this backchain by calling:
flow start GetTransactionBackChain transactionId:
This command returns a set of transaction IDs that represent each transaction that has modified the 'CandyCoupon'
from its issuance until the present state. You can analyze these transaction IDs further by running:
run internalFindVerifiedTransaction txnId:
This will show all the information about a transaction and displays how in-depth the history is for states stored in a chain of Corda transactions.
Let’s say that Alice wants to transact with Bob. However, she doesn’t want him to receive the entire history of the 'CandyCoupon'
she is sending, either for privacy or for computational resource reasons. Alice can ask the CandyShop to reissue the 'CandyCoupon'
before she sends it to Bob.
To do this, Alice requests a reissuance of the relevant 'CandyCoupon'
state she intends to transact from the CandyShop by running the `RequestReIssuance` flow. She must then prove the validity of the state inputs to the 'CandyCoupon'
the 'RequestReIssuanceAndShareRequiredTransactions'
flow:
class RequestReissuanceAndShareRequiredTransactions(
private val issuer: AbstractParty,
private val stateRefsToReissue: List,
private val assetIssuanceCommand: CommandData,
private val extraAssetIssuanceSigners: List = listOf(), // issuer is always a signer
private val requester: AbstractParty? = null // requester needs to be provided when using accounts
) : FlowLogic() where T: ContractState
The CandyShop responds, either by rejecting the request using the 'RejectReIssuanceRequest'
flow or accepting the request using the 'ReIssueStates'
flow:
class RejectReIssuanceRequest(
private val reIssuanceRequestStateAndRef: StateAndRef
): FlowLogic() where T: ContractState
class ReIssueStates(
private val reIssuanceRequestStateAndRef: StateAndRef,
private val issuerIsRequiredExitCommandSigner: Boolean = true
): FlowLogic() where T: ContractState
The CandyShop should only accept the request if it has received proof that a transaction exists with the output state that Alice wishes to have reissued, and that the CandyShop has not already reissued the state.
If the CandyShop accepts Alice’s request for reissuance, it will issue a new state that satisfies the request as an output. New states are encumbered and `ReissuanceLock` states are issued to indicate that the newly reissued states are locked.
To unlock the state, Alice must exit the original state from the vault and then provide the exit transaction IDs to the 'UnlockReissuedStates'
flow. Only once it is confirmed that the original 'CandyCoupon'
has been destroyed, can the re-issued version become valid and transactable. Think you can “destroy” the 'CandyCoupon'
by simply spending the coupon? Think again, the transaction to destroy will only be valid to unlock if it doesn’t produce any outputs, so no Candy for you.
Now you have destroyed the original state and unlocked the reissued state by proving the destruction of the original with the 'UnlockReissuedStates'
flow.
To see what the backchain of the newly reissued state, use the following command, making sure to use the new transaction ID:
flow start GetTransactionBackChain transactionId:
The backchain consists of just three transactions representing the 'RequestReissuance'
, the 'ReissueStates'
, and the 'UnlockReissuedStates'
. You now have a new 'CandyCoupon'
that you can send to Bob without him ever knowing what you were doing with the candy beforehand.