mirror of
https://github.com/corda/corda.git
synced 2025-06-15 21:58:17 +00:00
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:
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user