From b63df0ea390ad2b187959703577c3da72e030fee Mon Sep 17 00:00:00 2001 From: josecoll Date: Thu, 31 Aug 2017 14:01:10 +0100 Subject: [PATCH] Vault Service API final clean-up (#1348) * Remove notify/notifyAll from public Vault Service API. * 1st pass - remove ContractUpgrade public API calls from VaultService into self contained ContractUpgradeService. * Fix compile error caused by cut'n'paste refactoring. --- ...check_output_at_the_bottom_of_console_.xml | 25 ---------------- .../kotlin/net/corda/core/node/ServiceHub.kt | 1 + .../node/services/ContractUpgradeService.kt | 30 +++++++++++++++++++ .../corda/core/node/services/VaultService.kt | 29 ------------------ .../core/flows/ContractUpgradeFlowTest.kt | 2 +- .../net/corda/node/internal/AbstractNode.kt | 2 ++ .../corda/node/internal/CordaRPCOpsImpl.kt | 4 +-- .../corda/node/services/CoreFlowHandlers.kt | 2 +- .../node/services/api/ServiceHubInternal.kt | 3 +- .../upgrade/ContractUpgradeServiceImpl.kt | 26 ++++++++++++++++ .../node/services/vault/NodeVaultService.kt | 20 +++---------- .../persistence/DBTransactionStorageTests.kt | 2 +- .../persistence/HibernateConfigurationTest.kt | 3 +- .../services/vault/NodeVaultServiceTest.kt | 2 +- .../node/testing/MockServiceHubInternal.kt | 3 ++ .../net/corda/testing/node/MockServices.kt | 4 ++- 16 files changed, 79 insertions(+), 79 deletions(-) delete mode 100644 .idea/runConfigurations/Quasar_exclude_pattern_extraction_from_node_tests__need_not_pass__check_output_at_the_bottom_of_console_.xml create mode 100644 core/src/main/kotlin/net/corda/core/node/services/ContractUpgradeService.kt create mode 100644 node/src/main/kotlin/net/corda/node/services/upgrade/ContractUpgradeServiceImpl.kt diff --git a/.idea/runConfigurations/Quasar_exclude_pattern_extraction_from_node_tests__need_not_pass__check_output_at_the_bottom_of_console_.xml b/.idea/runConfigurations/Quasar_exclude_pattern_extraction_from_node_tests__need_not_pass__check_output_at_the_bottom_of_console_.xml deleted file mode 100644 index 8c8a89624b..0000000000 --- a/.idea/runConfigurations/Quasar_exclude_pattern_extraction_from_node_tests__need_not_pass__check_output_at_the_bottom_of_console_.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt index fb882eb4ad..def8fb0424 100644 --- a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt +++ b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt @@ -46,6 +46,7 @@ interface ServiceHub : ServicesForResolution { val vaultService: VaultService val vaultQueryService: VaultQueryService val keyManagementService: KeyManagementService + val contractUpgradeService: ContractUpgradeService /** * A map of hash->tx where tx has been signature/contract validated and the states are known to be correct. diff --git a/core/src/main/kotlin/net/corda/core/node/services/ContractUpgradeService.kt b/core/src/main/kotlin/net/corda/core/node/services/ContractUpgradeService.kt new file mode 100644 index 0000000000..c29702077c --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/node/services/ContractUpgradeService.kt @@ -0,0 +1,30 @@ +package net.corda.core.node.services + +import net.corda.core.contracts.StateAndRef +import net.corda.core.contracts.StateRef +import net.corda.core.contracts.UpgradedContract + +/** + * The [ContractUpgradeService] is responsible for securely upgrading contract state objects according to + * a specified and mutually agreed (amongst participants) contract version. + * See also [ContractUpgradeFlow] to understand the workflow associated with contract upgrades. + */ +interface ContractUpgradeService { + + /** Get contracts we would be willing to upgrade the suggested contract to. */ + fun getAuthorisedContractUpgrade(ref: StateRef): Class>? + + /** + * Authorise a contract state upgrade. + * This will store the upgrade authorisation in the vault, and will be queried by [ContractUpgradeFlow.Acceptor] during contract upgrade process. + * Invoking this method indicate the node is willing to upgrade the [state] using the [upgradedContractClass]. + * This method will NOT initiate the upgrade process. To start the upgrade process, see [ContractUpgradeFlow.Instigator]. + */ + fun authoriseContractUpgrade(stateAndRef: StateAndRef<*>, upgradedContractClass: Class>) + + /** + * Authorise a contract state upgrade. + * This will remove the upgrade authorisation from the vault. + */ + fun deauthoriseContractUpgrade(stateAndRef: StateAndRef<*>) +} diff --git a/core/src/main/kotlin/net/corda/core/node/services/VaultService.kt b/core/src/main/kotlin/net/corda/core/node/services/VaultService.kt index ebd70ae93d..0b7aeb862f 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/VaultService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/VaultService.kt @@ -173,17 +173,6 @@ interface VaultService { */ val updatesPublisher: PublishSubject> - /** - * Possibly update the vault by marking as spent states that these transactions consume, and adding any relevant - * new states that they create. You should only insert transactions that have been successfully verified here! - * - * TODO: Consider if there's a good way to enforce the must-be-verified requirement in the type system. - */ - fun notifyAll(txns: Iterable) - - /** Same as notifyAll but with a single transaction. */ - fun notify(tx: CoreTransaction) = notifyAll(listOf(tx)) - /** * Provide a [CordaFuture] for when a [StateRef] is consumed, which can be very useful in building tests. */ @@ -191,24 +180,6 @@ interface VaultService { return updates.filter { it.consumed.any { it.ref == ref } }.toFuture() } - /** Get contracts we would be willing to upgrade the suggested contract to. */ - // TODO: We need a better place to put business logic functions - fun getAuthorisedContractUpgrade(ref: StateRef): Class>? - - /** - * Authorise a contract state upgrade. - * This will store the upgrade authorisation in the vault, and will be queried by [ContractUpgradeFlow.Acceptor] during contract upgrade process. - * Invoking this method indicate the node is willing to upgrade the [state] using the [upgradedContractClass]. - * This method will NOT initiate the upgrade process. To start the upgrade process, see [ContractUpgradeFlow.Instigator]. - */ - fun authoriseContractUpgrade(stateAndRef: StateAndRef<*>, upgradedContractClass: Class>) - - /** - * Authorise a contract state upgrade. - * This will remove the upgrade authorisation from the vault. - */ - fun deauthoriseContractUpgrade(stateAndRef: StateAndRef<*>) - /** * Add a note to an existing [LedgerTransaction] given by its unique [SecureHash] id * Multiple notes may be attached to the same [LedgerTransaction]. diff --git a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt index 951f28b9e3..a8e93ea575 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -79,7 +79,7 @@ class ContractUpgradeFlowTest { assertFailsWith(UnexpectedFlowEndException::class) { rejectedFuture.getOrThrow() } // Party B authorise the contract state upgrade. - b.services.vaultService.authoriseContractUpgrade(btx!!.tx.outRef(0), DummyContractV2::class.java) + b.services.contractUpgradeService.authoriseContractUpgrade(btx!!.tx.outRef(0), DummyContractV2::class.java) // Party A initiates contract upgrade flow, expected to succeed this time. val resultFuture = a.services.startFlow(ContractUpgradeFlow(atx.tx.outRef(0), DummyContractV2::class.java)).resultFuture diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index c21f7bd800..ec864c606c 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -56,6 +56,7 @@ import net.corda.node.services.statemachine.FlowStateMachineImpl import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.statemachine.flowVersionAndInitiatingClass import net.corda.node.services.transactions.* +import net.corda.node.services.upgrade.ContractUpgradeServiceImpl import net.corda.node.services.vault.HibernateVaultQueryImpl import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.VaultSoftLockManager @@ -770,6 +771,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, override val schemaService by lazy { NodeSchemaService(pluginRegistries.flatMap { it.requiredSchemas }.toSet()) } override val networkMapCache by lazy { PersistentNetworkMapCache(this) } override val vaultService by lazy { NodeVaultService(this) } + override val contractUpgradeService by lazy { ContractUpgradeServiceImpl() } override val vaultQueryService by lazy { HibernateVaultQueryImpl(database.hibernateConfig, vaultService) } diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt index e87359f0a6..d4f51ee21f 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -170,8 +170,8 @@ class CordaRPCOpsImpl( } } - override fun authoriseContractUpgrade(state: StateAndRef<*>, upgradedContractClass: Class>) = services.vaultService.authoriseContractUpgrade(state, upgradedContractClass) - override fun deauthoriseContractUpgrade(state: StateAndRef<*>) = services.vaultService.deauthoriseContractUpgrade(state) + override fun authoriseContractUpgrade(state: StateAndRef<*>, upgradedContractClass: Class>) = services.contractUpgradeService.authoriseContractUpgrade(state, upgradedContractClass) + override fun deauthoriseContractUpgrade(state: StateAndRef<*>) = services.contractUpgradeService.deauthoriseContractUpgrade(state) override fun currentNodeTime(): Instant = Instant.now(services.clock) override fun waitUntilNetworkReady() = services.networkMapCache.nodeReady override fun partyFromAnonymous(party: AbstractParty): Party? = services.identityService.partyFromAnonymous(party) diff --git a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt index bbded81fa8..d311ea4fd2 100644 --- a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt +++ b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt @@ -58,7 +58,7 @@ class ContractUpgradeHandler(otherSide: Party) : AbstractStateReplacementFlow.Ac 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(proposal.stateRef.index) - val authorisedUpgrade = serviceHub.vaultService.getAuthorisedContractUpgrade(oldStateAndRef.ref) ?: + val authorisedUpgrade = serviceHub.contractUpgradeService.getAuthorisedContractUpgrade(oldStateAndRef.ref) ?: throw IllegalStateException("Contract state upgrade is unauthorised. State hash : ${oldStateAndRef.ref}") val proposedTx = stx.tx val expectedTx = ContractUpgradeFlow.assembleBareTx(oldStateAndRef, proposal.modification, proposedTx.privacySalt).toWireTransaction() diff --git a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt index 4d2bbfccbf..260e930ce8 100644 --- a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt +++ b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt @@ -22,6 +22,7 @@ import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.messaging.MessagingService import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl import net.corda.node.services.statemachine.FlowStateMachineImpl +import net.corda.node.services.vault.NodeVaultService import net.corda.node.utilities.CordaPersistence interface NetworkMapCacheInternal : NetworkMapCache { @@ -100,7 +101,7 @@ interface ServiceHubInternal : PluginServiceHub { if (notifyVault) { val toNotify = recordedTransactions.map { if (it.isNotaryChangeTransaction()) it.notaryChangeTx else it.tx } - vaultService.notifyAll(toNotify) + (vaultService as NodeVaultService).notifyAll(toNotify) } } diff --git a/node/src/main/kotlin/net/corda/node/services/upgrade/ContractUpgradeServiceImpl.kt b/node/src/main/kotlin/net/corda/node/services/upgrade/ContractUpgradeServiceImpl.kt new file mode 100644 index 0000000000..c93a8d55da --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/upgrade/ContractUpgradeServiceImpl.kt @@ -0,0 +1,26 @@ +package net.corda.node.services.upgrade + +import net.corda.core.contracts.StateAndRef +import net.corda.core.contracts.StateRef +import net.corda.core.contracts.UpgradedContract +import net.corda.core.node.services.ContractUpgradeService + +class ContractUpgradeServiceImpl : ContractUpgradeService { + + // TODO : Persist this in DB. + private val authorisedUpgrade = mutableMapOf>>() + + override fun getAuthorisedContractUpgrade(ref: StateRef) = authorisedUpgrade[ref] + + override fun authoriseContractUpgrade(stateAndRef: StateAndRef<*>, upgradedContractClass: Class>) { + val upgrade = upgradedContractClass.newInstance() + if (upgrade.legacyContract != 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) + } +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index 023e87a1f7..9bb3249da8 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -114,7 +114,7 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT * indicate whether an update consists entirely of regular or notary change transactions, which may require * different processing logic. */ - override fun notifyAll(txns: Iterable) { + fun notifyAll(txns: Iterable) { // It'd be easier to just group by type, but then we'd lose ordering. val regularTxns = mutableListOf() val notaryChangeTxns = mutableListOf() @@ -142,6 +142,9 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT if (notaryChangeTxns.isNotEmpty()) notifyNotaryChange(notaryChangeTxns.toList()) } + /** Same as notifyAll but with a single transaction. */ + fun notify(tx: CoreTransaction) = notifyAll(listOf(tx)) + private fun notifyRegular(txns: Iterable) { fun makeUpdate(tx: WireTransaction): Vault.Update { val myKeys = services.keyManagementService.filterMyKeys(tx.outputs.flatMap { it.data.participants.map { it.owningKey } }) @@ -369,22 +372,7 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT return claimedStates } - // TODO : Persists this in DB. - private val authorisedUpgrade = mutableMapOf>>() - override fun getAuthorisedContractUpgrade(ref: StateRef) = authorisedUpgrade[ref] - - override fun authoriseContractUpgrade(stateAndRef: StateAndRef<*>, upgradedContractClass: Class>) { - val upgrade = upgradedContractClass.newInstance() - if (upgrade.legacyContract != 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) - } @VisibleForTesting internal fun isRelevant(state: ContractState, myKeys: Set): Boolean { diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt index fa40b2dc05..6fbbf5e534 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt @@ -64,7 +64,7 @@ class DBTransactionStorageTests : TestDependencyInjectionBase() { validatedTransactions.addTransaction(stx) } // Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions. - vaultService.notifyAll(txs.map { it.tx }) + (vaultService as NodeVaultService).notifyAll(txs.map { it.tx }) } } } diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt index fcf843e776..f1fd90b136 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt @@ -25,6 +25,7 @@ import net.corda.finance.schemas.SampleCashSchemaV3 import net.corda.finance.utils.sumCash import net.corda.node.services.schema.HibernateObserver import net.corda.node.services.schema.NodeSchemaService +import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.VaultSchemaV1 import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.configureDatabase @@ -88,7 +89,7 @@ class HibernateConfigurationTest : TestDependencyInjectionBase() { validatedTransactions.addTransaction(stx) } // Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions. - vaultService.notifyAll(txs.map { it.tx }) + (vaultService as NodeVaultService).notifyAll(txs.map { it.tx }) } override fun jdbcSession() = database.createSession() } diff --git a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt index cc839addf4..cdf6d06fc5 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt @@ -96,7 +96,7 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() { val originalVault = vaultSvc val originalVaultQuery = vaultQuery val services2 = object : MockServices() { - override val vaultService: VaultService get() = originalVault + override val vaultService: NodeVaultService get() = originalVault as NodeVaultService override fun recordTransactions(notifyVault: Boolean, txs: Iterable) { for (stx in txs) { validatedTransactions.addTransaction(stx) diff --git a/test-utils/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt b/test-utils/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt index ff70a21606..d8ce9c23f7 100644 --- a/test-utils/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt +++ b/test-utils/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt @@ -42,6 +42,7 @@ open class MockServiceHubInternal( val scheduler: SchedulerService? = null, val overrideClock: Clock? = NodeClock(), val schemas: SchemaService? = NodeSchemaService(), + val customContractUpgradeService: ContractUpgradeService? = null, val customTransactionVerifierService: TransactionVerifierService? = InMemoryTransactionVerifierService(2) ) : ServiceHubInternal { override val vaultQueryService: VaultQueryService @@ -50,6 +51,8 @@ open class MockServiceHubInternal( get() = customTransactionVerifierService ?: throw UnsupportedOperationException() override val vaultService: VaultService get() = customVault ?: throw UnsupportedOperationException() + override val contractUpgradeService: ContractUpgradeService + get() = customContractUpgradeService ?: throw UnsupportedOperationException() override val keyManagementService: KeyManagementService get() = keyManagement ?: throw UnsupportedOperationException() override val identityService: IdentityService diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt b/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt index ab496c749a..3f0bdc17f5 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -26,6 +26,7 @@ import net.corda.node.services.persistence.InMemoryStateMachineRecordedTransacti import net.corda.node.services.schema.HibernateObserver import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.transactions.InMemoryTransactionVerifierService +import net.corda.node.services.upgrade.ContractUpgradeServiceImpl import net.corda.node.services.vault.HibernateVaultQueryImpl import net.corda.node.services.vault.NodeVaultService import net.corda.node.utilities.CordaPersistence @@ -123,7 +124,7 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub { validatedTransactions.addTransaction(stx) } // Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions. - vaultService.notifyAll(txs.map { it.tx }) + (vaultService as NodeVaultService).notifyAll(txs.map { it.tx }) } override val vaultQueryService: VaultQueryService = HibernateVaultQueryImpl(database.hibernateConfig, vaultService) @@ -155,6 +156,7 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub { override val keyManagementService: KeyManagementService = MockKeyManagementService(identityService, *keys) override val vaultService: VaultService get() = throw UnsupportedOperationException() + override val contractUpgradeService: ContractUpgradeService = ContractUpgradeServiceImpl() override val vaultQueryService: VaultQueryService get() = throw UnsupportedOperationException() override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException() override val clock: Clock get() = Clock.systemUTC()