CORDA-3200 Use PersistentIdentityMigrationBuilder instead of schema a… (#5449)

* CORDA-3200 Use PersistentIdentityMigrationBuilder instead of schema and correctly name table using node prefix

* CORDA-3200 Remove hacky test migration from PersistentIdentityMigrationNewTable into a unit test to fix postgres failure
This commit is contained in:
willhr3 2019-09-10 10:51:38 +01:00 committed by Roger Willis
parent eece19cce0
commit 4fb1787f1e
4 changed files with 56 additions and 121 deletions

View File

@ -44,26 +44,14 @@ class PersistentIdentityMigrationNewTable : CordaMigration() {
logger.error("Cannot migrate persistent states: Liquibase failed to provide a suitable database connection")
throw PersistentIdentitiesMigrationException("Cannot migrate persistent states as liquibase failed to provide a suitable database connection")
}
initialiseNodeServices(database, setOf(PersistentIdentitiesMigrationSchemaV1))
initialiseNodeServices(database, setOf(PersistentIdentitiesMigrationSchemaBuilder.getMappedSchema()))
val connection = database.connection as JdbcConnection
doAndTestMigration(connection)
}
private fun doAndTestMigration(connection: JdbcConnection) {
val alice = TestIdentity(CordaX500Name("Alice Corp", "Madrid", "ES"), 70)
val pkHash = addTestMapping(connection, alice)
// Extract data from old table needed to populate the new table
val keyPartiesMap = extractKeyParties(connection)
keyPartiesMap.forEach {
insertEntry(connection, it)
}
verifyTestMigration(connection, pkHash, alice.name.toString())
deleteTestMapping(connection, pkHash)
}
private fun extractKeyParties(connection: JdbcConnection): Map<String, CordaX500Name> {
@ -104,41 +92,6 @@ class PersistentIdentityMigrationNewTable : CordaMigration() {
override fun validate(database: Database?): ValidationErrors? {
return null
}
private fun addTestMapping(connection: JdbcConnection, testIdentity: TestIdentity): String {
val pkHash = UUID.randomUUID().toString()
val cert = testIdentity.identity.certPath.encoded
connection.prepareStatement("INSERT INTO node_identities (pk_hash, identity_value) VALUES (?,?)").use {
it.setString(1, pkHash)
it.setBytes(2, cert)
it.executeUpdate()
}
return pkHash
}
private fun deleteTestMapping(connection: JdbcConnection, pkHash: String) {
connection.prepareStatement("DELETE FROM node_identities WHERE pk_hash = ?").use {
it.setString(1, pkHash)
it.executeUpdate()
}
}
private fun verifyTestMigration(connection: JdbcConnection, pk: String, name: String) {
connection.createStatement().use {
try {
val rs = it.executeQuery("SELECT (pk_hash, name) FROM node_identities_no_cert")
while (rs.next()) {
val result = rs.getString(1)
require(result.contains(pk))
require(result.contains(name))
}
rs.close()
} catch (e: Exception) {
logger.error(e.localizedMessage)
}
}
}
}
/**
@ -150,49 +103,17 @@ class PersistentIdentityMigrationNewTable : CordaMigration() {
*/
object PersistentIdentitiesMigrationSchema
object PersistentIdentitiesMigrationSchemaV1 : MappedSchema(schemaFamily = PersistentIdentitiesMigrationSchema.javaClass, version = 1,
mappedTypes = listOf(
DBTransactionStorage.DBTransaction::class.java,
PersistentIdentityService.PersistentPublicKeyHashToCertificate::class.java,
PersistentIdentityService.PersistentPartyToPublicKeyHash::class.java,
PersistentIdentityService.PersistentPublicKeyHashToParty::class.java,
BasicHSMKeyManagementService.PersistentKey::class.java,
NodeAttachmentService.DBAttachment::class.java,
DBNetworkParametersStorage.PersistentNetworkParameters::class.java
)
)
class PersistentIdentitiesMigrationException(msg: String, cause: Exception? = null) : Exception(msg, cause)
/**
* A class that encapsulates a test identity containing a [CordaX500Name] and a [KeyPair]. Duplicate of [net.corda.testing.core.TestIdentity]
* to avoid circular dependencies.
*/
private class TestIdentity(val name: CordaX500Name, val keyPair: KeyPair) {
/** Creates an identity with a deterministic [keyPair] i.e. same [entropy] same keyPair. */
@JvmOverloads
constructor(name: CordaX500Name, entropy: Long, signatureScheme: SignatureScheme = Crypto.DEFAULT_SIGNATURE_SCHEME)
: this(name, Crypto.deriveKeyPairFromEntropy(signatureScheme, BigInteger.valueOf(entropy)))
val publicKey: PublicKey get() = keyPair.public
val party: Party = Party(name, publicKey)
val identity: PartyAndCertificate by lazy { getTestPartyAndCertificate(party) } // Often not needed.
fun getTestPartyAndCertificate(party: Party): PartyAndCertificate {
val trustRoot: X509Certificate = DEV_ROOT_CA.certificate
val intermediate: CertificateAndKeyPair = DEV_INTERMEDIATE_CA
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediate, party.name)
val identityCert = X509Utilities.createCertificate(
CertificateType.LEGAL_IDENTITY,
nodeCaCert,
nodeCaKeyPair,
party.name.x500Principal,
party.owningKey)
val certPath = X509Utilities.buildCertPath(identityCert, nodeCaCert, intermediate.certificate, trustRoot)
return PartyAndCertificate(certPath)
}
}
object PersistentIdentitiesMigrationSchemaBuilder {
fun getMappedSchema() =
MappedSchema(schemaFamily = PersistentIdentitiesMigrationSchema.javaClass, version = 1,
mappedTypes = listOf(
DBTransactionStorage.DBTransaction::class.java,
PersistentIdentityService.PersistentPublicKeyHashToCertificate::class.java,
PersistentIdentityService.PersistentPartyToPublicKeyHash::class.java,
PersistentIdentityService.PersistentPublicKeyHashToParty::class.java,
BasicHSMKeyManagementService.PersistentKey::class.java,
NodeAttachmentService.DBAttachment::class.java,
DBNetworkParametersStorage.PersistentNetworkParameters::class.java
))
}
class PersistentIdentitiesMigrationException(msg: String, cause: Exception? = null) : Exception(msg, cause)

View File

@ -5,7 +5,7 @@
logicalFilePath="migration/node-services.changelog-init.xml">
<changeSet author="R3.Corda" id="add-new-persistence-table">
<createTable tableName="identities_no_cert">
<createTable tableName="node_identities_no_cert">
<column name="pk_hash" type="NVARCHAR(130)">
<constraints nullable="false"/>
</column>

View File

@ -27,8 +27,10 @@ import org.junit.ClassRule
import org.junit.Test
import org.mockito.Mockito
import java.security.KeyPair
import java.sql.Connection
import java.time.Clock
import java.time.Duration
import java.util.*
class PersistentIdentityMigrationNewTableTest{
companion object {
@ -83,15 +85,9 @@ class PersistentIdentityMigrationNewTableTest{
@Test
fun `migrate identities to new table`() {
/**
* TODO - We have to mock every statement/ result to test this properly.
*
* The workaround for now is the [PersistentIdentitiesMigration.addTestMapping] and
* [PersistentIdentitiesMigration.deleteTestMapping] methods that allow us to see the migration occur properly during debugging.
*
* Since [PersistentIdentitiesMigration] implements [CordaMigration] the migration will run when the DB is setup.
*/
PersistentIdentityMigrationNewTable()
val pkHash = addTestMapping(cordaDB.dataSource.connection, alice)
PersistentIdentityMigrationNewTable()
verifyTestMigration(cordaDB.dataSource.connection, pkHash, alice.name.toString())
}
private fun saveAllIdentities(identities: List<PartyAndCertificate>) {
@ -137,4 +133,39 @@ class PersistentIdentityMigrationNewTableTest{
session.save(persistentParams)
}
}
private fun addTestMapping(connection: Connection, testIdentity: TestIdentity): String {
val pkHash = UUID.randomUUID().toString()
val cert = testIdentity.identity.certPath.encoded
connection.prepareStatement("INSERT INTO node_identities (pk_hash, identity_value) VALUES (?,?)").use {
it.setString(1, pkHash)
it.setBytes(2, cert)
it.executeUpdate()
}
return pkHash
}
// private fun deleteTestMapping(connection: Connection, pkHash: String) {
// connection.prepareStatement("DELETE FROM node_identities WHERE pk_hash = ?").use {
// it.setString(1, pkHash)
// it.executeUpdate()
// }
// }
private fun verifyTestMigration(connection: Connection, pk: String, name: String) {
connection.createStatement().use {
try {
val rs = it.executeQuery("SELECT (pk_hash, name) FROM node_identities_no_cert")
while (rs.next()) {
val result = rs.getString(1)
require(result.contains(pk))
require(result.contains(name))
}
rs.close()
} catch (e: Exception) {
println(e.localizedMessage)
}
}
}
}

View File

@ -1,42 +1,25 @@
package net.corda.node.services.persistence
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.Amount
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.node.services.KeyManagementService
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.finance.DOLLARS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.AbstractCashFlow
import net.corda.finance.issuedBy
import net.corda.node.migration.VaultStateMigrationTest.Companion.bankOfCorda
import net.corda.node.services.identity.PersistentIdentityService
import net.corda.node.services.keys.BasicHSMKeyManagementService
import net.corda.node.services.keys.E2ETestKeyManagementService
import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.BOC_NAME
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity
import net.corda.testing.internal.TestingNamedCacheFactory
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetworkParameters
import net.corda.testing.node.MockServices
import net.corda.testing.node.StartedMockNode
import net.corda.testing.node.internal.FINANCE_CORDAPPS
import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.TestStartedNode
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import java.util.*
import kotlin.test.assertEquals
class HibernateColumnConverterTests {