diff --git a/build.gradle b/build.gradle index 781fedb2c0..2408dffeb7 100644 --- a/build.gradle +++ b/build.gradle @@ -52,6 +52,7 @@ buildscript { ext.spring_jdbc_version ='5.0.0.RELEASE' ext.shiro_version = '1.4.0' ext.artifactory_plugin_version = constants.getProperty('artifactoryPluginVersion') + ext.liquibase_version = '3.5.3' // Update 121 is required for ObjectInputFilter and at time of writing 131 was latest: ext.java8_minUpdateVersion = '131' diff --git a/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt index 585cb8b6bf..653744c393 100644 --- a/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt @@ -33,7 +33,7 @@ class IdentitySyncFlowTests { mockNet = MockNetwork( networkSendManuallyPumped = false, threadPerNode = true, - cordappPackages = listOf("net.corda.finance.contracts.asset") + cordappPackages = listOf("net.corda.finance.contracts.asset", "net.corda.finance.schemas") ) } diff --git a/core/src/main/kotlin/net/corda/core/internal/schemas/NodeInfoSchema.kt b/core/src/main/kotlin/net/corda/core/internal/schemas/NodeInfoSchema.kt index 19d4a74255..10a630f610 100644 --- a/core/src/main/kotlin/net/corda/core/internal/schemas/NodeInfoSchema.kt +++ b/core/src/main/kotlin/net/corda/core/internal/schemas/NodeInfoSchema.kt @@ -19,6 +19,9 @@ object NodeInfoSchemaV1 : MappedSchema( version = 1, mappedTypes = listOf(PersistentNodeInfo::class.java, DBPartyAndCertificate::class.java, DBHostAndPort::class.java) ) { + + override val migrationResource = "node-info.changelog-master" + @Entity @Table(name = "node_infos") class PersistentNodeInfo( diff --git a/core/src/main/kotlin/net/corda/core/schemas/CommonSchema.kt b/core/src/main/kotlin/net/corda/core/schemas/CommonSchema.kt index a630a7b8e3..22aabb959f 100644 --- a/core/src/main/kotlin/net/corda/core/schemas/CommonSchema.kt +++ b/core/src/main/kotlin/net/corda/core/schemas/CommonSchema.kt @@ -4,9 +4,7 @@ import net.corda.core.contracts.* import net.corda.core.identity.AbstractParty import org.hibernate.annotations.Type import java.util.* -import javax.persistence.Column -import javax.persistence.ElementCollection -import javax.persistence.MappedSuperclass +import javax.persistence.* /** * JPA representation of the common schema entities @@ -18,6 +16,8 @@ object CommonSchema */ object CommonSchemaV1 : MappedSchema(schemaFamily = CommonSchema.javaClass, version = 1, mappedTypes = emptyList()) { + override val migrationResource = "common.changelog-master" + @MappedSuperclass open class LinearState( /** [ContractState] attributes */ @@ -25,6 +25,9 @@ object CommonSchemaV1 : MappedSchema(schemaFamily = CommonSchema.javaClass, vers /** X500Name of participant parties **/ @ElementCollection @Column(name = "participants") + @CollectionTable(name="state_participants",joinColumns = arrayOf( + JoinColumn(name = "output_index", referencedColumnName = "output_index"), + JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"))) var participants: MutableSet? = null, /** @@ -34,6 +37,7 @@ object CommonSchemaV1 : MappedSchema(schemaFamily = CommonSchema.javaClass, vers var externalId: String?, @Column(name = "uuid", nullable = false) + @Type(type = "uuid-char") var uuid: UUID ) : PersistentState() { @@ -50,6 +54,9 @@ object CommonSchemaV1 : MappedSchema(schemaFamily = CommonSchema.javaClass, vers /** X500Name of participant parties **/ @ElementCollection @Column(name = "participants") + @CollectionTable(name="state_participants",joinColumns = arrayOf( + JoinColumn(name = "output_index", referencedColumnName = "output_index"), + JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"))) var participants: MutableSet? = null, /** [OwnableState] attributes */ diff --git a/core/src/main/kotlin/net/corda/core/schemas/PersistentTypes.kt b/core/src/main/kotlin/net/corda/core/schemas/PersistentTypes.kt index cd55da4982..10fca25dc5 100644 --- a/core/src/main/kotlin/net/corda/core/schemas/PersistentTypes.kt +++ b/core/src/main/kotlin/net/corda/core/schemas/PersistentTypes.kt @@ -38,9 +38,16 @@ interface QueryableState : ContractState { * @param mappedTypes The JPA entity classes that the ORM layer needs to be configure with for this schema. */ open class MappedSchema(schemaFamily: Class<*>, - val version: Int, - val mappedTypes: Iterable>) { + val version: Int, + val mappedTypes: Iterable>) { val name: String = schemaFamily.name + + /** + * Points to a classpath resource containing the database changes for the [mappedTypes] + */ + protected open val migrationResource: String? = null + internal fun getMigrationResource(): String = migrationResource!! + override fun toString(): String = "${this.javaClass.simpleName}(name=$name, version=$version)" } //DOCEND MappedSchema @@ -69,4 +76,6 @@ data class PersistentStateRef( /** * Marker interface to denote a persistable Corda state entity that will always have a transaction id and index */ -interface StatePersistable \ No newline at end of file +interface StatePersistable + +fun getMigrationResource(schema: MappedSchema): String = schema.getMigrationResource() \ No newline at end of file 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 eb3b5609a5..2ae8a1f6ab 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -49,7 +49,7 @@ class ContractUpgradeFlowTest { @Before fun setup() { - mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows")) + mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows", "net.corda.finance.schemas")) aliceNode = mockNet.createPartyNode(ALICE_NAME) bobNode = mockNet.createPartyNode(BOB_NAME) notary = mockNet.defaultNotaryIdentity diff --git a/docs/source/api-persistence.rst b/docs/source/api-persistence.rst index c2c6e88e46..ba11b26cd7 100644 --- a/docs/source/api-persistence.rst +++ b/docs/source/api-persistence.rst @@ -144,3 +144,94 @@ which is then referenced within a custom flow: :start-after: DOCSTART TopupIssuer :end-before: DOCEND TopupIssuer +Database Migration +================== + +As a database migration tool, we use the open source library liquibase . + +If migration is enabled, the database state is checked (and updated) during node startup. (After deploying a new version of the code that contains database migrations, they are executed at that point). +Possible database changes range from schema changes to data changes. + +Liquibase will create a table called ``DATABASECHANGELOG``, that will store useful information about each change ( like timestamp, description, user, md5 hash so it can't be changed, etc) +We can also "tag" the database at each release to make rollback easier. + +Database changes are maintained in several xml files per ``MappedSchema``, so that only migrations corresponding to the node’s configured schemas are run. +For example, on the node-info schema, if there are any database changes for release 12, the changes will be added to a new file called: ``node-info.changelog-v12.xml`` which has to be included in ``node-info.changelog-master.xml``. + + +Example: +-------- + +Let's suppose that at some point, at version 12, there is a need to add a new column: ``contentSize`` to the ``DBAttachment`` entity. + +This means we have to: + - In the source code, add the ``contentSize`` property and map it to a new column. + - create the column in the ``node_attachments`` table. + - Run an update to set the size of all existing attachments, to not break the code that uses the new property + +.. code-block:: kotlin + + class DBAttachment( + ... + @Column(name = "content") + @Lob + var content: ByteArray, + + //newly added column + @Column(name = "content_size") + var contentSize: Int, + ... + ) + +The ``DBAttachment`` entity is included in the ``NodeServicesV1`` schema, so we create a file ``node-services.changelog-v12.xml`` with this changeset: + +.. code-block:: xml + + + + + + + + + + + + + +And include it in `node-services.changelog-master.xml`: + +.. code-block:: xml + + + + + + + + + + + + + + +By adding the rollback script, we give users the option to revert to an older version of the software. + +An easy way to manage the db version is to tag it on every release (or on every release that has migrations) + + + +Usage: +------ + +Configurations: + +- To enable migration at startup, set: + - database.runMigration = true // true by default + +Command line arguments: + +- To export the migration to a file use `—just-generate-database-migration outputSqlFile`. This will generate the delta from the last release, and will output the resulting sql into the outputSqlFile. It will not write to the db. It will not start the node! ( default value for `outputSqlFile` is a `.sql` file with the current date ) + +- To run the migration without starting the node: `--just-run-db-migration` diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index d17183d8d8..a5d6753bdf 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -6,6 +6,13 @@ from the previous milestone release. UNRELEASED ---------- +* Integrate database migration tool: http://www.liquibase.org/ : + * The migration files are split per ``MappedSchemas``. (added new property: migrationResource used to point to the resource file containing the db changes corresponding to the JPA entities) + * config flag ``database.initialiseSchema`` was renamed to: ``database.runMigration`` (if true then the migration is run during startup just before hibernate is initialised.) + * config flag: ``database.serverNameTablePrefix`` was removed as we no longer need table prefixes + * New command line argument: “—just-generate-database-migration outputSqlFile”: This will generate the delta from the last release, and will output the resulting sql into the outputSqlFile. It will not write to the db. It will not start the node! + * New command line argument: “--just-run-db-migration”: This will only run the db migration. It will not start the node! + * Exporting additional JMX metrics (artemis, hibernate statistics) and loading Jolokia agent at JVM startup when using DriverDSL and/or cordformation node runner. diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index 3a8a7ebfcd..7ac54bbe95 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -70,7 +70,6 @@ path to the node's base directory. :database: Database configuration: - :serverNameTablePrefix: Prefix string to apply to all the database tables. The default is no prefix. :transactionIsolationLevel: Transaction isolation level as defined by the ``TRANSACTION_`` constants in ``java.sql.Connection``, but without the "TRANSACTION_" prefix. Defaults to REPEATABLE_READ. :exportHibernateJMXStatistics: Whether to export Hibernate JMX statistics (caution: expensive run-time overhead) @@ -81,13 +80,13 @@ path to the node's base directory. :database: This section is used to configure JDBC and Hibernate related properties: - :serverNameTablePrefix: Prefix string to apply to all the database tables. The default is no prefix. - :transactionIsolationLevel: Transaction isolation level as defined by the ``TRANSACTION_`` constants in ``java.sql.Connection``, but without the "TRANSACTION_" prefix. Defaults to REPEATABLE_READ. :exportHibernateJMXStatistics: Whether to export Hibernate JMX statistics (caution: expensive run-time overhead) + :runMigration: Boolean on whether to run the database migration scripts. Defaults to true. + :schema: (optional) some database providers require a schema name when generating DDL and SQL statements. (the value is passed to Hibernate property 'hibernate.hbm2ddl.auto'). diff --git a/finance/src/main/kotlin/net/corda/finance/schemas/CashSchemaV1.kt b/finance/src/main/kotlin/net/corda/finance/schemas/CashSchemaV1.kt index 351dcc0c70..2977e297c9 100644 --- a/finance/src/main/kotlin/net/corda/finance/schemas/CashSchemaV1.kt +++ b/finance/src/main/kotlin/net/corda/finance/schemas/CashSchemaV1.kt @@ -19,7 +19,11 @@ object CashSchema * at the time of writing. */ @CordaSerializable -object CashSchemaV1 : MappedSchema(schemaFamily = CashSchema.javaClass, version = 1, mappedTypes = listOf(PersistentCashState::class.java)) { +object CashSchemaV1 : MappedSchema( + schemaFamily = CashSchema.javaClass, version = 1, mappedTypes = listOf(PersistentCashState::class.java)) { + + override val migrationResource = "cash.changelog-master" + @Entity @Table(name = "contract_cash_states", indexes = arrayOf(Index(name = "ccy_code_idx", columnList = "ccy_code"), diff --git a/finance/src/main/kotlin/net/corda/finance/schemas/CommercialPaperSchemaV1.kt b/finance/src/main/kotlin/net/corda/finance/schemas/CommercialPaperSchemaV1.kt index c3a9743115..fbcfe578b9 100644 --- a/finance/src/main/kotlin/net/corda/finance/schemas/CommercialPaperSchemaV1.kt +++ b/finance/src/main/kotlin/net/corda/finance/schemas/CommercialPaperSchemaV1.kt @@ -22,7 +22,11 @@ object CommercialPaperSchema * as it stood at the time of writing. */ @CordaSerializable -object CommercialPaperSchemaV1 : MappedSchema(schemaFamily = CommercialPaperSchema.javaClass, version = 1, mappedTypes = listOf(PersistentCommercialPaperState::class.java)) { +object CommercialPaperSchemaV1 : MappedSchema( + schemaFamily = CommercialPaperSchema.javaClass, version = 1, mappedTypes = listOf(PersistentCommercialPaperState::class.java)) { + + override val migrationResource = "commercial-paper.changelog-master" + @Entity @Table(name = "cp_states", indexes = arrayOf(Index(name = "ccy_code_index", columnList = "ccy_code"), diff --git a/finance/src/main/resources/migration/cash.changelog-init.xml b/finance/src/main/resources/migration/cash.changelog-init.xml new file mode 100644 index 0000000000..1142cfa847 --- /dev/null +++ b/finance/src/main/resources/migration/cash.changelog-init.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/finance/src/main/resources/migration/cash.changelog-master.xml b/finance/src/main/resources/migration/cash.changelog-master.xml new file mode 100644 index 0000000000..5ffd4689bd --- /dev/null +++ b/finance/src/main/resources/migration/cash.changelog-master.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/finance/src/main/resources/migration/commercial-paper.changelog-init.xml b/finance/src/main/resources/migration/commercial-paper.changelog-init.xml new file mode 100644 index 0000000000..ff5567281e --- /dev/null +++ b/finance/src/main/resources/migration/commercial-paper.changelog-init.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/finance/src/main/resources/migration/commercial-paper.changelog-master.xml b/finance/src/main/resources/migration/commercial-paper.changelog-master.xml new file mode 100644 index 0000000000..ef0091e538 --- /dev/null +++ b/finance/src/main/resources/migration/commercial-paper.changelog-master.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt index 71ae00d507..81f40d5c6f 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt @@ -261,14 +261,14 @@ class CommercialPaperTestsGeneric { private lateinit var aliceVaultService: VaultService private lateinit var alicesVault: Vault private val notaryServices = MockServices(rigorousMock(), MEGA_CORP.name, dummyNotary.key) - private val issuerServices = MockServices(listOf("net.corda.finance.contracts"), rigorousMock(), MEGA_CORP.name, dummyCashIssuer.key) + private val issuerServices = MockServices(listOf("net.corda.finance.contracts", "net.corda.finance.schemas"), rigorousMock(), MEGA_CORP.name, dummyCashIssuer.key) private lateinit var moveTX: SignedTransaction @Test fun `issue move and then redeem`() { val aliceDatabaseAndServices = makeTestDatabaseAndMockServices( listOf(ALICE_KEY), makeTestIdentityService(listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY)), - listOf("net.corda.finance.contracts"), + listOf("net.corda.finance.contracts", "net.corda.finance.schemas"), MEGA_CORP.name) val databaseAlice = aliceDatabaseAndServices.first aliceServices = aliceDatabaseAndServices.second @@ -281,7 +281,7 @@ class CommercialPaperTestsGeneric { val bigCorpDatabaseAndServices = makeTestDatabaseAndMockServices( listOf(BIG_CORP_KEY), makeTestIdentityService(listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY)), - listOf("net.corda.finance.contracts"), + listOf("net.corda.finance.contracts", "net.corda.finance.schemas"), MEGA_CORP.name) val databaseBigCorp = bigCorpDatabaseAndServices.first bigCorpServices = bigCorpDatabaseAndServices.second diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt index 760aab4aea..66ed294997 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt @@ -93,15 +93,15 @@ class CashTests { @Before fun setUp() { LogHelper.setLevel(NodeVaultService::class) - megaCorpServices = MockServices(listOf("net.corda.finance.contracts.asset"), rigorousMock(), MEGA_CORP.name, MEGA_CORP_KEY) - miniCorpServices = MockServices(listOf("net.corda.finance.contracts.asset"), rigorousMock().also { + megaCorpServices = MockServices(listOf("net.corda.finance.contracts.asset", "net.corda.finance.schemas"), rigorousMock(), MEGA_CORP.name, MEGA_CORP_KEY) + miniCorpServices = MockServices(listOf("net.corda.finance.contracts.asset", "net.corda.finance.schemas"), rigorousMock().also { doNothing().whenever(it).justVerifyAndRegisterIdentity(argThat { name == MINI_CORP.name }) }, MINI_CORP.name, MINI_CORP_KEY) val notaryServices = MockServices(listOf("net.corda.finance.contracts.asset"), rigorousMock(), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) val databaseAndServices = makeTestDatabaseAndMockServices( listOf(generateKeyPair()), makeTestIdentityService(listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY)), - listOf("net.corda.finance.contracts.asset"), + listOf("net.corda.finance.contracts.asset", "net.corda.finance.schemas"), CordaX500Name("Me", "London", "GB")) database = databaseAndServices.first ourServices = databaseAndServices.second diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/DummyFungibleContract.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/DummyFungibleContract.kt index 62f3b7ad8f..cb71ff168a 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/DummyFungibleContract.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/DummyFungibleContract.kt @@ -8,9 +8,9 @@ import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState import net.corda.core.schemas.QueryableState import net.corda.core.transactions.LedgerTransaction -import net.corda.finance.schemas.SampleCashSchemaV1 -import net.corda.finance.schemas.SampleCashSchemaV2 -import net.corda.finance.schemas.SampleCashSchemaV3 +import net.corda.finance.sampleschemas.SampleCashSchemaV1 +import net.corda.finance.sampleschemas.SampleCashSchemaV2 +import net.corda.finance.sampleschemas.SampleCashSchemaV3 import net.corda.finance.utils.sumCash import net.corda.finance.utils.sumCashOrNull import net.corda.finance.utils.sumCashOrZero diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2ImplTest.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2ImplTest.kt index faba806b2a..c631d57b04 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2ImplTest.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2ImplTest.kt @@ -17,7 +17,7 @@ import org.junit.Test import java.util.Collections.nCopies class CashSelectionH2ImplTest { - private val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance")) + private val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance", "net.corda.core.schemas", "net.corda.finance.sampleschemas")) @After fun cleanUp() { diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt index 5bb058c5ac..7684723a9e 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt @@ -29,7 +29,7 @@ class CashExitFlowTests { @Before fun start() { mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), - cordappPackages = listOf("net.corda.finance.contracts.asset")) + cordappPackages = listOf("net.corda.finance.contracts.asset", "net.corda.finance.schemas")) bankOfCordaNode = mockNet.createPartyNode(BOC_NAME) bankOfCorda = bankOfCordaNode.info.identityFromX500Name(BOC_NAME) notary = mockNet.defaultNotaryIdentity diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt index 98df506c31..30c24f3a8f 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt @@ -26,7 +26,9 @@ class CashIssueFlowTests { @Before fun start() { - mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset")) + mockNet = MockNetwork( + servicePeerAllocationStrategy = RoundRobin(), + cordappPackages = listOf("net.corda.finance.contracts.asset", "net.corda.finance.schemas")) bankOfCordaNode = mockNet.createPartyNode(BOC_NAME) bankOfCorda = bankOfCordaNode.info.identityFromX500Name(BOC_NAME) notary = mockNet.defaultNotaryIdentity diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt index 1c046752a2..97475b3172 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt @@ -31,7 +31,7 @@ class CashPaymentFlowTests { @Before fun start() { - mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset")) + mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts.asset", "net.corda.finance.schemas")) bankOfCordaNode = mockNet.createPartyNode(BOC_NAME) bankOfCorda = bankOfCordaNode.info.identityFromX500Name(BOC_NAME) aliceNode = mockNet.createPartyNode(ALICE_NAME) diff --git a/finance/src/test/kotlin/net/corda/finance/schemas/SampleCashSchemaV1.kt b/finance/src/test/kotlin/net/corda/finance/sampleschemas/SampleCashSchemaV1.kt similarity index 90% rename from finance/src/test/kotlin/net/corda/finance/schemas/SampleCashSchemaV1.kt rename to finance/src/test/kotlin/net/corda/finance/sampleschemas/SampleCashSchemaV1.kt index 7ac073621e..a530ec5aa4 100644 --- a/finance/src/test/kotlin/net/corda/finance/schemas/SampleCashSchemaV1.kt +++ b/finance/src/test/kotlin/net/corda/finance/sampleschemas/SampleCashSchemaV1.kt @@ -1,4 +1,4 @@ -package net.corda.finance.schemas +package net.corda.finance.sampleschemas import net.corda.core.contracts.MAX_ISSUER_REF_SIZE import net.corda.core.schemas.MappedSchema @@ -20,8 +20,11 @@ object CashSchema * at the time of writing. */ object SampleCashSchemaV1 : MappedSchema(schemaFamily = CashSchema.javaClass, version = 1, mappedTypes = listOf(PersistentCashState::class.java)) { + + override val migrationResource = "sample-cash-v1.changelog-init" + @Entity - @Table(name = "contract_cash_states", + @Table(name = "contract_cash_states_v1", indexes = arrayOf(Index(name = "ccy_code_idx", columnList = "ccy_code"), Index(name = "pennies_idx", columnList = "pennies"))) class PersistentCashState( diff --git a/finance/src/test/kotlin/net/corda/finance/schemas/SampleCashSchemaV2.kt b/finance/src/test/kotlin/net/corda/finance/sampleschemas/SampleCashSchemaV2.kt similarity index 92% rename from finance/src/test/kotlin/net/corda/finance/schemas/SampleCashSchemaV2.kt rename to finance/src/test/kotlin/net/corda/finance/sampleschemas/SampleCashSchemaV2.kt index 8b548f89ce..13ccdd6420 100644 --- a/finance/src/test/kotlin/net/corda/finance/schemas/SampleCashSchemaV2.kt +++ b/finance/src/test/kotlin/net/corda/finance/sampleschemas/SampleCashSchemaV2.kt @@ -1,4 +1,4 @@ -package net.corda.finance.schemas +package net.corda.finance.sampleschemas import net.corda.core.identity.AbstractParty import net.corda.core.schemas.CommonSchemaV1 @@ -15,6 +15,9 @@ import javax.persistence.Table */ object SampleCashSchemaV2 : MappedSchema(schemaFamily = CashSchema.javaClass, version = 2, mappedTypes = listOf(PersistentCashState::class.java)) { + + override val migrationResource = "sample-cash-v2.changelog-init" + @Entity @Table(name = "cash_states_v2", indexes = arrayOf(Index(name = "ccy_code_idx2", columnList = "ccy_code"))) diff --git a/finance/src/test/kotlin/net/corda/finance/schemas/SampleCashSchemaV3.kt b/finance/src/test/kotlin/net/corda/finance/sampleschemas/SampleCashSchemaV3.kt similarity index 77% rename from finance/src/test/kotlin/net/corda/finance/schemas/SampleCashSchemaV3.kt rename to finance/src/test/kotlin/net/corda/finance/sampleschemas/SampleCashSchemaV3.kt index 0a6c0cc289..9b0b14df29 100644 --- a/finance/src/test/kotlin/net/corda/finance/schemas/SampleCashSchemaV3.kt +++ b/finance/src/test/kotlin/net/corda/finance/sampleschemas/SampleCashSchemaV3.kt @@ -1,14 +1,11 @@ -package net.corda.finance.schemas +package net.corda.finance.sampleschemas import net.corda.core.contracts.MAX_ISSUER_REF_SIZE import net.corda.core.identity.AbstractParty import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState import org.hibernate.annotations.Type -import javax.persistence.Column -import javax.persistence.ElementCollection -import javax.persistence.Entity -import javax.persistence.Table +import javax.persistence.* /** * First version of a cash contract ORM schema that maps all fields of the [Cash] contract state as it stood @@ -16,6 +13,9 @@ import javax.persistence.Table */ object SampleCashSchemaV3 : MappedSchema(schemaFamily = CashSchema.javaClass, version = 3, mappedTypes = listOf(PersistentCashState::class.java)) { + + override val migrationResource = "sample-cash-v3.changelog-init" + @Entity @Table(name = "cash_states_v3") class PersistentCashState( @@ -23,6 +23,9 @@ object SampleCashSchemaV3 : MappedSchema(schemaFamily = CashSchema.javaClass, ve /** X500Name of participant parties **/ @ElementCollection + @CollectionTable(name="state_participants", joinColumns = arrayOf( + JoinColumn(name = "output_index", referencedColumnName = "output_index"), + JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"))) var participants: MutableSet? = null, /** X500Name of owner party **/ diff --git a/finance/src/test/kotlin/net/corda/finance/schemas/SampleCommercialPaperSchemaV1.kt b/finance/src/test/kotlin/net/corda/finance/sampleschemas/SampleCommercialPaperSchemaV1.kt similarity index 93% rename from finance/src/test/kotlin/net/corda/finance/schemas/SampleCommercialPaperSchemaV1.kt rename to finance/src/test/kotlin/net/corda/finance/sampleschemas/SampleCommercialPaperSchemaV1.kt index f407bf7fc2..44a7ba7467 100644 --- a/finance/src/test/kotlin/net/corda/finance/schemas/SampleCommercialPaperSchemaV1.kt +++ b/finance/src/test/kotlin/net/corda/finance/sampleschemas/SampleCommercialPaperSchemaV1.kt @@ -1,4 +1,4 @@ -package net.corda.finance.schemas +package net.corda.finance.sampleschemas import net.corda.core.contracts.MAX_ISSUER_REF_SIZE import net.corda.core.schemas.MappedSchema @@ -21,8 +21,11 @@ object CommercialPaperSchema * as it stood at the time of writing. */ object SampleCommercialPaperSchemaV1 : MappedSchema(schemaFamily = CommercialPaperSchema.javaClass, version = 1, mappedTypes = listOf(PersistentCommercialPaperState::class.java)) { + + override val migrationResource = "sample-cp-v1.changelog-init" + @Entity - @Table(name = "cp_states", + @Table(name = "cp_states_v1", indexes = arrayOf(Index(name = "ccy_code_index", columnList = "ccy_code"), Index(name = "maturity_index", columnList = "maturity_instant"), Index(name = "face_value_index", columnList = "face_value"))) diff --git a/finance/src/test/kotlin/net/corda/finance/schemas/SampleCommercialPaperSchemaV2.kt b/finance/src/test/kotlin/net/corda/finance/sampleschemas/SampleCommercialPaperSchemaV2.kt similarity index 94% rename from finance/src/test/kotlin/net/corda/finance/schemas/SampleCommercialPaperSchemaV2.kt rename to finance/src/test/kotlin/net/corda/finance/sampleschemas/SampleCommercialPaperSchemaV2.kt index 2c7882c048..dc07367af7 100644 --- a/finance/src/test/kotlin/net/corda/finance/schemas/SampleCommercialPaperSchemaV2.kt +++ b/finance/src/test/kotlin/net/corda/finance/sampleschemas/SampleCommercialPaperSchemaV2.kt @@ -1,4 +1,4 @@ -package net.corda.finance.schemas +package net.corda.finance.sampleschemas import net.corda.core.contracts.MAX_ISSUER_REF_SIZE import net.corda.core.identity.AbstractParty @@ -19,6 +19,9 @@ import javax.persistence.Table */ object SampleCommercialPaperSchemaV2 : MappedSchema(schemaFamily = CommercialPaperSchema.javaClass, version = 1, mappedTypes = listOf(PersistentCommercialPaperState::class.java)) { + + override val migrationResource = "sample-cp-v2.changelog-init" + @Entity @Table(name = "cp_states_v2", indexes = arrayOf(Index(name = "ccy_code_index2", columnList = "ccy_code"), diff --git a/finance/src/test/resources/migration/sample-cash-v1.changelog-init.xml b/finance/src/test/resources/migration/sample-cash-v1.changelog-init.xml new file mode 100644 index 0000000000..bf4dc09eec --- /dev/null +++ b/finance/src/test/resources/migration/sample-cash-v1.changelog-init.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/finance/src/test/resources/migration/sample-cash-v2.changelog-init.xml b/finance/src/test/resources/migration/sample-cash-v2.changelog-init.xml new file mode 100644 index 0000000000..ea6f138389 --- /dev/null +++ b/finance/src/test/resources/migration/sample-cash-v2.changelog-init.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/finance/src/test/resources/migration/sample-cash-v3.changelog-init.xml b/finance/src/test/resources/migration/sample-cash-v3.changelog-init.xml new file mode 100644 index 0000000000..aad9bc8da7 --- /dev/null +++ b/finance/src/test/resources/migration/sample-cash-v3.changelog-init.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/finance/src/test/resources/migration/sample-cp-v1.changelog-init.xml b/finance/src/test/resources/migration/sample-cp-v1.changelog-init.xml new file mode 100644 index 0000000000..c43348fd37 --- /dev/null +++ b/finance/src/test/resources/migration/sample-cp-v1.changelog-init.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/finance/src/test/resources/migration/sample-cp-v2.changelog-init.xml b/finance/src/test/resources/migration/sample-cp-v2.changelog-init.xml new file mode 100644 index 0000000000..d441a347af --- /dev/null +++ b/finance/src/test/resources/migration/sample-cp-v2.changelog-init.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt index cbb85829d8..7ce747fc5f 100644 --- a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt +++ b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/hsm/SigningServiceIntegrationTest.kt @@ -190,4 +190,4 @@ private fun makeTestDataSourceProperties(): Properties { return props } -internal fun makeNotInitialisingTestDatabaseProperties() = DatabaseConfig(initialiseSchema = false) \ No newline at end of file +internal fun makeNotInitialisingTestDatabaseProperties() = DatabaseConfig(runMigration = false) \ No newline at end of file diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistenceUtils.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistenceUtils.kt index d30bb74e48..8387e3cb3e 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistenceUtils.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistenceUtils.kt @@ -7,6 +7,7 @@ import net.corda.core.schemas.MappedSchema import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseTransaction +import net.corda.nodeapi.internal.persistence.SchemaMigration import java.util.* import javax.persistence.LockModeType import javax.persistence.criteria.CriteriaBuilder @@ -35,7 +36,13 @@ fun configureDatabase(dataSourceProperties: Properties, databaseConfig: DatabaseConfig = DatabaseConfig()): CordaPersistence { val config = HikariConfig(dataSourceProperties) val dataSource = HikariDataSource(config) - return CordaPersistence(dataSource, databaseConfig, setOf(NetworkManagementSchemaServices.SchemaV1), emptyList()) + + val schemas = setOf(NetworkManagementSchemaServices.SchemaV1) + if (databaseConfig.runMigration) { + SchemaMigration(schemas, dataSource).runMigration() + } + + return CordaPersistence(dataSource, databaseConfig, schemas, emptyList()) } sealed class NetworkManagementSchemaServices { @@ -45,5 +52,7 @@ sealed class NetworkManagementSchemaServices { CertificateDataEntity::class.java, NodeInfoEntity::class.java, NetworkParametersEntity::class.java, - NetworkMapEntity::class.java)) + NetworkMapEntity::class.java)) { + override val migrationResource = "network-manager.changelog-master" + } } diff --git a/network-management/src/main/resources/migration/network-manager.changelog-init.xml b/network-management/src/main/resources/migration/network-manager.changelog-init.xml new file mode 100644 index 0000000000..87dee6bd37 --- /dev/null +++ b/network-management/src/main/resources/migration/network-manager.changelog-init.xml @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/network-management/src/main/resources/migration/network-manager.changelog-master.xml b/network-management/src/main/resources/migration/network-manager.changelog-master.xml new file mode 100644 index 0000000000..48836fc4c8 --- /dev/null +++ b/network-management/src/main/resources/migration/network-manager.changelog-master.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/node-api/build.gradle b/node-api/build.gradle index d2c51dde94..566a4bf521 100644 --- a/node-api/build.gradle +++ b/node-api/build.gradle @@ -34,6 +34,9 @@ dependencies { // For AMQP serialisation. compile "org.apache.qpid:proton-j:0.21.0" + // For db migration + compile "org.liquibase:liquibase-core:$liquibase_version" + // Unit testing helpers. testCompile "junit:junit:$junit_version" testCompile "org.assertj:assertj-core:$assertj_version" diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt index e029620dce..c76778df7e 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt @@ -18,8 +18,7 @@ const val NODE_DATABASE_PREFIX = "node_" // This class forms part of the node config and so any changes to it must be handled with care data class DatabaseConfig( - val initialiseSchema: Boolean = true, - val serverNameTablePrefix: String = "", + val runMigration: Boolean = true, val transactionIsolationLevel: TransactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ, val schema: String? = null, val exportHibernateJMXStatistics: Boolean = false @@ -47,6 +46,7 @@ class CordaPersistence( ) : Closeable { val defaultIsolationLevel = databaseConfig.transactionIsolationLevel val hibernateConfig: HibernateConfiguration by lazy { + transaction { HibernateConfiguration(schemas, databaseConfig, attributeConverters) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt index fc37580e61..1b83967f43 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt @@ -47,25 +47,17 @@ class HibernateConfiguration( logger.info("Creating session factory for schemas: $schemas") val serviceRegistry = BootstrapServiceRegistryBuilder().build() val metadataSources = MetadataSources(serviceRegistry) - // We set a connection provider as the auto schema generation requires it. The auto schema generation will not - // necessarily remain and would likely be replaced by something like Liquibase. For now it is very convenient though. - // TODO: replace auto schema generation as it isn't intended for production use, according to Hibernate docs. - val config = Configuration(metadataSources).setProperty("hibernate.connection.provider_class", NodeDatabaseConnectionProvider::class.java.name) - .setProperty("hibernate.hbm2ddl.auto", if (databaseConfig.initialiseSchema) "update" else "validate") - .setProperty("hibernate.format_sql", "true") - .setProperty("hibernate.connection.isolation", databaseConfig.transactionIsolationLevel.jdbcValue.toString()) - if (databaseConfig.schema != null) { - // This property helps 'hibernate.hbm2ddl.auto' to work properly when many schemas have similar table names. - config.setProperty("hibernate.default_schema", databaseConfig.schema) - } + val config = Configuration(metadataSources).setProperty("hibernate.connection.provider_class", NodeDatabaseConnectionProvider::class.java.name) + .setProperty("hibernate.hbm2ddl.auto", "validate") + .setProperty("hibernate.connection.isolation", databaseConfig.transactionIsolationLevel.jdbcValue.toString()) schemas.forEach { schema -> // TODO: require mechanism to set schemaOptions (databaseSchema, tablePrefix) which are not global to session schema.mappedTypes.forEach { config.addAnnotatedClass(it) } } - val sessionFactory = buildSessionFactory(config, metadataSources, databaseConfig.serverNameTablePrefix) + val sessionFactory = buildSessionFactory(config, metadataSources) logger.info("Created session factory for schemas: $schemas") // export Hibernate JMX statistics @@ -92,15 +84,9 @@ class HibernateConfiguration( } } - private fun buildSessionFactory(config: Configuration, metadataSources: MetadataSources, tablePrefix: String): SessionFactory { + private fun buildSessionFactory(config: Configuration, metadataSources: MetadataSources): SessionFactory { config.standardServiceRegistryBuilder.applySettings(config.properties) val metadata = metadataSources.getMetadataBuilder(config.standardServiceRegistryBuilder.build()).run { - applyPhysicalNamingStrategy(object : PhysicalNamingStrategyStandardImpl() { - override fun toPhysicalTableName(name: Identifier?, context: JdbcEnvironment?): Identifier { - val default = super.toPhysicalTableName(name, context) - return Identifier.toIdentifier(tablePrefix + default.text, default.isQuoted) - } - }) // register custom converters attributeConverters.forEach { applyAttributeConverter(it) } // Register a tweaked version of `org.hibernate.type.MaterializedBlobType` that truncates logged messages. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/SchemaMigration.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/SchemaMigration.kt new file mode 100644 index 0000000000..7cde7edafd --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/SchemaMigration.kt @@ -0,0 +1,84 @@ +package net.corda.nodeapi.internal.persistence + +import com.fasterxml.jackson.databind.ObjectMapper +import liquibase.Contexts +import liquibase.Liquibase +import liquibase.database.Database +import liquibase.database.DatabaseFactory +import liquibase.database.core.MSSQLDatabase +import liquibase.database.jvm.JdbcConnection +import liquibase.resource.ClassLoaderResourceAccessor +import net.corda.core.schemas.MappedSchema +import net.corda.core.schemas.getMigrationResource +import java.io.* +import javax.sql.DataSource + +private const val MIGRATION_PREFIX = "migration" + +class SchemaMigration(val schemas: Set, val dataSource: DataSource) { + + fun generateMigrationScript(outputFile: File) = doRunMigration(PrintWriter(outputFile)) + + fun runMigration() = doRunMigration() + + private fun doRunMigration(outputWriter: Writer? = null) { + + // virtual file name of the changelog that includes all schemas + val dynamicInclude = "master.changelog.json" + + dataSource.connection.use { connection -> + + //create a resourse accessor that aggregates the changelogs included in the schemas into one dynamic stream + val customResourceAccessor = object : ClassLoaderResourceAccessor() { + override fun getResourcesAsStream(path: String): Set { + + if (path == dynamicInclude) { + //collect all changelog file referenced in the included schemas + val changelogList = schemas.map { mappedSchema -> + getMigrationResource(mappedSchema).let { + "${MIGRATION_PREFIX}/${it}.xml" + } + } + + //create a map in liquibase format including all migration files + val includeAllFiles = mapOf("databaseChangeLog" to changelogList.map { file -> mapOf("include" to mapOf("file" to file)) }) + + // transform it to json + val includeAllFilesJson = ObjectMapper().writeValueAsBytes(includeAllFiles) + + //return the json as a stream + return setOf(ByteArrayInputStream(includeAllFilesJson)) + } + return super.getResourcesAsStream(path)?.take(1)?.toSet() ?: emptySet() + } + } + + val liquibase = Liquibase(dynamicInclude, customResourceAccessor, getLiquibaseDatabase(JdbcConnection(connection))) + + if (outputWriter != null) { + liquibase.update(Contexts(), outputWriter) + } else { + liquibase.update(Contexts()) + } + } + } + + private fun getLiquibaseDatabase(conn: JdbcConnection): Database { + + // the standard MSSQLDatabase in liquibase does not support sequences for Ms Azure + // this class just overrides that behaviour + class AzureDatabase(conn: JdbcConnection) : MSSQLDatabase() { + init { + this.connection = conn + } + + override fun getShortName(): String = "azure" + + override fun supportsSequences(): Boolean = true + } + + val liquibaseDbImplementation = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(conn) + + return if (liquibaseDbImplementation is MSSQLDatabase) AzureDatabase(conn) else liquibaseDbImplementation + } +} diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt index 9067d05264..aa62f67cfe 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt @@ -11,6 +11,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.POUNDS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow +import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User @@ -45,7 +46,7 @@ class DistributedServiceTests : IntegrationTest() { ) driver( - extraCordappPackagesToScan = listOf("net.corda.finance.contracts"), + extraCordappPackagesToScan = listOf("net.corda.finance.contracts", "net.corda.finance.schemas"), notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, rpcUsers = listOf(testUser), cluster = ClusterSpec.Raft(clusterSize = 3)))) { alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(testUser)).getOrThrow() diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index 1dacc8d5b3..3d072aaad3 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -138,6 +138,8 @@ object MessageSchemaV1 : MappedSchema( version = 1, mappedTypes = listOf(PersistentMessage::class.java)) { + override val migrationResource = "message-schema.changelog-init" + @Entity @Table(name = "messages") class PersistentMessage( diff --git a/node/src/integration-test/resources/migration/message-schema.changelog-init.xml b/node/src/integration-test/resources/migration/message-schema.changelog-init.xml new file mode 100644 index 0000000000..52be6b145e --- /dev/null +++ b/node/src/integration-test/resources/migration/message-schema.changelog-init.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/ArgsParser.kt b/node/src/main/kotlin/net/corda/node/ArgsParser.kt index e88bb64deb..63bd5896fa 100644 --- a/node/src/main/kotlin/net/corda/node/ArgsParser.kt +++ b/node/src/main/kotlin/net/corda/node/ArgsParser.kt @@ -10,6 +10,8 @@ import org.slf4j.event.Level import java.io.PrintStream import java.nio.file.Path import java.nio.file.Paths +import java.text.SimpleDateFormat +import java.util.* // NOTE: Do not use any logger in this class as args parsing is done before the logger is setup. class ArgsParser { @@ -34,8 +36,13 @@ class ArgsParser { private val noLocalShellArg = optionParser.accepts("no-local-shell", "Do not start the embedded shell locally.") private val isRegistrationArg = optionParser.accepts("initial-registration", "Start initial node registration with Corda network to obtain certificate from the permissioning server.") private val isVersionArg = optionParser.accepts("version", "Print the version and exit") + private val justRunDbMigrationArg = optionParser.accepts("just-run-db-migration", + "This will only run the db migration. It will not start the node!") private val justGenerateNodeInfoArg = optionParser.accepts("just-generate-node-info", "Perform the node start-up task necessary to generate its nodeInfo, save it to disk, then quit") + private val justGenerateDatabaseMigrationArg = optionParser + .accepts("just-generate-database-migration", "Generate the database migration in the specified output file, and then quit.") + .withOptionalArg() private val bootstrapRaftClusterArg = optionParser.accepts("bootstrap-raft-cluster", "Bootstraps Raft cluster. The node forms a single node cluster (ignoring otherwise configured peer addresses), acting as a seed for other nodes to join the cluster.") private val helpArg = optionParser.accepts("help").forHelp() @@ -54,9 +61,14 @@ class ArgsParser { val noLocalShell = optionSet.has(noLocalShellArg) val sshdServer = optionSet.has(sshdServerArg) val justGenerateNodeInfo = optionSet.has(justGenerateNodeInfoArg) + val justRunDbMigration = optionSet.has(justRunDbMigrationArg) + val generateDatabaseMigrationToFile = if (optionSet.has(justGenerateDatabaseMigrationArg)) + Pair(true, optionSet.valueOf(justGenerateDatabaseMigrationArg) ?: "migration${SimpleDateFormat("yyyyMMddHHmmss").format(Date())}.sql") + else + Pair(false, null) val bootstrapRaftCluster = optionSet.has(bootstrapRaftClusterArg) return CmdLineOptions(baseDirectory, configFile, help, loggingLevel, logToConsole, isRegistration, isVersion, - noLocalShell, sshdServer, justGenerateNodeInfo, bootstrapRaftCluster) + noLocalShell, sshdServer, justGenerateNodeInfo, justRunDbMigration, generateDatabaseMigrationToFile, bootstrapRaftCluster) } fun printHelp(sink: PrintStream) = optionParser.printHelpOn(sink) @@ -72,6 +84,8 @@ data class CmdLineOptions(val baseDirectory: Path, val noLocalShell: Boolean, val sshdServer: Boolean, val justGenerateNodeInfo: Boolean, + val justRunDbMigration: Boolean, + val generateDatabaseMigrationToFile: Pair, val bootstrapRaftCluster: Boolean) { fun loadConfig(): NodeConfiguration { val config = ConfigHelper.loadConfig(baseDirectory, configFile).parseAsNodeConfiguration() 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 4d34bf61ee..790ccfbf2d 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -59,6 +59,7 @@ import net.corda.node.services.vault.VaultSoftLockManager import net.corda.node.shell.InteractiveShell import net.corda.node.utilities.AffinityExecutor import net.corda.nodeapi.internal.NetworkParameters +import net.corda.nodeapi.internal.persistence.SchemaMigration import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig @@ -68,6 +69,7 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry import org.slf4j.Logger import rx.Observable import rx.Scheduler +import java.io.File import java.io.IOException import java.lang.management.ManagementFactory import java.lang.reflect.InvocationTargetException @@ -195,6 +197,18 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } } + fun generateDatabaseSchema(outputFile: String) { + HikariDataSource(HikariConfig(configuration.dataSourceProperties)).use { dataSource -> + SchemaMigration(cordappLoader.cordappSchemas, dataSource).generateMigrationScript(File(outputFile)) + } + } + + fun runDbMigration() { + HikariDataSource(HikariConfig(configuration.dataSourceProperties)).use { dataSource -> + SchemaMigration(cordappLoader.cordappSchemas, dataSource).runMigration() + } + } + open fun start(): StartedNode { check(started == null) { "Node has already been started" } log.info("Node starting up ...") @@ -829,6 +843,12 @@ fun configureDatabase(dataSourceProperties: Properties, JavaTypeDescriptorRegistry.INSTANCE.addDescriptor(AbstractPartyDescriptor(identityService)) val config = HikariConfig(dataSourceProperties) val dataSource = HikariDataSource(config) + val attributeConverters = listOf(AbstractPartyToX500NameAsStringConverter(identityService)) + + if(databaseConfig.runMigration){ + SchemaMigration(schemaService.schemaOptions.keys, dataSource).runMigration() + } + return CordaPersistence(dataSource, databaseConfig, schemaService.schemaOptions.keys, attributeConverters) } diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index 14803be925..652d36ef8a 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -120,6 +120,14 @@ open class NodeStartup(val args: Array) { node.generateNodeInfo() return } + if (cmdlineOptions.justRunDbMigration) { + node.runDbMigration() + return + } + if (cmdlineOptions.generateDatabaseMigrationToFile.first) { + node.generateDatabaseSchema(cmdlineOptions.generateDatabaseMigrationToFile.second!!) + return + } val startedNode = node.start() Node.printBasicNodeInfo("Loaded CorDapps", startedNode.services.cordappProvider.cordapps.joinToString { it.name }) startedNode.internals.nodeReadyFuture.thenMatch({ diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index d411e2ff31..c08b81efce 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -120,7 +120,7 @@ data class NodeConfigurationImpl( // TODO See TODO above. Rename this to nodeInfoPollingFrequency and make it of type Duration override val additionalNodeInfoPollingFrequencyMsec: Long = 5.seconds.toMillis(), override val sshd: SSHDConfiguration? = null, - override val database: DatabaseConfig = DatabaseConfig(initialiseSchema = devMode, exportHibernateJMXStatistics = devMode) + override val database: DatabaseConfig = DatabaseConfig(exportHibernateJMXStatistics = devMode) ) : NodeConfiguration { override val exportJMXto: String get() = "http" diff --git a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt index 72749b8b48..108488479e 100644 --- a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt +++ b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt @@ -51,7 +51,9 @@ class NodeSchemaService(extraSchemas: Set = emptySet()) : SchemaSe PersistentIdentityService.PersistentIdentityNames::class.java, ContractUpgradeServiceImpl.DBContractUpgrade::class.java, RunOnceService.MutualExclusion::class.java - )) + )){ + override val migrationResource = "node-services.changelog-master" + } // Required schemas are those used by internal Corda services // For example, cash is used by the vault for coin selection (but will be extracted as a standalone CorDapp in future) diff --git a/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt b/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt index 78d5a3ac60..2626c01a9a 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt @@ -27,6 +27,9 @@ object VaultSchema @CordaSerializable object VaultSchemaV1 : MappedSchema(schemaFamily = VaultSchema.javaClass, version = 1, mappedTypes = listOf(VaultStates::class.java, VaultLinearStates::class.java, VaultFungibleStates::class.java, VaultTxnNote::class.java)) { + + override val migrationResource = "vault-schema.changelog-master" + @Entity @Table(name = "vault_states", indexes = arrayOf(Index(name = "state_status_idx", columnList = "state_status"), diff --git a/node/src/main/resources/migration/common.changelog-init.xml b/node/src/main/resources/migration/common.changelog-init.xml new file mode 100644 index 0000000000..513bf9dfa6 --- /dev/null +++ b/node/src/main/resources/migration/common.changelog-init.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/node/src/main/resources/migration/common.changelog-master.xml b/node/src/main/resources/migration/common.changelog-master.xml new file mode 100644 index 0000000000..4f4293dba6 --- /dev/null +++ b/node/src/main/resources/migration/common.changelog-master.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/node/src/main/resources/migration/node-info.changelog-init.xml b/node/src/main/resources/migration/node-info.changelog-init.xml new file mode 100644 index 0000000000..9a29719c17 --- /dev/null +++ b/node/src/main/resources/migration/node-info.changelog-init.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/node/src/main/resources/migration/node-info.changelog-master.xml b/node/src/main/resources/migration/node-info.changelog-master.xml new file mode 100644 index 0000000000..01f59ba81b --- /dev/null +++ b/node/src/main/resources/migration/node-info.changelog-master.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/node/src/main/resources/migration/node-services.changelog-init.xml b/node/src/main/resources/migration/node-services.changelog-init.xml new file mode 100644 index 0000000000..8097adb7dd --- /dev/null +++ b/node/src/main/resources/migration/node-services.changelog-init.xml @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/node/src/main/resources/migration/node-services.changelog-master.xml b/node/src/main/resources/migration/node-services.changelog-master.xml new file mode 100644 index 0000000000..3dd7efe052 --- /dev/null +++ b/node/src/main/resources/migration/node-services.changelog-master.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/node/src/main/resources/migration/vault-schema.changelog-init.xml b/node/src/main/resources/migration/vault-schema.changelog-init.xml new file mode 100644 index 0000000000..d4885ec446 --- /dev/null +++ b/node/src/main/resources/migration/vault-schema.changelog-init.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/node/src/main/resources/migration/vault-schema.changelog-master.xml b/node/src/main/resources/migration/vault-schema.changelog-master.xml new file mode 100644 index 0000000000..35febe3b00 --- /dev/null +++ b/node/src/main/resources/migration/vault-schema.changelog-master.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java index b71fc7db16..bee1dba5ee 100644 --- a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java +++ b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java @@ -27,6 +27,7 @@ import net.corda.testing.TestIdentity; import net.corda.testing.contracts.DummyLinearContract; import net.corda.testing.contracts.VaultFiller; import net.corda.testing.node.MockServices; +import net.corda.testing.schemas.DummyLinearStateSchemaV1; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -69,7 +70,11 @@ public class VaultQueryJavaTests { @Before public void setUp() throws CertificateException, InvalidAlgorithmParameterException { - List cordappPackages = Arrays.asList("net.corda.testing.contracts", "net.corda.finance.contracts.asset", CashSchemaV1.class.getPackage().getName()); + List cordappPackages = Arrays.asList( + "net.corda.testing.contracts", + "net.corda.finance.contracts.asset", + CashSchemaV1.class.getPackage().getName(), + DummyLinearStateSchemaV1.class.getPackage().getName()); IdentityServiceInternal identitySvc = makeTestIdentityService(Arrays.asList(MEGA_CORP.getIdentity(), DUMMY_CASH_ISSUER_INFO.getIdentity(), DUMMY_NOTARY.getIdentity())); Pair databaseAndServices = makeTestDatabaseAndMockServices( Arrays.asList(MEGA_CORP.getKey(), DUMMY_NOTARY.getKey()), diff --git a/node/src/test/kotlin/net/corda/node/ArgsParserTest.kt b/node/src/test/kotlin/net/corda/node/ArgsParserTest.kt index 8ba6f00b27..67d743e72f 100644 --- a/node/src/test/kotlin/net/corda/node/ArgsParserTest.kt +++ b/node/src/test/kotlin/net/corda/node/ArgsParserTest.kt @@ -25,7 +25,10 @@ class ArgsParserTest { noLocalShell = false, sshdServer = false, justGenerateNodeInfo = false, - bootstrapRaftCluster = false)) + justRunDbMigration = false, + bootstrapRaftCluster = false, + generateDatabaseMigrationToFile = Pair(false, null) + )) } @Test diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index ec6086fb7e..a554e194cd 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -76,7 +76,7 @@ class CordaRPCOpsImplTest { @Before fun setup() { - mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset")) + mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset", "net.corda.finance.schemas")) aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME)) rpc = SecureCordaRPCOps(aliceNode.services, aliceNode.smm, aliceNode.database, aliceNode.services) CURRENT_RPC_CONTEXT.set(RpcAuthContext(InvocationContext.rpc(testActor()), buildSubject("TEST_USER", emptySet()))) diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index 8feafba9a3..cf5f84e301 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -68,7 +68,7 @@ import kotlin.test.assertTrue @RunWith(Parameterized::class) class TwoPartyTradeFlowTests(private val anonymous: Boolean) { companion object { - private val cordappPackages = listOf("net.corda.finance.contracts") + private val cordappPackages = listOf("net.corda.finance.contracts", "net.corda.finance.schemas") @JvmStatic @Parameterized.Parameters(name = "Anonymous = {0}") fun data(): Collection = listOf(true, false) 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 e323c98851..f2e1e82d45 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,8 +25,8 @@ import net.corda.finance.SWISS_FRANCS import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.DummyFungibleContract import net.corda.finance.schemas.CashSchemaV1 -import net.corda.finance.schemas.SampleCashSchemaV2 -import net.corda.finance.schemas.SampleCashSchemaV3 +import net.corda.finance.sampleschemas.SampleCashSchemaV2 +import net.corda.finance.sampleschemas.SampleCashSchemaV3 import net.corda.finance.utils.sumCash import net.corda.node.services.schema.HibernateObserver import net.corda.node.services.schema.NodeSchemaService @@ -40,6 +40,7 @@ import net.corda.testing.* import net.corda.testing.contracts.* import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties +import net.corda.testing.schemas.DummyDealStateSchemaV1 import net.corda.testing.schemas.DummyLinearStateSchemaV1 import net.corda.testing.schemas.DummyLinearStateSchemaV2 import org.assertj.core.api.Assertions @@ -105,7 +106,7 @@ class HibernateConfigurationTest { doReturn(it.party).whenever(mock).wellKnownPartyFromX500Name(it.name) } } - val schemaService = NodeSchemaService() + val schemaService = NodeSchemaService(extraSchemas = setOf(CashSchemaV1, SampleCashSchemaV2, SampleCashSchemaV3, DummyLinearStateSchemaV1, DummyLinearStateSchemaV2, DummyDealStateSchemaV1 )) database = configureDatabase(dataSourceProps, DatabaseConfig(), identityService, schemaService) database.transaction { hibernateConfig = database.hibernateConfig diff --git a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt index 16fa18453a..9ad3ec390c 100644 --- a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt @@ -8,6 +8,7 @@ import net.corda.core.crypto.SecureHash import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.node.services.Vault +import net.corda.core.schemas.CommonSchemaV1 import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState import net.corda.core.schemas.QueryableState @@ -55,7 +56,9 @@ class HibernateObserverTests { val testSchema = TestSchema val rawUpdatesPublisher = PublishSubject.create>() val schemaService = object : SchemaService { - override val schemaOptions: Map = emptyMap() + override val schemaOptions: Map = mapOf( + CommonSchemaV1 to SchemaService.SchemaOptions(), + testSchema to SchemaService.SchemaOptions()) override fun selectSchemas(state: ContractState): Iterable = setOf(testSchema) diff --git a/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt index 27a363864f..aa0d9253cd 100644 --- a/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt @@ -72,8 +72,11 @@ class NodeSchemaServiceTest { class SchemaFamily object TestSchema : MappedSchema(SchemaFamily::class.java, 1, setOf(Parent::class.java, Child::class.java)) { + + override val migrationResource = "test.changelog-init" + @Entity - @Table(name = "Parents") + @Table(name = "parents") class Parent : PersistentState() { @OneToMany(fetch = FetchType.LAZY) @JoinColumns(JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"), JoinColumn(name = "output_index", referencedColumnName = "output_index")) @@ -84,7 +87,7 @@ object TestSchema : MappedSchema(SchemaFamily::class.java, 1, setOf(Parent::clas @Suppress("unused") @Entity - @Table(name = "Children") + @Table(name = "children") class Child { @Id @GeneratedValue diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt index ff8a0780c1..57beddeb10 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt @@ -88,7 +88,7 @@ class DistributedImmutableMapTests { private fun createReplica(myAddress: NetworkHostAndPort, clusterAddress: NetworkHostAndPort? = null): CompletableFuture { val storage = Storage.builder().withStorageLevel(StorageLevel.MEMORY).build() val address = Address(myAddress.host, myAddress.port) - val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(serverNameTablePrefix = "PORT_${myAddress.port}_"), rigorousMock()) + val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock()) databases.add(database) val stateMachineFactory = { DistributedImmutableMap(database, RaftUniquenessProvider.Companion::createMap) } diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index 071ac50d57..2f75c95959 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -21,7 +21,7 @@ import net.corda.finance.contracts.asset.Cash import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.schemas.CashSchemaV1.PersistentCashState import net.corda.finance.schemas.CommercialPaperSchemaV1 -import net.corda.finance.schemas.SampleCashSchemaV3 +import net.corda.finance.sampleschemas.SampleCashSchemaV3 import net.corda.node.internal.configureDatabase import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig @@ -30,6 +30,7 @@ import net.corda.testing.contracts.* import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices import net.corda.testing.node.makeTestIdentityService +import net.corda.testing.schemas.DummyDealStateSchemaV1 import net.corda.testing.schemas.DummyLinearStateSchemaV1 import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy @@ -91,7 +92,8 @@ open class VaultQueryTests { "net.corda.testing.contracts", "net.corda.finance.contracts", CashSchemaV1::class.packageName, - DummyLinearStateSchemaV1::class.packageName) + DummyLinearStateSchemaV1::class.packageName, + DummyDealStateSchemaV1::class.packageName) private lateinit var services: MockServices private lateinit var vaultFiller: VaultFiller private lateinit var vaultFillerCashNotary: VaultFiller diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt index 34ef9e5203..c2e074f9c5 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt @@ -25,6 +25,7 @@ import net.corda.testing.contracts.* import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices import net.corda.testing.node.makeTestIdentityService +import net.corda.testing.schemas.DummyLinearStateSchemaV1 import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.After @@ -39,7 +40,8 @@ import kotlin.test.fail class VaultWithCashTest { private companion object { - val cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", CashSchemaV1::class.packageName) + private val cordappPackages = listOf( + "net.corda.testing.contracts", "net.corda.finance.contracts.asset", CashSchemaV1::class.packageName, DummyLinearStateSchemaV1::class.packageName) val BOB = TestIdentity(BOB_NAME, 80).party val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10) val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1) diff --git a/node/src/test/resources/migration/test.changelog-init.xml b/node/src/test/resources/migration/test.changelog-init.xml new file mode 100644 index 0000000000..fd1f7f199a --- /dev/null +++ b/node/src/test/resources/migration/test.changelog-init.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/CommercialPaper.kt b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/CommercialPaper.kt index c8ec2eb1fd..cdd6b83927 100644 --- a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/CommercialPaper.kt +++ b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/CommercialPaper.kt @@ -18,6 +18,7 @@ import net.corda.core.transactions.TransactionBuilder import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash import com.r3.corda.enterprise.perftestcordapp.schemas.CommercialPaperSchemaV1 import com.r3.corda.enterprise.perftestcordapp.utils.sumCashBy +import net.corda.core.crypto.toStringShort import java.time.Instant import java.util.* @@ -76,13 +77,13 @@ class CommercialPaper : Contract { override fun generateMappedObject(schema: MappedSchema): PersistentState { return when (schema) { is CommercialPaperSchemaV1 -> CommercialPaperSchemaV1.PersistentCommercialPaperState( - issuanceParty = this.issuance.party.owningKey.toBase58String(), + issuancePartyHash = this.issuance.party.owningKey.toStringShort(), issuanceRef = this.issuance.reference.bytes, - owner = this.owner.owningKey.toBase58String(), + ownerHash = this.owner.owningKey.toStringShort(), maturity = this.maturityDate, faceValue = this.faceValue.quantity, currency = this.faceValue.token.product.currencyCode, - faceValueIssuerParty = this.faceValue.token.issuer.party.owningKey.toBase58String(), + faceValueIssuerPartyHash = this.faceValue.token.issuer.party.owningKey.toStringShort(), faceValueIssuerRef = this.faceValue.token.issuer.reference.bytes ) /** Additional schema mappings would be added here (eg. CommercialPaperV2, ...) */ diff --git a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/cash/selection/CashSelectionH2Impl.kt b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/cash/selection/CashSelectionH2Impl.kt index f94ae307eb..14f834998c 100644 --- a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/cash/selection/CashSelectionH2Impl.kt +++ b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/cash/selection/CashSelectionH2Impl.kt @@ -56,9 +56,9 @@ class CashSelectionH2Impl : AbstractCashSelection() { if (notary != null) psSelectJoin.setString(++pIndex, notary.name.toString()) if (onlyFromIssuerParties.isNotEmpty()) - psSelectJoin.setObject(++pIndex, onlyFromIssuerParties.map { it.owningKey.toStringShort() as Any }.toTypedArray()) + psSelectJoin.setObject(++pIndex, onlyFromIssuerParties.map { it.owningKey.toStringShort() as Any}.toTypedArray() ) if (withIssuerRefs.isNotEmpty()) - psSelectJoin.setObject(++pIndex, withIssuerRefs.map { it.bytes as Any }.toTypedArray()) + psSelectJoin.setObject(++pIndex, withIssuerRefs.map { it.bytes.toHexString() as Any }.toTypedArray()) log.debug { psSelectJoin.toString() } psSelectJoin.executeQuery().use { rs -> diff --git a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/schemas/CashSchemaV1.kt b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/schemas/CashSchemaV1.kt index 2aabf08c27..1404f9ac88 100644 --- a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/schemas/CashSchemaV1.kt +++ b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/schemas/CashSchemaV1.kt @@ -21,6 +21,8 @@ object CashSchema */ @CordaSerializable object CashSchemaV1 : MappedSchema(schemaFamily = CashSchema.javaClass, version = 1, mappedTypes = listOf(PersistentCashState::class.java)) { + override val migrationResource = "cash-pt.changelog-master" + @Entity @Table(name = "contract_pt_cash_states", indexes = arrayOf(Index(name = "ccy_code_idx", columnList = "ccy_code"), diff --git a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/schemas/CommercialPaperSchemaV1.kt b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/schemas/CommercialPaperSchemaV1.kt index 71857824f9..0c3f2257f6 100644 --- a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/schemas/CommercialPaperSchemaV1.kt +++ b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/schemas/CommercialPaperSchemaV1.kt @@ -1,7 +1,11 @@ package com.r3.corda.enterprise.perftestcordapp.schemas + +import net.corda.core.contracts.MAX_ISSUER_REF_SIZE import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState import net.corda.core.serialization.CordaSerializable +import net.corda.core.utilities.MAX_HASH_HEX_SIZE +import org.hibernate.annotations.Type import java.time.Instant import javax.persistence.Column import javax.persistence.Entity @@ -19,20 +23,23 @@ object CommercialPaperSchema */ @CordaSerializable object CommercialPaperSchemaV1 : MappedSchema(schemaFamily = CommercialPaperSchema.javaClass, version = 1, mappedTypes = listOf(PersistentCommercialPaperState::class.java)) { + + override val migrationResource = "commercial-paper-pt.changelog-master" + @Entity @Table(name = "cp_pt_states", indexes = arrayOf(Index(name = "ccy_code_index", columnList = "ccy_code"), Index(name = "maturity_index", columnList = "maturity_instant"), Index(name = "face_value_index", columnList = "face_value"))) class PersistentCommercialPaperState( - @Column(name = "issuance_key") - var issuanceParty: String, + @Column(name = "issuance_key_hash", length = MAX_HASH_HEX_SIZE) + var issuancePartyHash: String, @Column(name = "issuance_ref") var issuanceRef: ByteArray, - @Column(name = "owner_key") - var owner: String, + @Column(name = "owner_key_hash", length = MAX_HASH_HEX_SIZE) + var ownerHash: String, @Column(name = "maturity_instant") var maturity: Instant, @@ -43,10 +50,11 @@ object CommercialPaperSchemaV1 : MappedSchema(schemaFamily = CommercialPaperSche @Column(name = "ccy_code", length = 3) var currency: String, - @Column(name = "face_value_issuer_key") - var faceValueIssuerParty: String, + @Column(name = "face_value_issuer_key_hash", length = MAX_HASH_HEX_SIZE) + var faceValueIssuerPartyHash: String, - @Column(name = "face_value_issuer_ref") + @Column(name = "face_value_issuer_ref", length = MAX_ISSUER_REF_SIZE) + @Type(type = "corda-wrapper-binary") var faceValueIssuerRef: ByteArray ) : PersistentState() } diff --git a/perftestcordapp/src/main/resources/migration/cash-pt.changelog-init.xml b/perftestcordapp/src/main/resources/migration/cash-pt.changelog-init.xml new file mode 100644 index 0000000000..1f7ae34bbd --- /dev/null +++ b/perftestcordapp/src/main/resources/migration/cash-pt.changelog-init.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/perftestcordapp/src/main/resources/migration/cash-pt.changelog-master.xml b/perftestcordapp/src/main/resources/migration/cash-pt.changelog-master.xml new file mode 100644 index 0000000000..2049c7d998 --- /dev/null +++ b/perftestcordapp/src/main/resources/migration/cash-pt.changelog-master.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/perftestcordapp/src/main/resources/migration/commercial-paper-pt.changelog-init.xml b/perftestcordapp/src/main/resources/migration/commercial-paper-pt.changelog-init.xml new file mode 100644 index 0000000000..eec5e608b3 --- /dev/null +++ b/perftestcordapp/src/main/resources/migration/commercial-paper-pt.changelog-init.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/perftestcordapp/src/main/resources/migration/commercial-paper-pt.changelog-master.xml b/perftestcordapp/src/main/resources/migration/commercial-paper-pt.changelog-master.xml new file mode 100644 index 0000000000..4b4efd4240 --- /dev/null +++ b/perftestcordapp/src/main/resources/migration/commercial-paper-pt.changelog-master.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashSelectionH2Test.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashSelectionH2Test.kt index ab9e46a684..a28e82e637 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashSelectionH2Test.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashSelectionH2Test.kt @@ -17,7 +17,9 @@ import org.junit.Test import java.util.Collections.nCopies class CashSelectionH2Test { - private val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset")) + private val mockNet = MockNetwork( + threadPerNode = true, + cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas")) @After fun cleanUp() { diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashTests.kt index dd53cbbdf7..acf6b068ce 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashTests.kt @@ -133,13 +133,19 @@ class CashTests { @Before fun setUp() { LogHelper.setLevel(NodeVaultService::class) - megaCorpServices = MockServices(listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset"), rigorousMock(), MEGA_CORP.name, MEGA_CORP_KEY) - miniCorpServices = MockServices(listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset"), rigorousMock().also { - doNothing().whenever(it).justVerifyAndRegisterIdentity(argThat { name == MINI_CORP.name }) - }, MINI_CORP.name, MINI_CORP_KEY) - val notaryServices = MockServices(listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset"), rigorousMock(), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) + megaCorpServices = MockServices( + listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas"), + rigorousMock(), MEGA_CORP.name, MEGA_CORP_KEY) + miniCorpServices = MockServices( + listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas"), + rigorousMock().also { + doNothing().whenever(it).justVerifyAndRegisterIdentity(argThat { name == MINI_CORP.name }) + }, MINI_CORP.name, MINI_CORP_KEY) + val notaryServices = MockServices( + listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas"), + rigorousMock(), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) val databaseAndServices = makeTestDatabaseAndMockServices( - cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset"), + cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas"), initialIdentityName = CordaX500Name(organisation = "Me", locality = "London", country = "GB"), keys = listOf(generateKeyPair()), identityService = makeTestIdentityService(listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY))) @@ -247,9 +253,9 @@ class CashTests { transaction { attachment(Cash.PROGRAM_ID) output(Cash.PROGRAM_ID, - Cash.State( - amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34), - owner = AnonymousParty(ALICE_PUBKEY))) + Cash.State( + amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34), + owner = AnonymousParty(ALICE_PUBKEY))) command(MINI_CORP_PUBKEY, Cash.Commands.Issue()) this.verifies() } @@ -432,9 +438,9 @@ class CashTests { attachment(Cash.PROGRAM_ID) input(Cash.PROGRAM_ID, inState) input(Cash.PROGRAM_ID, - inState.copy( - amount = 150.POUNDS `issued by` defaultIssuer, - owner = AnonymousParty(BOB_PUBKEY))) + inState.copy( + amount = 150.POUNDS `issued by` defaultIssuer, + owner = AnonymousParty(BOB_PUBKEY))) output(Cash.PROGRAM_ID, outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer)) command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" @@ -851,16 +857,17 @@ class CashTests { // Double spend. @Test fun chainCashDoubleSpendFailsWith() { - val mockService = MockServices(listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset"), rigorousMock().also { + val mockService = MockServices( + listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas"), rigorousMock().also { doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY) }, MEGA_CORP.name, MEGA_CORP_KEY) mockService.ledger(DUMMY_NOTARY) { unverifiedTransaction { attachment(Cash.PROGRAM_ID) output(Cash.PROGRAM_ID, "MEGA_CORP cash", - Cash.State( - amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), - owner = MEGA_CORP)) + Cash.State( + amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), + owner = MEGA_CORP)) } transaction { diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt index c2cc2b7115..041aff1263 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashExitFlowTests.kt @@ -29,7 +29,7 @@ class CashExitFlowTests { @Before fun start() { mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), - cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset")) + cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas")) bankOfCordaNode = mockNet.createPartyNode(BOC_NAME) bankOfCorda = bankOfCordaNode.info.identityFromX500Name(BOC_NAME) notary = mockNet.defaultNotaryIdentity diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentFlowTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentFlowTests.kt index 80c1ed495c..f4f79e8bf8 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentFlowTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentFlowTests.kt @@ -31,7 +31,8 @@ class CashIssueAndPaymentFlowTests { @Before fun start() { - mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset")) + mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), + cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas")) bankOfCordaNode = mockNet.createPartyNode(BOC_NAME) aliceNode = mockNet.createPartyNode(ALICE_NAME) bankOfCorda = bankOfCordaNode.info.chooseIdentity() diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentNoSelectionFlowTest.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentNoSelectionFlowTest.kt index d8e4f7c3b2..56f4b09b08 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentNoSelectionFlowTest.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueAndPaymentNoSelectionFlowTest.kt @@ -40,7 +40,7 @@ class CashIssueAndPayNoSelectionTests(private val anonymous: Boolean) { @Before fun start() { mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), - cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset")) + cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas")) bankOfCordaNode = mockNet.createPartyNode(BOC_NAME) aliceNode = mockNet.createPartyNode(ALICE_NAME) bankOfCorda = bankOfCordaNode.info.chooseIdentity() diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt index aea4d0ebd9..cf46c8cc06 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashIssueFlowTests.kt @@ -26,7 +26,9 @@ class CashIssueFlowTests { @Before fun start() { - mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset")) + mockNet = MockNetwork( + servicePeerAllocationStrategy = RoundRobin(), + cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas")) bankOfCordaNode = mockNet.createPartyNode(BOC_NAME) bankOfCorda = bankOfCordaNode.info.identityFromX500Name(BOC_NAME) notary = mockNet.defaultNotaryIdentity diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlowTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlowTests.kt index dda8aec4c8..a876d81420 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlowTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFlowTests.kt @@ -31,7 +31,9 @@ class CashPaymentFlowTests { @Before fun start() { - mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset")) + mockNet = MockNetwork( + servicePeerAllocationStrategy = RoundRobin(), + cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas")) bankOfCordaNode = mockNet.createPartyNode(BOC_NAME) aliceNode = mockNet.createPartyNode(ALICE_NAME) bankOfCorda = bankOfCordaNode.info.chooseIdentity() diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt index 34ff8c2a90..1ddfe67308 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt @@ -79,7 +79,8 @@ internal fun CheckpointStorage.checkpoints(): List> @RunWith(Parameterized::class) class TwoPartyTradeFlowTests(private val anonymous: Boolean) { companion object { - private val cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts") + private val cordappPackages = listOf( + "com.r3.corda.enterprise.perftestcordapp.contracts", "com.r3.corda.enterprise.perftestcordapp.schemas") @JvmStatic @Parameterized.Parameters(name = "Anonymous = {0}") fun data(): Collection = listOf(true, false) diff --git a/samples/irs-demo/web/src/main/resources/application-BankA.properties b/samples/irs-demo/web/src/main/resources/application-BankA.properties index 600944e6a5..507a605a82 100644 --- a/samples/irs-demo/web/src/main/resources/application-BankA.properties +++ b/samples/irs-demo/web/src/main/resources/application-BankA.properties @@ -1,2 +1,3 @@ corda.host=localhost:10006 -server.port=10007 \ No newline at end of file +server.port=10007 +liquibase.enabled=false \ No newline at end of file diff --git a/samples/irs-demo/web/src/main/resources/application-BankB.properties b/samples/irs-demo/web/src/main/resources/application-BankB.properties index 187c6cc5c2..9726da7b95 100644 --- a/samples/irs-demo/web/src/main/resources/application-BankB.properties +++ b/samples/irs-demo/web/src/main/resources/application-BankB.properties @@ -1,2 +1,3 @@ corda.host=localhost:10009 -server.port=10010 \ No newline at end of file +server.port=10010 +liquibase.enabled=false \ No newline at end of file diff --git a/samples/irs-demo/web/src/main/resources/application-NotaryService.properties b/samples/irs-demo/web/src/main/resources/application-NotaryService.properties index e3de4502b7..566c3fc3a0 100644 --- a/samples/irs-demo/web/src/main/resources/application-NotaryService.properties +++ b/samples/irs-demo/web/src/main/resources/application-NotaryService.properties @@ -1,2 +1,3 @@ corda.host=localhost:10003 -server.port=10004 \ No newline at end of file +server.port=10004 +liquibase.enabled=false \ No newline at end of file diff --git a/samples/irs-demo/web/src/main/resources/application.properties b/samples/irs-demo/web/src/main/resources/application.properties index fae7da6e77..8ded80ca5c 100644 --- a/samples/irs-demo/web/src/main/resources/application.properties +++ b/samples/irs-demo/web/src/main/resources/application.properties @@ -1,2 +1,3 @@ corda.user=user -corda.password=password \ No newline at end of file +corda.password=password +liquibase.enabled=false \ No newline at end of file diff --git a/samples/irs-demo/web/src/test/kotlin/net/corda/irs/web/IrsDemoWebApplicationTests.kt b/samples/irs-demo/web/src/test/kotlin/net/corda/irs/web/IrsDemoWebApplicationTests.kt index cbddd8a4b3..59f1907afe 100644 --- a/samples/irs-demo/web/src/test/kotlin/net/corda/irs/web/IrsDemoWebApplicationTests.kt +++ b/samples/irs-demo/web/src/test/kotlin/net/corda/irs/web/IrsDemoWebApplicationTests.kt @@ -8,7 +8,7 @@ import org.springframework.boot.test.mock.mockito.MockBean import org.springframework.test.context.junit4.SpringRunner @RunWith(SpringRunner::class) -@SpringBootTest(properties = arrayOf("corda.host=localhost:12345", "corda.user=user", "corda.password=password")) +@SpringBootTest(properties = arrayOf("corda.host=localhost:12345", "corda.user=user", "corda.password=password", "liquibase.enabled=false")) class IrsDemoWebApplicationTests { @MockBean lateinit var rpc: CordaRPCOps diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index 8903449bd6..9012446102 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -426,7 +426,7 @@ class DriverDSLImpl( providedName = nodeNames[0], rpcUsers = spec.rpcUsers, verifierType = spec.verifierType, - customOverrides = notaryConfig(clusterAddress) //TODO discrepancy with OS - check if 'serverNameTablePrefix' can be removed in OS + customOverrides = notaryConfig(clusterAddress) ) // All other nodes will join the cluster @@ -436,7 +436,7 @@ class DriverDSLImpl( providedName = it, rpcUsers = spec.rpcUsers, verifierType = spec.verifierType, - customOverrides = notaryConfig(nodeAddress, clusterAddress) //TODO discrepancy with OS - check if 'serverNameTablePrefix' can be removed in OS + customOverrides = notaryConfig(nodeAddress, clusterAddress) ) } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/IntegrationTest.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/IntegrationTest.kt index 4d699a4c99..79df695f84 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/IntegrationTest.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/IntegrationTest.kt @@ -30,6 +30,7 @@ abstract class IntegrationTest { @JvmStatic fun globalSetUp() { if (dbProvider.isNotEmpty()) { + runDbScript(dbProvider,"$testDbScriptDir/db-global-cleanup.sql", databaseSchemas) runDbScript(dbProvider,"$testDbScriptDir/db-global-setup.sql", databaseSchemas) } } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/schemas/DummyDealStateSchemaV1.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/schemas/DummyDealStateSchemaV1.kt index 33c6a44c8e..c1e73a5f71 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/schemas/DummyDealStateSchemaV1.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/schemas/DummyDealStateSchemaV1.kt @@ -18,6 +18,9 @@ object DummyDealStateSchema * at the time of writing. */ object DummyDealStateSchemaV1 : MappedSchema(schemaFamily = DummyDealStateSchema.javaClass, version = 1, mappedTypes = listOf(PersistentDummyDealState::class.java)) { + + override val migrationResource = "dummy-deal.changelog-init" + @Entity @Table(name = "dummy_deal_states") class PersistentDummyDealState( diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/schemas/DummyLinearStateSchemaV1.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/schemas/DummyLinearStateSchemaV1.kt index 1a610c9372..844ea0543b 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/schemas/DummyLinearStateSchemaV1.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/schemas/DummyLinearStateSchemaV1.kt @@ -4,6 +4,7 @@ import net.corda.core.contracts.ContractState import net.corda.core.identity.AbstractParty import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState +import org.hibernate.annotations.Type import java.time.Instant import java.util.* import javax.persistence.* @@ -18,6 +19,9 @@ object DummyLinearStateSchema * at the time of writing. */ object DummyLinearStateSchemaV1 : MappedSchema(schemaFamily = DummyLinearStateSchema.javaClass, version = 1, mappedTypes = listOf(PersistentDummyLinearState::class.java)) { + + override val migrationResource = "dummy-linear-v1.changelog-init" + @Entity @Table(name = "dummy_linear_states", indexes = arrayOf(Index(name = "external_id_idx", columnList = "external_id"), @@ -27,6 +31,9 @@ object DummyLinearStateSchemaV1 : MappedSchema(schemaFamily = DummyLinearStateSc /** X500Name of participant parties **/ @ElementCollection + @CollectionTable(name="state_participants",joinColumns = arrayOf( + JoinColumn(name = "output_index", referencedColumnName = "output_index"), + JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"))) var participants: MutableSet, /** @@ -36,6 +43,7 @@ object DummyLinearStateSchemaV1 : MappedSchema(schemaFamily = DummyLinearStateSc var externalId: String?, @Column(name = "uuid", nullable = false) + @Type(type = "uuid-char") var uuid: UUID, /** diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/schemas/DummyLinearStateSchemaV2.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/schemas/DummyLinearStateSchemaV2.kt index 35ad6880c5..0b9f942678 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/schemas/DummyLinearStateSchemaV2.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/schemas/DummyLinearStateSchemaV2.kt @@ -14,6 +14,9 @@ import javax.persistence.Table */ object DummyLinearStateSchemaV2 : MappedSchema(schemaFamily = DummyLinearStateSchema.javaClass, version = 2, mappedTypes = listOf(PersistentDummyLinearState::class.java)) { + + override val migrationResource = "dummy-linear-v2.changelog-init" + @Entity @Table(name = "dummy_linear_states_v2") class PersistentDummyLinearState( diff --git a/testing/test-utils/src/main/resources/database-scripts/azure-sql/db-global-cleanup.sql b/testing/test-utils/src/main/resources/database-scripts/azure-sql/db-global-cleanup.sql index c77a50dfb7..55ec8a0593 100644 --- a/testing/test-utils/src/main/resources/database-scripts/azure-sql/db-global-cleanup.sql +++ b/testing/test-utils/src/main/resources/database-scripts/azure-sql/db-global-cleanup.sql @@ -30,12 +30,7 @@ DROP TABLE IF EXISTS ${schema}.children; DROP TABLE IF EXISTS ${schema}.parents; DROP TABLE IF EXISTS ${schema}.contract_cash_states; DROP TABLE IF EXISTS ${schema}.messages; -DROP TABLE IF EXISTS ${schema}.DummyDealStateSchemaV1$PersistentDummyDealState_participants; -DROP TABLE IF EXISTS ${schema}.DummyLinearStateSchemaV1$PersistentDummyLinearState_participants; -DROP TABLE IF EXISTS ${schema}.DummyLinearStateSchemaV2$PersistentDummyLinearState_participants; -DROP TABLE IF EXISTS ${schema}.SampleCashSchemaV2$PersistentCashState_participants; -DROP TABLE IF EXISTS ${schema}.SampleCashSchemaV3$PersistentCashState_participants; -DROP TABLE IF EXISTS ${schema}.SampleCommercialPaperSchemaV2$PersistentCommercialPaperState_participants; +DROP TABLE IF EXISTS ${schema}.state_participants; DROP TABLE IF EXISTS ${schema}.cash_states_v2; DROP TABLE IF EXISTS ${schema}.cash_states_v3; DROP TABLE IF EXISTS ${schema}.cp_states_v2; @@ -43,6 +38,8 @@ DROP TABLE IF EXISTS ${schema}.dummy_deal_states; DROP TABLE IF EXISTS ${schema}.dummy_linear_states; DROP TABLE IF EXISTS ${schema}.dummy_linear_states_v2; DROP TABLE IF EXISTS ${schema}.node_mutual_exclusion; +DROP TABLE IF EXISTS ${schema}.DATABASECHANGELOG; +DROP TABLE IF EXISTS ${schema}.DATABASECHANGELOGLOCK; DROP SEQUENCE IF EXISTS ${schema}.hibernate_sequence; DROP USER IF EXISTS ${schema}; DROP SCHEMA IF EXISTS ${schema}; \ No newline at end of file diff --git a/testing/test-utils/src/main/resources/database-scripts/azure-sql/db-setup.sql b/testing/test-utils/src/main/resources/database-scripts/azure-sql/db-setup.sql index fc4542c2fa..9357df9527 100644 --- a/testing/test-utils/src/main/resources/database-scripts/azure-sql/db-setup.sql +++ b/testing/test-utils/src/main/resources/database-scripts/azure-sql/db-setup.sql @@ -30,12 +30,7 @@ DROP TABLE IF EXISTS ${schema}.children; DROP TABLE IF EXISTS ${schema}.parents; DROP TABLE IF EXISTS ${schema}.contract_cash_states; DROP TABLE IF EXISTS ${schema}.messages; -DROP TABLE IF EXISTS ${schema}.DummyDealStateSchemaV1$PersistentDummyDealState_participants; -DROP TABLE IF EXISTS ${schema}.DummyLinearStateSchemaV1$PersistentDummyLinearState_participants; -DROP TABLE IF EXISTS ${schema}.DummyLinearStateSchemaV2$PersistentDummyLinearState_participants; -DROP TABLE IF EXISTS ${schema}.SampleCashSchemaV2$PersistentCashState_participants; -DROP TABLE IF EXISTS ${schema}.SampleCashSchemaV3$PersistentCashState_participants; -DROP TABLE IF EXISTS ${schema}.SampleCommercialPaperSchemaV2$PersistentCommercialPaperState_participants; +DROP TABLE IF EXISTS ${schema}.state_participants; DROP TABLE IF EXISTS ${schema}.cash_states_v2; DROP TABLE IF EXISTS ${schema}.cash_states_v3; DROP TABLE IF EXISTS ${schema}.cp_states_v2; @@ -43,4 +38,6 @@ DROP TABLE IF EXISTS ${schema}.dummy_deal_states; DROP TABLE IF EXISTS ${schema}.dummy_linear_states; DROP TABLE IF EXISTS ${schema}.dummy_linear_states_v2; DROP TABLE IF EXISTS ${schema}.node_mutual_exclusion; +DROP TABLE IF EXISTS ${schema}.DATABASECHANGELOG; +DROP TABLE IF EXISTS ${schema}.DATABASECHANGELOGLOCK; DROP SEQUENCE IF EXISTS ${schema}.hibernate_sequence; \ No newline at end of file diff --git a/testing/test-utils/src/main/resources/database-scripts/sql-server/db-global-cleanup.sql b/testing/test-utils/src/main/resources/database-scripts/sql-server/db-global-cleanup.sql index 8a1d6cc4e8..2faae150ee 100644 --- a/testing/test-utils/src/main/resources/database-scripts/sql-server/db-global-cleanup.sql +++ b/testing/test-utils/src/main/resources/database-scripts/sql-server/db-global-cleanup.sql @@ -30,12 +30,7 @@ DROP TABLE IF EXISTS ${schema}.children; DROP TABLE IF EXISTS ${schema}.parents; DROP TABLE IF EXISTS ${schema}.contract_cash_states; DROP TABLE IF EXISTS ${schema}.messages; -DROP TABLE IF EXISTS ${schema}.DummyDealStateSchemaV1$PersistentDummyDealState_participants; -DROP TABLE IF EXISTS ${schema}.DummyLinearStateSchemaV1$PersistentDummyLinearState_participants; -DROP TABLE IF EXISTS ${schema}.DummyLinearStateSchemaV2$PersistentDummyLinearState_participants; -DROP TABLE IF EXISTS ${schema}.SampleCashSchemaV2$PersistentCashState_participants; -DROP TABLE IF EXISTS ${schema}.SampleCashSchemaV3$PersistentCashState_participants; -DROP TABLE IF EXISTS ${schema}.SampleCommercialPaperSchemaV2$PersistentCommercialPaperState_participants; +DROP TABLE IF EXISTS ${schema}.state_participants; DROP TABLE IF EXISTS ${schema}.cash_states_v2; DROP TABLE IF EXISTS ${schema}.cash_states_v3; DROP TABLE IF EXISTS ${schema}.cp_states_v2; @@ -44,6 +39,8 @@ DROP TABLE IF EXISTS ${schema}.dummy_linear_states; DROP TABLE IF EXISTS ${schema}.dummy_linear_states_v2; DROP TABLE IF EXISTS ${schema}.node_mutual_exclusion; DROP SEQUENCE IF EXISTS ${schema}.hibernate_sequence; +DROP TABLE IF EXISTS ${schema}.DATABASECHANGELOG; +DROP TABLE IF EXISTS ${schema}.DATABASECHANGELOGLOCK; DROP USER IF EXISTS ${schema}; DROP LOGIN ${schema}; DROP SCHEMA IF EXISTS ${schema}; \ No newline at end of file diff --git a/testing/test-utils/src/main/resources/database-scripts/sql-server/db-global-setup.sql b/testing/test-utils/src/main/resources/database-scripts/sql-server/db-global-setup.sql index 807bd5ad98..a9f9ef63d9 100644 --- a/testing/test-utils/src/main/resources/database-scripts/sql-server/db-global-setup.sql +++ b/testing/test-utils/src/main/resources/database-scripts/sql-server/db-global-setup.sql @@ -30,12 +30,7 @@ DROP TABLE IF EXISTS ${schema}.children; DROP TABLE IF EXISTS ${schema}.parents; DROP TABLE IF EXISTS ${schema}.contract_cash_states; DROP TABLE IF EXISTS ${schema}.messages; -DROP TABLE IF EXISTS ${schema}.DummyDealStateSchemaV1$PersistentDummyDealState_participants; -DROP TABLE IF EXISTS ${schema}.DummyLinearStateSchemaV1$PersistentDummyLinearState_participants; -DROP TABLE IF EXISTS ${schema}.DummyLinearStateSchemaV2$PersistentDummyLinearState_participants; -DROP TABLE IF EXISTS ${schema}.SampleCashSchemaV2$PersistentCashState_participants; -DROP TABLE IF EXISTS ${schema}.SampleCashSchemaV3$PersistentCashState_participants; -DROP TABLE IF EXISTS ${schema}.SampleCommercialPaperSchemaV2$PersistentCommercialPaperState_participants; +DROP TABLE IF EXISTS ${schema}.state_participants; DROP TABLE IF EXISTS ${schema}.cash_states_v2; DROP TABLE IF EXISTS ${schema}.cash_states_v3; DROP TABLE IF EXISTS ${schema}.cp_states_v2; @@ -44,6 +39,8 @@ DROP TABLE IF EXISTS ${schema}.dummy_linear_states; DROP TABLE IF EXISTS ${schema}.dummy_linear_states_v2; DROP TABLE IF EXISTS ${schema}.node_mutual_exclusion; DROP SEQUENCE IF EXISTS ${schema}.hibernate_sequence; +DROP TABLE IF EXISTS ${schema}.DATABASECHANGELOG; +DROP TABLE IF EXISTS ${schema}.DATABASECHANGELOGLOCK; DROP USER IF EXISTS ${schema}; DROP LOGIN ${schema}; DROP SCHEMA IF EXISTS ${schema}; diff --git a/testing/test-utils/src/main/resources/database-scripts/sql-server/db-setup.sql b/testing/test-utils/src/main/resources/database-scripts/sql-server/db-setup.sql index 8658cb9acf..da348a5a87 100644 --- a/testing/test-utils/src/main/resources/database-scripts/sql-server/db-setup.sql +++ b/testing/test-utils/src/main/resources/database-scripts/sql-server/db-setup.sql @@ -30,12 +30,7 @@ DROP TABLE IF EXISTS ${schema}.children; DROP TABLE IF EXISTS ${schema}.parents; DROP TABLE IF EXISTS ${schema}.contract_cash_states; DROP TABLE IF EXISTS ${schema}.messages; -DROP TABLE IF EXISTS ${schema}.DummyDealStateSchemaV1$PersistentDummyDealState_participants; -DROP TABLE IF EXISTS ${schema}.DummyLinearStateSchemaV1$PersistentDummyLinearState_participants; -DROP TABLE IF EXISTS ${schema}.DummyLinearStateSchemaV2$PersistentDummyLinearState_participants; -DROP TABLE IF EXISTS ${schema}.SampleCashSchemaV2$PersistentCashState_participants; -DROP TABLE IF EXISTS ${schema}.SampleCashSchemaV3$PersistentCashState_participants; -DROP TABLE IF EXISTS ${schema}.SampleCommercialPaperSchemaV2$PersistentCommercialPaperState_participants; +DROP TABLE IF EXISTS ${schema}.state_participants; DROP TABLE IF EXISTS ${schema}.cash_states_v2; DROP TABLE IF EXISTS ${schema}.cash_states_v3; DROP TABLE IF EXISTS ${schema}.cp_states_v2; @@ -44,6 +39,8 @@ DROP TABLE IF EXISTS ${schema}.dummy_linear_states; DROP TABLE IF EXISTS ${schema}.dummy_linear_states_v2; DROP TABLE IF EXISTS ${schema}.node_mutual_exclusion; DROP SEQUENCE IF EXISTS ${schema}.hibernate_sequence; +DROP TABLE IF EXISTS ${schema}.DATABASECHANGELOG; +DROP TABLE IF EXISTS ${schema}.DATABASECHANGELOGLOCK; IF NOT EXISTS (SELECT schema_name FROM information_schema.schemata WHERE schema_name = '${schema}') EXEC('CREATE SCHEMA ${schema}'); IF NOT EXISTS (SELECT * FROM sys.sysusers WHERE name='${schema}') CREATE USER ${schema} FOR LOGIN ${schema} WITH DEFAULT_SCHEMA = ${schema}; GRANT ALTER, DELETE, EXECUTE, INSERT, REFERENCES, SELECT, UPDATE, VIEW DEFINITION ON SCHEMA::${schema} TO ${schema}; diff --git a/testing/test-utils/src/main/resources/migration/dummy-deal.changelog-init.xml b/testing/test-utils/src/main/resources/migration/dummy-deal.changelog-init.xml new file mode 100644 index 0000000000..a5f6480277 --- /dev/null +++ b/testing/test-utils/src/main/resources/migration/dummy-deal.changelog-init.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/test-utils/src/main/resources/migration/dummy-linear-v1.changelog-init.xml b/testing/test-utils/src/main/resources/migration/dummy-linear-v1.changelog-init.xml new file mode 100644 index 0000000000..f63061335d --- /dev/null +++ b/testing/test-utils/src/main/resources/migration/dummy-linear-v1.changelog-init.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/test-utils/src/main/resources/migration/dummy-linear-v2.changelog-init.xml b/testing/test-utils/src/main/resources/migration/dummy-linear-v2.changelog-init.xml new file mode 100644 index 0000000000..777fc22329 --- /dev/null +++ b/testing/test-utils/src/main/resources/migration/dummy-linear-v2.changelog-init.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt index 05d73b4aef..a7383cfce1 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt @@ -138,7 +138,7 @@ class VerifierTests : IntegrationTest() { @Test fun `single verifier works with a node`() { verifierDriver( - extraCordappPackagesToScan = listOf("net.corda.finance.contracts"), + extraCordappPackagesToScan = listOf("net.corda.finance.contracts", "net.corda.finance.schemas"), notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, verifierType = VerifierType.OutOfProcess)) ) { val aliceNode = startNode(providedName = ALICE_NAME).getOrThrow()