[CORDA-2636] Ensure states created with contract upgrades can be migrated (#4786)

* Ensure states created with contract upgrades can be migrated

* Remove line from api-current.txt representing an uncallable constructor
This commit is contained in:
JamesHR3 2019-02-19 09:48:39 +00:00 committed by Tommy Lillehagen
parent 21d32681ff
commit efabab35c4
13 changed files with 40 additions and 22 deletions

View File

@ -7744,7 +7744,6 @@ public class net.corda.testing.node.MockServices extends java.lang.Object implem
public <init>(net.corda.core.identity.CordaX500Name, net.corda.core.node.services.IdentityService) public <init>(net.corda.core.identity.CordaX500Name, net.corda.core.node.services.IdentityService)
public <init>(net.corda.core.identity.CordaX500Name, net.corda.core.node.services.IdentityService, int, kotlin.jvm.internal.DefaultConstructorMarker) public <init>(net.corda.core.identity.CordaX500Name, net.corda.core.node.services.IdentityService, int, kotlin.jvm.internal.DefaultConstructorMarker)
public <init>(net.corda.core.identity.CordaX500Name, net.corda.core.node.services.IdentityService, java.security.KeyPair, java.security.KeyPair...) public <init>(net.corda.core.identity.CordaX500Name, net.corda.core.node.services.IdentityService, java.security.KeyPair, java.security.KeyPair...)
public <init>(net.corda.node.cordapp.CordappLoader, net.corda.core.node.services.IdentityService, net.corda.core.node.NetworkParameters, net.corda.testing.core.TestIdentity, java.security.KeyPair[], net.corda.core.node.services.KeyManagementService, kotlin.jvm.internal.DefaultConstructorMarker)
public <init>(net.corda.testing.core.TestIdentity, net.corda.core.node.NetworkParameters, net.corda.testing.core.TestIdentity...) public <init>(net.corda.testing.core.TestIdentity, net.corda.core.node.NetworkParameters, net.corda.testing.core.TestIdentity...)
public <init>(net.corda.testing.core.TestIdentity, net.corda.testing.core.TestIdentity...) public <init>(net.corda.testing.core.TestIdentity, net.corda.testing.core.TestIdentity...)
public final void addMockCordapp(String) public final void addMockCordapp(String)

View File

@ -307,12 +307,12 @@ object AttachmentsClassLoaderBuilder {
* *
* @param txId The transaction ID that triggered this request; it's unused except for error messages and exceptions that can occur during setup. * @param txId The transaction ID that triggered this request; it's unused except for error messages and exceptions that can occur during setup.
*/ */
fun <T> withAttachmentsClassloaderContext(attachments: List<Attachment>, params: NetworkParameters, txId: SecureHash, block: (ClassLoader) -> T): T { fun <T> withAttachmentsClassloaderContext(attachments: List<Attachment>, params: NetworkParameters, txId: SecureHash, parent: ClassLoader = ClassLoader.getSystemClassLoader(), block: (ClassLoader) -> T): T {
val attachmentIds = attachments.map { it.id }.toSet() val attachmentIds = attachments.map { it.id }.toSet()
val serializationContext = cache.computeIfAbsent(Key(attachmentIds, params)) { val serializationContext = cache.computeIfAbsent(Key(attachmentIds, params)) {
// Create classloader and load serializers, whitelisted classes // Create classloader and load serializers, whitelisted classes
val transactionClassLoader = AttachmentsClassLoader(attachments, params, txId) val transactionClassLoader = AttachmentsClassLoader(attachments, params, txId, parent)
val serializers = createInstancesOfClassesImplementing(transactionClassLoader, SerializationCustomSerializer::class.java) val serializers = createInstancesOfClassesImplementing(transactionClassLoader, SerializationCustomSerializer::class.java)
val whitelistedClasses = ServiceLoader.load(SerializationWhitelist::class.java, transactionClassLoader) val whitelistedClasses = ServiceLoader.load(SerializationWhitelist::class.java, transactionClassLoader)
.flatMap { it.whitelist } .flatMap { it.whitelist }

View File

@ -1,4 +1,4 @@
package net.corda.node.cordapp package net.corda.nodeapi.internal.cordapp
import net.corda.core.cordapp.Cordapp import net.corda.core.cordapp.Cordapp
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic

View File

@ -12,6 +12,7 @@ import net.corda.core.identity.CordaX500Name
import net.corda.nodeapi.internal.MigrationHelpers.getMigrationResource import net.corda.nodeapi.internal.MigrationHelpers.getMigrationResource
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.nodeapi.internal.cordapp.CordappLoader
import sun.security.x509.X500Name import sun.security.x509.X500Name
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.InputStream import java.io.InputStream
@ -28,7 +29,7 @@ class SchemaMigration(
val schemas: Set<MappedSchema>, val schemas: Set<MappedSchema>,
val dataSource: DataSource, val dataSource: DataSource,
private val databaseConfig: DatabaseConfig, private val databaseConfig: DatabaseConfig,
private val classLoader: ClassLoader = Thread.currentThread().contextClassLoader, cordappLoader: CordappLoader? = null,
private val currentDirectory: Path?, private val currentDirectory: Path?,
private val ourName: CordaX500Name) { private val ourName: CordaX500Name) {
@ -36,8 +37,15 @@ class SchemaMigration(
private val logger = contextLogger() private val logger = contextLogger()
const val NODE_BASE_DIR_KEY = "liquibase.nodeDaseDir" const val NODE_BASE_DIR_KEY = "liquibase.nodeDaseDir"
const val NODE_X500_NAME = "liquibase.nodeName" const val NODE_X500_NAME = "liquibase.nodeName"
val loader = ThreadLocal<CordappLoader>()
} }
init {
loader.set(cordappLoader)
}
private val classLoader = cordappLoader?.appClassLoader ?: Thread.currentThread().contextClassLoader
/** /**
* Main entry point to the schema migration. * Main entry point to the schema migration.
* Called during node startup. * Called during node startup.

View File

@ -16,7 +16,7 @@ import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.node.cordapp.CordappLoader import net.corda.nodeapi.internal.cordapp.CordappLoader
import net.corda.node.internal.cordapp.CordappProviderImpl import net.corda.node.internal.cordapp.CordappProviderImpl
import net.corda.node.internal.cordapp.JarScanningCordappLoader import net.corda.node.internal.cordapp.JarScanningCordappLoader
import net.corda.nodeapi.internal.AttachmentsClassLoaderStaticContractTests.AttachmentDummyContract.Companion.ATTACHMENT_PROGRAM_ID import net.corda.nodeapi.internal.AttachmentsClassLoaderStaticContractTests.AttachmentDummyContract.Companion.ATTACHMENT_PROGRAM_ID

View File

@ -34,7 +34,7 @@ import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.minutes import net.corda.core.utilities.minutes
import net.corda.node.CordaClock import net.corda.node.CordaClock
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.cordapp.CordappLoader import net.corda.nodeapi.internal.cordapp.CordappLoader
import net.corda.node.internal.classloading.requireAnnotation import net.corda.node.internal.classloading.requireAnnotation
import net.corda.node.internal.cordapp.* import net.corda.node.internal.cordapp.*
import net.corda.node.internal.rpc.proxies.AuthenticatedRpcOpsProxy import net.corda.node.internal.rpc.proxies.AuthenticatedRpcOpsProxy
@ -762,7 +762,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
protected open fun startDatabase() { protected open fun startDatabase() {
val props = configuration.dataSourceProperties val props = configuration.dataSourceProperties
if (props.isEmpty) throw DatabaseConfigurationException("There must be a database configured.") if (props.isEmpty) throw DatabaseConfigurationException("There must be a database configured.")
database.startHikariPool(props, configuration.database, schemaService.internalSchemas(), metricRegistry, this.cordappLoader.appClassLoader, configuration.baseDirectory, configuration.myLegalName) database.startHikariPool(props, configuration.database, schemaService.internalSchemas(), metricRegistry, this.cordappLoader, configuration.baseDirectory, configuration.myLegalName)
// Now log the vendor string as this will also cause a connection to be tested eagerly. // Now log the vendor string as this will also cause a connection to be tested eagerly.
logVendorString(database, log) logVendorString(database, log)
} }
@ -1074,10 +1074,10 @@ fun createCordaPersistence(databaseConfig: DatabaseConfig,
return CordaPersistence(databaseConfig, schemaService.schemaOptions.keys, jdbcUrl, cacheFactory, attributeConverters, customClassLoader) return CordaPersistence(databaseConfig, schemaService.schemaOptions.keys, jdbcUrl, cacheFactory, attributeConverters, customClassLoader)
} }
fun CordaPersistence.startHikariPool(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemas: Set<MappedSchema>, metricRegistry: MetricRegistry? = null, classloader: ClassLoader = Thread.currentThread().contextClassLoader, currentDir: Path? = null, ourName: CordaX500Name) { fun CordaPersistence.startHikariPool(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemas: Set<MappedSchema>, metricRegistry: MetricRegistry? = null, cordappLoader: CordappLoader? = null, currentDir: Path? = null, ourName: CordaX500Name) {
try { try {
val dataSource = DataSourceFactory.createDataSource(hikariProperties, metricRegistry = metricRegistry) val dataSource = DataSourceFactory.createDataSource(hikariProperties, metricRegistry = metricRegistry)
val schemaMigration = SchemaMigration(schemas, dataSource, databaseConfig, classloader, currentDir, ourName) val schemaMigration = SchemaMigration(schemas, dataSource, databaseConfig, cordappLoader, currentDir, ourName)
schemaMigration.nodeStartup(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L }) schemaMigration.nodeStartup(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L })
start(dataSource) start(dataSource)
} catch (ex: Exception) { } catch (ex: Exception) {

View File

@ -12,7 +12,7 @@ import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.node.cordapp.CordappLoader import net.corda.nodeapi.internal.cordapp.CordappLoader
import net.corda.node.services.persistence.AttachmentStorageInternal import net.corda.node.services.persistence.AttachmentStorageInternal
import java.net.URL import java.net.URL
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap

View File

@ -20,7 +20,7 @@ import net.corda.core.serialization.SerializationWhitelist
import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SerializeAsToken
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.cordapp.CordappLoader import net.corda.nodeapi.internal.cordapp.CordappLoader
import net.corda.nodeapi.internal.coreContractClasses import net.corda.nodeapi.internal.coreContractClasses
import net.corda.serialization.internal.DefaultWhitelist import net.corda.serialization.internal.DefaultWhitelist
import org.apache.commons.collections4.map.LRUMap import org.apache.commons.collections4.map.LRUMap

View File

@ -1,6 +1,7 @@
package net.corda.node.migration package net.corda.node.migration
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.cordapp.CordappContext
import net.corda.core.cordapp.CordappProvider import net.corda.core.cordapp.CordappProvider
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.internal.deserialiseComponentGroup import net.corda.core.internal.deserialiseComponentGroup
@ -8,10 +9,7 @@ import net.corda.core.internal.div
import net.corda.core.internal.readObject import net.corda.core.internal.readObject
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters
import net.corda.core.node.ServicesForResolution import net.corda.core.node.ServicesForResolution
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.*
import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.NetworkParametersService
import net.corda.core.node.services.TransactionStorage
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder import net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder
import net.corda.core.transactions.ContractUpgradeLedgerTransaction import net.corda.core.transactions.ContractUpgradeLedgerTransaction
@ -23,6 +21,7 @@ import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
import net.corda.nodeapi.internal.network.SignedNetworkParameters import net.corda.nodeapi.internal.network.SignedNetworkParameters
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.SchemaMigration import net.corda.nodeapi.internal.persistence.SchemaMigration
import sun.reflect.generics.reflectiveObjects.NotImplementedException
import java.nio.file.Paths import java.nio.file.Paths
import java.time.Clock import java.time.Clock
import java.time.Duration import java.time.Duration
@ -39,7 +38,19 @@ class MigrationServicesForResolution(
val logger = contextLogger() val logger = contextLogger()
} }
override val cordappProvider: CordappProvider override val cordappProvider: CordappProvider
get() = throw NotImplementedError() get() = object : CordappProvider {
val cordappLoader = SchemaMigration.loader.get()
override fun getAppContext(): CordappContext {
throw NotImplementedException()
}
override fun getContractAttachmentID(contractClassName: ContractClassName): AttachmentId? {
throw NotImplementedException()
}
}
private val cordappLoader = SchemaMigration.loader.get()
private fun defaultNetworkParameters(): NetworkParameters { private fun defaultNetworkParameters(): NetworkParameters {
logger.warn("Using a dummy set of network parameters for migration.") logger.warn("Using a dummy set of network parameters for migration.")
@ -96,7 +107,7 @@ class MigrationServicesForResolution(
private fun extractStateFromTx(tx: WireTransaction, stateIndices: Collection<Int>): List<TransactionState<ContractState>> { private fun extractStateFromTx(tx: WireTransaction, stateIndices: Collection<Int>): List<TransactionState<ContractState>> {
return try { return try {
val attachments = tx.attachments.mapNotNull { attachments.openAttachment(it)} val attachments = tx.attachments.mapNotNull { attachments.openAttachment(it)}
val states = AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext(attachments, networkParameters, tx.id) { val states = AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext(attachments, networkParameters, tx.id, cordappLoader.appClassLoader) {
deserialiseComponentGroup(tx.componentGroups, TransactionState::class, ComponentGroupEnum.OUTPUTS_GROUP, forceDeserialize = true) deserialiseComponentGroup(tx.componentGroups, TransactionState::class, ComponentGroupEnum.OUTPUTS_GROUP, forceDeserialize = true)
} }
states.filterIndexed {index, _ -> stateIndices.contains(index)}.toList() states.filterIndexed {index, _ -> stateIndices.contains(index)}.toList()

View File

@ -6,7 +6,7 @@ import net.corda.core.internal.notary.NotaryService
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.node.SerialFilter import net.corda.node.SerialFilter
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.cordapp.CordappLoader import net.corda.nodeapi.internal.cordapp.CordappLoader
import net.corda.node.internal.cordapp.VirtualCordapp import net.corda.node.internal.cordapp.VirtualCordapp
import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.NotaryConfig

View File

@ -19,7 +19,7 @@ import net.corda.core.utilities.NonEmptySet
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.node.cordapp.CordappLoader import net.corda.nodeapi.internal.cordapp.CordappLoader
import net.corda.node.services.api.VaultServiceInternal import net.corda.node.services.api.VaultServiceInternal
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity

View File

@ -21,7 +21,7 @@ import net.corda.core.serialization.SerializeAsToken
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.cordapp.CordappLoader import net.corda.nodeapi.internal.cordapp.CordappLoader
import net.corda.node.internal.ServicesForResolutionImpl import net.corda.node.internal.ServicesForResolutionImpl
import net.corda.node.internal.cordapp.JarScanningCordappLoader import net.corda.node.internal.cordapp.JarScanningCordappLoader
import net.corda.node.services.api.* import net.corda.node.services.api.*

View File

@ -6,7 +6,7 @@ import net.corda.core.internal.DEPLOYED_CORDAPP_UPLOADER
import net.corda.core.internal.cordapp.CordappImpl import net.corda.core.internal.cordapp.CordappImpl
import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.node.cordapp.CordappLoader import net.corda.nodeapi.internal.cordapp.CordappLoader
import net.corda.node.internal.cordapp.CordappProviderImpl import net.corda.node.internal.cordapp.CordappProviderImpl
import net.corda.testing.services.MockAttachmentStorage import net.corda.testing.services.MockAttachmentStorage
import java.security.PublicKey import java.security.PublicKey