Add support for contract upgrades (#165)

* Add support for contract upgrades
* Add interface for the upgraded contract to implement, which provides functionality for upgrading legacy states.
* Add shared upgrade command and verification code for it.
* Add DummyContractV2 to illustrate what an upgraded contract looks like.
* Add new functions to vault service to support upgrading state objects.
* Add contract upgrade flow
This commit is contained in:
Patrick Kuo
2017-02-09 17:14:31 +00:00
committed by GitHub
parent a61c767505
commit c054ffe719
22 changed files with 496 additions and 42 deletions

View File

@ -58,7 +58,7 @@ class RaftNotaryServiceTests : NodeBasedTest() {
private fun issueState(node: AbstractNode, notary: Party, notaryKey: KeyPair): StateAndRef<*> {
return databaseTransaction(node.database) {
val tx = DummyContract.generateInitial(node.info.legalIdentity.ref(0), Random().nextInt(), notary)
val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.legalIdentity.ref(0))
tx.signWith(node.services.legalIdentityKey)
tx.signWith(notaryKey)
val stx = tx.toSignedTransaction()

View File

@ -260,6 +260,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
false
}
startMessagingService(CordaRPCOpsImpl(services, smm, database))
services.registerFlowInitiator(ContractUpgradeFlow.Instigator::class) { ContractUpgradeFlow.Acceptor(it) }
runOnStop += Runnable { net.stop() }
_networkMapRegistrationFuture.setFuture(registerWithNetworkMapIfConfigured())
smm.start()

View File

@ -2,6 +2,7 @@ package net.corda.node.internal
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.UpgradedContract
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic
@ -102,6 +103,8 @@ class CordaRPCOpsImpl(
override fun attachmentExists(id: SecureHash) = services.storageService.attachments.openAttachment(id) != null
override fun uploadAttachment(jar: InputStream) = services.storageService.attachments.importAttachment(jar)
override fun authoriseContractUpgrade(state: StateAndRef<*>, upgradedContractClass: Class<UpgradedContract<*, *>>) = services.vaultService.authoriseContractUpgrade(state, upgradedContractClass)
override fun deauthoriseContractUpgrade(state: StateAndRef<*>) = services.vaultService.deauthoriseContractUpgrade(state)
override fun currentNodeTime(): Instant = Instant.now(services.clock)
@Suppress("OverridingDeprecatedMember", "DEPRECATION")
override fun uploadFile(dataType: String, name: String?, file: InputStream): String {

View File

@ -337,14 +337,27 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT
return Vault.Update(consumedStates, ourNewStates.toHashSet())
}
private fun isRelevant(state: ContractState, ourKeys: Set<PublicKey>): Boolean {
return if (state is OwnableState) {
state.owner.containsAny(ourKeys)
} else if (state is LinearState) {
// It's potentially of interest to the vault
state.isRelevant(ourKeys)
} else {
false
// TODO : Persists this in DB.
private val authorisedUpgrade = mutableMapOf<StateRef, Class<UpgradedContract<*, *>>>()
override fun getAuthorisedContractUpgrade(ref: StateRef) = authorisedUpgrade[ref]
override fun authoriseContractUpgrade(stateAndRef: StateAndRef<*>, upgradedContractClass: Class<UpgradedContract<*, *>>) {
val upgrade = upgradedContractClass.newInstance()
if (upgrade.legacyContract.javaClass != stateAndRef.state.data.contract.javaClass) {
throw IllegalArgumentException("The contract state cannot be upgraded using provided UpgradedContract.")
}
authorisedUpgrade.put(stateAndRef.ref, upgradedContractClass)
}
}
override fun deauthoriseContractUpgrade(stateAndRef: StateAndRef<*>) {
authorisedUpgrade.remove(stateAndRef.ref)
}
private fun isRelevant(state: ContractState, ourKeys: Set<PublicKey>) = when (state) {
is OwnableState -> state.owner.containsAny(ourKeys)
// It's potentially of interest to the vault
is LinearState -> state.isRelevant(ourKeys)
else -> false
}
}

View File

@ -150,7 +150,7 @@ class NotaryChangeTests {
}
fun issueState(node: AbstractNode, notaryNode: AbstractNode): StateAndRef<*> {
val tx = DummyContract.generateInitial(node.info.legalIdentity.ref(0), Random().nextInt(), notaryNode.info.notaryIdentity)
val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0))
val nodeKey = node.services.legalIdentityKey
tx.signWith(nodeKey)
val notaryKeyPair = notaryNode.services.notaryIdentityKey
@ -178,7 +178,7 @@ fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode, notaryNode: A
}
fun issueInvalidState(node: AbstractNode, notary: Party): StateAndRef<*> {
val tx = DummyContract.generateInitial(node.info.legalIdentity.ref(0), Random().nextInt(), notary)
val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.legalIdentity.ref(0))
tx.setTime(Instant.now(), 30.seconds)
val nodeKey = node.services.legalIdentityKey
tx.signWith(nodeKey)

View File

@ -140,7 +140,7 @@ class NotaryServiceTests {
}
fun issueState(node: AbstractNode): StateAndRef<*> {
val tx = DummyContract.generateInitial(node.info.legalIdentity.ref(0), Random().nextInt(), notaryNode.info.notaryIdentity)
val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0))
val nodeKey = node.services.legalIdentityKey
tx.signWith(nodeKey)
val notaryKeyPair = notaryNode.services.notaryIdentityKey

View File

@ -86,7 +86,7 @@ class ValidatingNotaryServiceTests {
}
fun issueState(node: AbstractNode): StateAndRef<*> {
val tx = DummyContract.generateInitial(node.info.legalIdentity.ref(0), Random().nextInt(), notaryNode.info.notaryIdentity)
val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0))
val nodeKey = node.services.legalIdentityKey
tx.signWith(nodeKey)
val notaryKeyPair = notaryNode.services.notaryIdentityKey