Cleaned up ContractUpgradeFlow API (#1591)

This commit is contained in:
Shams Asari
2017-09-22 10:17:01 +01:00
committed by josecoll
parent a11577840c
commit 11be5dd417
9 changed files with 107 additions and 97 deletions

View File

@ -8,7 +8,6 @@ import net.corda.confidential.SwapIdentitiesFlow
import net.corda.confidential.SwapIdentitiesHandler
import net.corda.core.concurrent.CordaFuture
import net.corda.core.flows.*
import net.corda.core.flows.ContractUpgradeFlow.Acceptor
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
@ -35,6 +34,7 @@ import net.corda.core.utilities.debug
import net.corda.node.internal.classloading.requireAnnotation
import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.internal.cordapp.CordappProvider
import net.corda.node.services.ContractUpgradeHandler
import net.corda.node.services.FinalityHandler
import net.corda.node.services.NotaryChangeHandler
import net.corda.node.services.api.*
@ -372,7 +372,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
private fun installCoreFlows() {
installCoreFlow(FinalityFlow::class, ::FinalityHandler)
installCoreFlow(NotaryChangeFlow::class, ::NotaryChangeHandler)
installCoreFlow(ContractUpgradeFlow.Initiator::class, ::Acceptor)
installCoreFlow(ContractUpgradeFlow.Initiate::class, ::ContractUpgradeHandler)
installCoreFlow(SwapIdentitiesFlow::class, ::SwapIdentitiesHandler)
}

View File

@ -54,8 +54,7 @@ class CordappLoader private constructor(private val cordappJarPaths: List<URL>)
/**
* Creates a dev mode CordappLoader intended to only be used in test environments.
*
* @param scanPackage Resolves the JARs that contain scanPackage and use them as the source for
* the classpath scanning.
* @param scanPackages list of packages to scan.
*/
fun createDevMode(scanPackages: String): CordappLoader {
val paths = scanPackages.split(",").flatMap { scanPackage ->
@ -123,7 +122,11 @@ class CordappLoader private constructor(private val cordappJarPaths: List<URL>)
}
val found = scanResult.getClassesWithAnnotation(FlowLogic::class, StartableByRPC::class).filter { it.isUserInvokable() }
val coreFlows = listOf(ContractUpgradeFlow.Initiator::class.java)
val coreFlows = listOf(
ContractUpgradeFlow.Initiate::class.java,
ContractUpgradeFlow.Authorise::class.java,
ContractUpgradeFlow.Deauthorise::class.java
)
return found + coreFlows
}

View File

@ -1,8 +1,13 @@
package net.corda.node.services
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.UpgradeCommand
import net.corda.core.contracts.UpgradedContract
import net.corda.core.contracts.requireThat
import net.corda.core.flows.*
import net.corda.core.identity.Party
import net.corda.core.internal.ContractUpgradeUtils
import net.corda.core.transactions.SignedTransaction
// TODO: We should have a whitelist of contracts we're willing to accept at all, and reject if the transaction
@ -41,3 +46,27 @@ class NotaryChangeHandler(otherSideSession: FlowSession) : AbstractStateReplacem
}
}
}
class ContractUpgradeHandler(otherSide: FlowSession) : AbstractStateReplacementFlow.Acceptor<Class<out UpgradedContract<ContractState, *>>>(otherSide) {
@Suspendable
override fun verifyProposal(stx: SignedTransaction, proposal: AbstractStateReplacementFlow.Proposal<Class<out UpgradedContract<ContractState, *>>>) {
// Retrieve signed transaction from our side, we will apply the upgrade logic to the transaction on our side, and
// verify outputs matches the proposed upgrade.
val ourSTX = serviceHub.validatedTransactions.getTransaction(proposal.stateRef.txhash)
requireNotNull(ourSTX) { "We don't have a copy of the referenced state" }
val oldStateAndRef = ourSTX!!.tx.outRef<ContractState>(proposal.stateRef.index)
val authorisedUpgrade = serviceHub.contractUpgradeService.getAuthorisedContractUpgrade(oldStateAndRef.ref) ?:
throw IllegalStateException("Contract state upgrade is unauthorised. State hash : ${oldStateAndRef.ref}")
val proposedTx = stx.tx
val expectedTx = ContractUpgradeUtils.assembleBareTx(oldStateAndRef, proposal.modification, proposedTx.privacySalt).toWireTransaction()
requireThat {
"The instigator is one of the participants" using (initiatingSession.counterparty in oldStateAndRef.state.data.participants)
"The proposed upgrade ${proposal.modification.javaClass} is a trusted upgrade path" using (proposal.modification.name == authorisedUpgrade)
"The proposed tx matches the expected tx for this upgrade" using (proposedTx == expectedTx)
}
ContractUpgradeFlow.verify(
oldStateAndRef.state,
expectedTx.outRef<ContractState>(0).state,
expectedTx.toLedgerTransaction(serviceHub).commandsOfType<UpgradeCommand>().single())
}
}

View File

@ -46,7 +46,7 @@ class CordappLoaderTest {
val actualCordapp = actual.first()
assertThat(actualCordapp.contractClassNames).isEqualTo(listOf("net.corda.finance.contracts.isolated.AnotherDummyContract"))
assertThat(actualCordapp.initiatedFlows).isEmpty()
assertThat(actualCordapp.rpcFlows).isEqualTo(listOf(loader.appClassLoader.loadClass("net.corda.core.flows.ContractUpgradeFlow\$Initiator").asSubclass(FlowLogic::class.java)))
assertThat(actualCordapp.rpcFlows).contains(loader.appClassLoader.loadClass("net.corda.core.flows.ContractUpgradeFlow\$Initiate").asSubclass(FlowLogic::class.java))
assertThat(actualCordapp.services).isEmpty()
assertThat(actualCordapp.plugins).hasSize(1)
assertThat(actualCordapp.plugins.first().javaClass.name).isEqualTo("net.corda.finance.contracts.isolated.DummyPlugin")