mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
ENT-12366 ExternalVerifier no longer needs legacy contracts folder, and can derive everything it needs from attachments. (#7866)
* ENT-12366 ExternalVerifier no longer needs legacy contracts folder, and can derive everything it needs from attachments.
* ENT-12366 Fix compiler warnings
* Revert "ENT-12366 Fix compiler warnings"
This reverts commit 4e884a5519
.
* ENT-12366 Attempt to appease warnings in both 1.2 and 1.9 compilers
This commit is contained in:
parent
a67e6cdb1e
commit
436eca1524
@ -13,21 +13,26 @@ import net.corda.core.internal.getRequiredTransaction
|
|||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.transactions.ContractUpgradeLedgerTransaction
|
import net.corda.core.transactions.ContractUpgradeLedgerTransaction
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
|
import net.corda.coretesting.internal.matchers.rpc.willReturn
|
||||||
|
import net.corda.coretesting.internal.matchers.rpc.willThrow
|
||||||
import net.corda.node.services.Permissions.Companion.startFlow
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.contracts.DummyContractV2
|
import net.corda.testing.contracts.DummyContractV2
|
||||||
import net.corda.testing.core.ALICE_NAME
|
import net.corda.testing.core.ALICE_NAME
|
||||||
import net.corda.testing.core.BOB_NAME
|
import net.corda.testing.core.BOB_NAME
|
||||||
import net.corda.testing.core.singleIdentity
|
import net.corda.testing.core.singleIdentity
|
||||||
import net.corda.coretesting.internal.matchers.rpc.willReturn
|
|
||||||
import net.corda.coretesting.internal.matchers.rpc.willThrow
|
|
||||||
import net.corda.testing.node.User
|
import net.corda.testing.node.User
|
||||||
import net.corda.testing.node.internal.*
|
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
|
||||||
|
import net.corda.testing.node.internal.InternalMockNetwork
|
||||||
|
import net.corda.testing.node.internal.RPCDriverDSL
|
||||||
|
import net.corda.testing.node.internal.TestStartedNode
|
||||||
|
import net.corda.testing.node.internal.enclosedCordapp
|
||||||
|
import net.corda.testing.node.internal.rpcDriver
|
||||||
|
import net.corda.testing.node.internal.rpcTestUser
|
||||||
|
import net.corda.testing.node.internal.startRpcClient
|
||||||
import org.junit.AfterClass
|
import org.junit.AfterClass
|
||||||
import org.junit.Ignore
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
@Ignore("Explicit contract upgrade not supported in 4.12")
|
|
||||||
class ContractUpgradeFlowRPCTest : WithContracts, WithFinality {
|
class ContractUpgradeFlowRPCTest : WithContracts, WithFinality {
|
||||||
companion object {
|
companion object {
|
||||||
private val classMockNet = InternalMockNetwork(cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP, enclosedCordapp()))
|
private val classMockNet = InternalMockNetwork(cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP, enclosedCordapp()))
|
||||||
|
@ -47,11 +47,9 @@ import net.corda.testing.node.internal.TestStartedNode
|
|||||||
import net.corda.testing.node.internal.enclosedCordapp
|
import net.corda.testing.node.internal.enclosedCordapp
|
||||||
import net.corda.testing.node.internal.startFlow
|
import net.corda.testing.node.internal.startFlow
|
||||||
import org.junit.AfterClass
|
import org.junit.AfterClass
|
||||||
import org.junit.Ignore
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.Currency
|
import java.util.Currency
|
||||||
|
|
||||||
@Ignore("Explicit contract upgrade not supported in 4.12")
|
|
||||||
class ContractUpgradeFlowTest : WithContracts, WithFinality {
|
class ContractUpgradeFlowTest : WithContracts, WithFinality {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -33,6 +33,7 @@ import net.corda.core.node.ServicesForResolution
|
|||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
|
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
|
import net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder
|
||||||
import net.corda.core.transactions.ContractUpgradeFilteredTransaction.FilteredComponent
|
import net.corda.core.transactions.ContractUpgradeFilteredTransaction.FilteredComponent
|
||||||
import net.corda.core.transactions.ContractUpgradeWireTransaction.Companion.calculateUpgradedState
|
import net.corda.core.transactions.ContractUpgradeWireTransaction.Companion.calculateUpgradedState
|
||||||
import net.corda.core.transactions.ContractUpgradeWireTransaction.Component.INPUTS
|
import net.corda.core.transactions.ContractUpgradeWireTransaction.Component.INPUTS
|
||||||
@ -281,30 +282,38 @@ private constructor(
|
|||||||
fun resolve(verificationSupport: VerificationSupport,
|
fun resolve(verificationSupport: VerificationSupport,
|
||||||
wtx: ContractUpgradeWireTransaction,
|
wtx: ContractUpgradeWireTransaction,
|
||||||
sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
|
sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
|
||||||
val inputs = wtx.inputs.map(verificationSupport::getStateAndRef)
|
|
||||||
val (legacyContractAttachment, upgradedContractAttachment) = verificationSupport.getAttachments(listOf(
|
val (legacyContractAttachment, upgradedContractAttachment) = verificationSupport.getAttachments(listOf(
|
||||||
wtx.legacyContractAttachmentId,
|
wtx.legacyContractAttachmentId,
|
||||||
wtx.upgradedContractAttachmentId
|
wtx.upgradedContractAttachmentId
|
||||||
))
|
))
|
||||||
|
if (legacyContractAttachment == null) throw AttachmentResolutionException(wtx.legacyContractAttachmentId)
|
||||||
|
if (upgradedContractAttachment == null) throw AttachmentResolutionException(wtx.upgradedContractAttachmentId)
|
||||||
val networkParameters = verificationSupport.getNetworkParameters(wtx.networkParametersHash)
|
val networkParameters = verificationSupport.getNetworkParameters(wtx.networkParametersHash)
|
||||||
?: throw TransactionResolutionException(wtx.id)
|
?: throw TransactionResolutionException(wtx.id)
|
||||||
val upgradedContract = loadUpgradedContract(wtx.upgradedContractClassName, wtx.id, verificationSupport.appClassLoader)
|
|
||||||
return ContractUpgradeLedgerTransaction(
|
return AttachmentsClassLoaderBuilder.withAttachmentsClassLoaderContext(
|
||||||
inputs,
|
listOf(legacyContractAttachment, upgradedContractAttachment),
|
||||||
wtx.notary,
|
|
||||||
legacyContractAttachment ?: throw AttachmentResolutionException(wtx.legacyContractAttachmentId),
|
|
||||||
upgradedContractAttachment ?: throw AttachmentResolutionException(wtx.upgradedContractAttachmentId),
|
|
||||||
wtx.id,
|
|
||||||
wtx.privacySalt,
|
|
||||||
sigs,
|
|
||||||
networkParameters,
|
networkParameters,
|
||||||
upgradedContract
|
wtx.id,
|
||||||
)
|
verificationSupport::isAttachmentTrusted,
|
||||||
|
attachmentsClassLoaderCache = verificationSupport.attachmentsClassLoaderCache
|
||||||
|
) { serializationContext ->
|
||||||
|
val inputs = wtx.inputs.map(verificationSupport::getStateAndRef)
|
||||||
|
val upgradedContract = loadUpgradedContract(wtx.upgradedContractClassName, wtx.id, serializationContext.deserializationClassLoader)
|
||||||
|
ContractUpgradeLedgerTransaction(
|
||||||
|
inputs,
|
||||||
|
wtx.notary,
|
||||||
|
legacyContractAttachment,
|
||||||
|
upgradedContractAttachment,
|
||||||
|
wtx.id,
|
||||||
|
wtx.privacySalt,
|
||||||
|
sigs,
|
||||||
|
networkParameters,
|
||||||
|
upgradedContract)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO There is an inconsistency with the class loader used with this method. Transaction resolution uses the app class loader,
|
|
||||||
// whilst TransactionStorageVerification.getContractUpdateOutput uses an attachments class loder comprised of the the legacy and
|
|
||||||
// upgraded attachments
|
|
||||||
@CordaInternal
|
@CordaInternal
|
||||||
@JvmSynthetic
|
@JvmSynthetic
|
||||||
internal fun loadUpgradedContract(className: ContractClassName, id: SecureHash, classLoader: ClassLoader): UpgradedContract<ContractState, *> {
|
internal fun loadUpgradedContract(className: ContractClassName, id: SecureHash, classLoader: ClassLoader): UpgradedContract<ContractState, *> {
|
||||||
|
@ -30,7 +30,6 @@ import net.corda.core.internal.TransactionDeserialisationException
|
|||||||
import net.corda.core.internal.createComponentGroups
|
import net.corda.core.internal.createComponentGroups
|
||||||
import net.corda.core.internal.deserialiseComponentGroup
|
import net.corda.core.internal.deserialiseComponentGroup
|
||||||
import net.corda.core.internal.equivalent
|
import net.corda.core.internal.equivalent
|
||||||
import net.corda.core.internal.flatMapToSet
|
|
||||||
import net.corda.core.internal.getGroup
|
import net.corda.core.internal.getGroup
|
||||||
import net.corda.core.internal.isUploaderTrusted
|
import net.corda.core.internal.isUploaderTrusted
|
||||||
import net.corda.core.internal.lazyMapped
|
import net.corda.core.internal.lazyMapped
|
||||||
@ -190,19 +189,17 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
|||||||
@JvmSynthetic
|
@JvmSynthetic
|
||||||
internal fun toLedgerTransactionInternal(verificationSupport: VerificationSupport): LedgerTransaction {
|
internal fun toLedgerTransactionInternal(verificationSupport: VerificationSupport): LedgerTransaction {
|
||||||
// Look up public keys to authenticated identities.
|
// Look up public keys to authenticated identities.
|
||||||
val authenticatedCommands = if (verificationSupport.isInProcess) {
|
if (!verificationSupport.isInProcess) {
|
||||||
commands.lazyMapped { cmd, _ ->
|
val signersGroup: List<List<PublicKey>> = uncheckedCast(deserialiseComponentGroup(componentGroups, List::class, SIGNERS_GROUP))
|
||||||
|
if (signersGroup.isNotEmpty()) {
|
||||||
|
// Pre-fetch all signing keys if the signers component group is present (Corda 4+)
|
||||||
|
verificationSupport.getParties(signersGroup.flatten().toSet())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val authenticatedCommands = commands.lazyMapped { cmd, _ ->
|
||||||
val parties = verificationSupport.getParties(cmd.signers).filterNotNull()
|
val parties = verificationSupport.getParties(cmd.signers).filterNotNull()
|
||||||
CommandWithParties(cmd.signers, parties, cmd.value)
|
CommandWithParties(cmd.signers, parties, cmd.value)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
val allSigners = commands.flatMapToSet { it.signers }
|
|
||||||
val allParties = verificationSupport.getParties(allSigners)
|
|
||||||
commands.map { cmd ->
|
|
||||||
val parties = cmd.signers.mapNotNull { allParties[allSigners.indexOf(it)] }
|
|
||||||
CommandWithParties(cmd.signers, parties, cmd.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that the lazy mappings will use the correct SerializationContext.
|
// Ensure that the lazy mappings will use the correct SerializationContext.
|
||||||
val serializationFactory = SerializationFactory.defaultFactory
|
val serializationFactory = SerializationFactory.defaultFactory
|
||||||
|
@ -58,7 +58,6 @@ import kotlin.io.path.div
|
|||||||
import kotlin.io.path.fileAttributesViewOrNull
|
import kotlin.io.path.fileAttributesViewOrNull
|
||||||
import kotlin.io.path.isExecutable
|
import kotlin.io.path.isExecutable
|
||||||
import kotlin.io.path.isWritable
|
import kotlin.io.path.isWritable
|
||||||
import kotlin.io.path.notExists
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle to the node's external verifier. The verifier process is started lazily on the first verification request.
|
* Handle to the node's external verifier. The verifier process is started lazily on the first verification request.
|
||||||
@ -117,12 +116,6 @@ class ExternalVerifierHandleImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun startServer() {
|
private fun startServer() {
|
||||||
val legacyContractsPath = (baseDirectory / "legacy-contracts")
|
|
||||||
if (legacyContractsPath.notExists()) {
|
|
||||||
log.error("Failed to start external verifier because $legacyContractsPath does not exist. Please create a legacy-contracts " +
|
|
||||||
"directory under $baseDirectory and place your legacy contracts into this directory. See the documentation for details.")
|
|
||||||
throw IOException("Cannot start external verifier because $legacyContractsPath does not exist.")
|
|
||||||
}
|
|
||||||
if (::socketFile.isInitialized) return
|
if (::socketFile.isInitialized) return
|
||||||
// Try to create the UNIX domain file in /tmp to keep the full path under the 100 char limit. If we don't have access to it then
|
// Try to create the UNIX domain file in /tmp to keep the full path under the 100 char limit. If we don't have access to it then
|
||||||
// fallback to the temp dir specified by the JVM and hope it's short enough.
|
// fallback to the temp dir specified by the JVM and hope it's short enough.
|
||||||
|
@ -12,12 +12,13 @@ import net.corda.core.serialization.internal.AttachmentsClassLoaderCache
|
|||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
class ExternalVerificationContext(
|
class ExternalVerificationContext(
|
||||||
override val appClassLoader: ClassLoader,
|
|
||||||
override val attachmentsClassLoaderCache: AttachmentsClassLoaderCache,
|
override val attachmentsClassLoaderCache: AttachmentsClassLoaderCache,
|
||||||
private val externalVerifier: ExternalVerifier,
|
private val externalVerifier: ExternalVerifier,
|
||||||
private val transactionInputsAndReferences: Map<StateRef, SerializedTransactionState>,
|
private val transactionInputsAndReferences: Map<StateRef, SerializedTransactionState>,
|
||||||
override val rotatedKeys: RotatedKeys
|
override val rotatedKeys: RotatedKeys
|
||||||
) : VerificationSupport {
|
) : VerificationSupport {
|
||||||
|
override val appClassLoader: ClassLoader get() = throw NotImplementedError("Cannot call appClassLoader")
|
||||||
|
|
||||||
override val isInProcess: Boolean get() = false
|
override val isInProcess: Boolean get() = false
|
||||||
|
|
||||||
override fun getParties(keys: Collection<PublicKey>): List<Party?> = externalVerifier.getParties(keys)
|
override fun getParties(keys: Collection<PublicKey>): List<Party?> = externalVerifier.getParties(keys)
|
||||||
|
@ -10,7 +10,6 @@ import net.corda.core.internal.mapToSet
|
|||||||
import net.corda.core.internal.objectOrNewInstance
|
import net.corda.core.internal.objectOrNewInstance
|
||||||
import net.corda.core.internal.toSimpleString
|
import net.corda.core.internal.toSimpleString
|
||||||
import net.corda.core.internal.toSynchronised
|
import net.corda.core.internal.toSynchronised
|
||||||
import net.corda.core.internal.toTypedArray
|
|
||||||
import net.corda.core.internal.verification.AttachmentFixups
|
import net.corda.core.internal.verification.AttachmentFixups
|
||||||
import net.corda.core.node.NetworkParameters
|
import net.corda.core.node.NetworkParameters
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
@ -45,19 +44,14 @@ import net.corda.serialization.internal.verifier.ExternalVerifierOutbound.Verifi
|
|||||||
import net.corda.serialization.internal.verifier.ExternalVerifierOutbound.VerifierRequest.GetNetworkParameters
|
import net.corda.serialization.internal.verifier.ExternalVerifierOutbound.VerifierRequest.GetNetworkParameters
|
||||||
import net.corda.serialization.internal.verifier.ExternalVerifierOutbound.VerifierRequest.GetParties
|
import net.corda.serialization.internal.verifier.ExternalVerifierOutbound.VerifierRequest.GetParties
|
||||||
import net.corda.serialization.internal.verifier.ExternalVerifierOutbound.VerifierRequest.GetTrustedClassAttachments
|
import net.corda.serialization.internal.verifier.ExternalVerifierOutbound.VerifierRequest.GetTrustedClassAttachments
|
||||||
import net.corda.serialization.internal.verifier.loadCustomSerializationScheme
|
|
||||||
import net.corda.serialization.internal.verifier.readCordaSerializable
|
import net.corda.serialization.internal.verifier.readCordaSerializable
|
||||||
import net.corda.serialization.internal.verifier.writeCordaSerializable
|
import net.corda.serialization.internal.verifier.writeCordaSerializable
|
||||||
import java.net.URLClassLoader
|
|
||||||
import java.nio.channels.SocketChannel
|
import java.nio.channels.SocketChannel
|
||||||
import java.nio.file.Path
|
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
import kotlin.io.path.div
|
|
||||||
import kotlin.io.path.listDirectoryEntries
|
|
||||||
|
|
||||||
@Suppress("MagicNumber")
|
@Suppress("MagicNumber")
|
||||||
class ExternalVerifier(private val baseDirectory: Path, private val channel: SocketChannel) {
|
class ExternalVerifier(private val channel: SocketChannel) {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
}
|
}
|
||||||
@ -69,7 +63,6 @@ class ExternalVerifier(private val baseDirectory: Path, private val channel: Soc
|
|||||||
private val networkParametersMap: OptionalCache<SecureHash, NetworkParameters>
|
private val networkParametersMap: OptionalCache<SecureHash, NetworkParameters>
|
||||||
private val trustedClassAttachments: Cache<String, List<SecureHash>>
|
private val trustedClassAttachments: Cache<String, List<SecureHash>>
|
||||||
|
|
||||||
private lateinit var appClassLoader: ClassLoader
|
|
||||||
private lateinit var currentNetworkParameters: NetworkParameters
|
private lateinit var currentNetworkParameters: NetworkParameters
|
||||||
private lateinit var rotatedKeys: RotatedKeys
|
private lateinit var rotatedKeys: RotatedKeys
|
||||||
|
|
||||||
@ -102,39 +95,15 @@ class ExternalVerifier(private val baseDirectory: Path, private val channel: Soc
|
|||||||
val initialisation = channel.readCordaSerializable(Initialisation::class)
|
val initialisation = channel.readCordaSerializable(Initialisation::class)
|
||||||
log.info("Received $initialisation")
|
log.info("Received $initialisation")
|
||||||
|
|
||||||
appClassLoader = createAppClassLoader()
|
|
||||||
|
|
||||||
// Then use the initialisation message to create the correct serialization context
|
|
||||||
_contextSerializationEnv.set(null)
|
|
||||||
_contextSerializationEnv.set(SerializationEnvironment.with(
|
|
||||||
verifierSerializationFactory(initialisation, appClassLoader).apply {
|
|
||||||
initialisation.customSerializationSchemeClassName?.let {
|
|
||||||
registerScheme(loadCustomSerializationScheme(it, appClassLoader))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
p2pContext = AMQP_P2P_CONTEXT.withClassLoader(appClassLoader)
|
|
||||||
))
|
|
||||||
|
|
||||||
attachmentFixups.load(appClassLoader)
|
|
||||||
|
|
||||||
currentNetworkParameters = initialisation.currentNetworkParameters
|
currentNetworkParameters = initialisation.currentNetworkParameters
|
||||||
networkParametersMap.put(initialisation.serializedCurrentNetworkParameters.hash, Optional.of(currentNetworkParameters))
|
networkParametersMap.put(initialisation.serializedCurrentNetworkParameters.hash, Optional.of(currentNetworkParameters))
|
||||||
rotatedKeys = initialisation.rotatedKeys
|
rotatedKeys = initialisation.rotatedKeys
|
||||||
log.info("External verifier initialised")
|
log.info("External verifier initialised")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createAppClassLoader(): ClassLoader {
|
|
||||||
val cordappJarUrls = (baseDirectory / "legacy-contracts").listDirectoryEntries("*.jar")
|
|
||||||
.stream()
|
|
||||||
.map { it.toUri().toURL() }
|
|
||||||
.toTypedArray()
|
|
||||||
log.debug { "CorDapps: ${cordappJarUrls?.joinToString()}" }
|
|
||||||
return URLClassLoader(cordappJarUrls, javaClass.classLoader)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("INVISIBLE_MEMBER")
|
@Suppress("INVISIBLE_MEMBER")
|
||||||
private fun verifyTransaction(request: VerificationRequest) {
|
private fun verifyTransaction(request: VerificationRequest) {
|
||||||
val verificationContext = ExternalVerificationContext(appClassLoader, attachmentsClassLoaderCache, this,
|
val verificationContext = ExternalVerificationContext(attachmentsClassLoaderCache, this,
|
||||||
request.ctxInputsAndReferences, rotatedKeys)
|
request.ctxInputsAndReferences, rotatedKeys)
|
||||||
val result: Try<Unit> = try {
|
val result: Try<Unit> = try {
|
||||||
val ctx = request.ctx
|
val ctx = request.ctx
|
||||||
|
@ -26,7 +26,7 @@ object Main {
|
|||||||
val channel = SocketChannel.open(StandardProtocolFamily.UNIX)
|
val channel = SocketChannel.open(StandardProtocolFamily.UNIX)
|
||||||
channel.connect(UnixDomainSocketAddress.of(socketFile))
|
channel.connect(UnixDomainSocketAddress.of(socketFile))
|
||||||
log.info("Connected to node on UNIX domain file $socketFile")
|
log.info("Connected to node on UNIX domain file $socketFile")
|
||||||
ExternalVerifier(baseDirectory, channel).run()
|
ExternalVerifier(channel).run()
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
log.error("Unexpected error which has terminated the verifier", t)
|
log.error("Unexpected error which has terminated the verifier", t)
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
|
Loading…
Reference in New Issue
Block a user