2
votes

Context: We have recently been making changes to the contract code in the same CorDapp before we realised that you can't just swop out the old CorDapp with a new CorDapp plugin given the attachment constraint placed on transactionstate. As such old state objects which references the old attachment hash will throw out an error of ''ContractConstraintRejection'' when a new CorDapp JAR is put in placed of the old one.

Resolution: Upgrade the contract in the following steps -

  1. Amend the original state / contract to reflect the new desired implementation
  2. Test the new state and contract
  3. Stop all nodes on the business network
  4. Produce a JAR file and distribute it to all required parties
  5. Start all nodes on the network
  6. All nodes should run the contract upgrade authorisation flow for each state that requires updating
  7. For each state, one node should run the contract upgrade initiation flow

Guidance required:

  • On step 4, how do we produce just a JAR file that contains only the updated new contract? I had clone a template and added just a contract code + api calls to do the upgrade, compiled the code and added to a node. When i restart the node, I can't seem to access those api in the new JAR to do the upgrade.
  • On step 6 & 7, I have tried writing an web API to call the authorisation in all the nodes, followed by another call to ''upgrade flow''. I kept hitting the wall. If I were to just do RPC calls, is it better to run a script in IDE like this (https://github.com/ragmondo/corda-nodeinfo). Are there any examples I could reference on authorisation + calling the upgrade flow. The tutorial on the docsite is not exactly clear (https://docs.corda.net/contract-upgrade.html).
1
Thanks Ben, looking into this. For step 4, are you making sure you're listing the new API in this file: github.com/corda/cordapp-example/blob/release-V2/kotlin-source/…?Joel
Yupz I have added the manifest, but actually I wasn't sure if cloning the template to create the new contract code to upgrade is the right way to create the JAR file though? I can try again.Ben Tan
As a side note, when we are able to send the contract code as an attachment to the wire in future releases, do we still need to upgrade the contract code like this?Ben Tan
Yes, you'll still have to upgrade contract code in this way. The new contract should be packaged up into a JAR in the standard Java way.Joel

1 Answers

2
votes

Here is an example of how you'd upgrade a state's contract:

fun main(args: Array<String>) {
    require(args.size == 2) { "Usage: TemplateClient <PartyA RPC address> <PartyB RPC address>" }

    // Create a connection to PartyA and PartyB.
    val (partyAProxy, partyBProxy) = args.map { arg ->
        val nodeAddress = parse(arg)
        val client = CordaRPCClient(nodeAddress)
        client.start("user1", "test").proxy
    }

    // Issue a State that uses OldContract onto the ledger.
    val partyBIdentity = partyBProxy.nodeInfo().legalIdentities.first()
    partyAProxy.startFlowDynamic(Initiator::class.java, partyBIdentity)

    // Authorise the upgrade of all the State instances using OldContract.
    listOf(partyAProxy, partyBProxy).forEach { proxy ->
        // Extract all the unconsumed State instances from the vault.
        val stateAndRefs = proxy.vaultQuery(State::class.java).states

        // Run the upgrade flow for each one.
        stateAndRefs.forEach { stateAndRef ->
            proxy.startFlowDynamic(
                    ContractUpgradeFlow.Authorise::class.java,
                    stateAndRef,
                    NewContract::class.java)
        }
    }

    // Initiate the upgrade of all the State instances using OldContract.
    partyAProxy.vaultQuery(State::class.java).states.forEach { stateAndRef ->
        partyAProxy.startFlowDynamic(
                ContractUpgradeFlow.Initiate::class.java,
                stateAndRef,
                NewContract::class.java)
    }

    // Give the node the time to run the contract upgrade flows.
    Thread.sleep(10000)

    // Log all the State instances in the vault to show they are using NewContract.
    partyAProxy.vaultQuery(State::class.java).states.forEach { logger.info("{}", it.state) }
}

See the Contract Upgrades sample here for the full example.