mirror of
https://github.com/corda/corda.git
synced 2025-06-02 23:50:54 +00:00
ENT-12366: External verifier now sets appclassloader to legacy contra… (#7855)
* ENT-12366: External verifier now sets appclassloader to legacy contracts directory instead of the cordapps directory. * ENT-12366: Now check legacy-contracts exists before start external verifier.
This commit is contained in:
parent
7852754ad9
commit
33cf48e04b
@ -4,6 +4,7 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import net.corda.core.contracts.TransactionState
|
import net.corda.core.contracts.TransactionState
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.hash
|
import net.corda.core.internal.hash
|
||||||
import net.corda.core.internal.mapToSet
|
import net.corda.core.internal.mapToSet
|
||||||
import net.corda.core.internal.toPath
|
import net.corda.core.internal.toPath
|
||||||
@ -22,9 +23,13 @@ import net.corda.finance.flows.CashIssueAndPaymentFlow
|
|||||||
import net.corda.finance.issuedBy
|
import net.corda.finance.issuedBy
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
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.DUMMY_NOTARY_NAME
|
||||||
import net.corda.testing.core.internal.JarSignatureTestUtils.unsignJar
|
import net.corda.testing.core.internal.JarSignatureTestUtils.unsignJar
|
||||||
|
import net.corda.testing.core.singleIdentity
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.NodeParameters
|
import net.corda.testing.driver.NodeParameters
|
||||||
|
import net.corda.testing.node.NotarySpec
|
||||||
import net.corda.testing.node.TestCordapp
|
import net.corda.testing.node.TestCordapp
|
||||||
import net.corda.testing.node.internal.DriverDSLImpl
|
import net.corda.testing.node.internal.DriverDSLImpl
|
||||||
import net.corda.testing.node.internal.FINANCE_WORKFLOWS_CORDAPP
|
import net.corda.testing.node.internal.FINANCE_WORKFLOWS_CORDAPP
|
||||||
@ -81,7 +86,8 @@ class TransactionBuilderDriverTest {
|
|||||||
internalDriver(
|
internalDriver(
|
||||||
cordappsForAllNodes = listOf(FINANCE_WORKFLOWS_CORDAPP),
|
cordappsForAllNodes = listOf(FINANCE_WORKFLOWS_CORDAPP),
|
||||||
startNodesInProcess = false,
|
startNodesInProcess = false,
|
||||||
networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
|
networkParameters = testNetworkParameters(minimumPlatformVersion = 4),
|
||||||
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = false))
|
||||||
) {
|
) {
|
||||||
val (legacyContracts, legacyDependency) = splitFinanceContractCordapp(legacyFinanceContractsJar)
|
val (legacyContracts, legacyDependency) = splitFinanceContractCordapp(legacyFinanceContractsJar)
|
||||||
val currentContracts = TestCordapp.of(currentFinanceContractsJar.toUri()).asSigned() as TestCordappInternal
|
val currentContracts = TestCordapp.of(currentFinanceContractsJar.toUri()).asSigned() as TestCordappInternal
|
||||||
@ -95,15 +101,23 @@ class TransactionBuilderDriverTest {
|
|||||||
legacyContracts = listOf(legacyContracts)
|
legacyContracts = listOf(legacyContracts)
|
||||||
)).getOrThrow()
|
)).getOrThrow()
|
||||||
|
|
||||||
|
val nodeBob = startNode(NodeParameters(
|
||||||
|
BOB_NAME,
|
||||||
|
additionalCordapps = listOf(currentContracts),
|
||||||
|
legacyContracts = listOf(legacyContracts,legacyDependency)
|
||||||
|
)).getOrThrow()
|
||||||
|
val bobParty = nodeBob.nodeInfo.singleIdentity()
|
||||||
|
|
||||||
|
|
||||||
// First make sure the missing dependency causes an issue
|
// First make sure the missing dependency causes an issue
|
||||||
assertThatThrownBy {
|
assertThatThrownBy {
|
||||||
createTransaction(node)
|
createTransaction(node, bobParty)
|
||||||
}.hasMessageContaining("Transaction being built has a missing legacy attachment for class net/corda/finance/contracts/asset/")
|
}.hasMessageContaining("Transaction being built has a missing legacy attachment for class net/corda/finance/contracts/asset/")
|
||||||
|
|
||||||
// Upload the missing dependency
|
// Upload the missing dependency
|
||||||
legacyDependency.jarFile.inputStream().use(node.rpc::uploadAttachment)
|
legacyDependency.jarFile.inputStream().use(node.rpc::uploadAttachment)
|
||||||
|
|
||||||
val stx = createTransaction(node)
|
val stx = createTransaction(node, bobParty)
|
||||||
assertThat(stx.tx.legacyAttachments).contains(legacyContracts.jarFile.hash, legacyDependency.jarFile.hash)
|
assertThat(stx.tx.legacyAttachments).contains(legacyContracts.jarFile.hash, legacyDependency.jarFile.hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,12 +181,12 @@ class TransactionBuilderDriverTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun DriverDSLImpl.createTransaction(node: NodeHandle): SignedTransaction {
|
private fun DriverDSLImpl.createTransaction(node: NodeHandle, destination: Party = defaultNotaryIdentity): SignedTransaction {
|
||||||
return node.rpc.startFlow(
|
return node.rpc.startFlow(
|
||||||
::CashIssueAndPaymentFlow,
|
::CashIssueAndPaymentFlow,
|
||||||
1.DOLLARS,
|
1.DOLLARS,
|
||||||
OpaqueBytes.of(0x00),
|
OpaqueBytes.of(0x00),
|
||||||
defaultNotaryIdentity,
|
destination,
|
||||||
false,
|
false,
|
||||||
defaultNotaryIdentity
|
defaultNotaryIdentity
|
||||||
).returnValue.getOrThrow().stx
|
).returnValue.getOrThrow().stx
|
||||||
|
@ -193,17 +193,18 @@ class ExternalVerificationUnsignedCordappsTest {
|
|||||||
fun startNodes() {
|
fun startNodes() {
|
||||||
// The 4.11 finance CorDapp jars
|
// The 4.11 finance CorDapp jars
|
||||||
val legacyCordapps = listOf(unsignedResourceJar("corda-finance-contracts-4.11.jar"), smokeTestResource("corda-finance-workflows-4.11.jar"))
|
val legacyCordapps = listOf(unsignedResourceJar("corda-finance-contracts-4.11.jar"), smokeTestResource("corda-finance-workflows-4.11.jar"))
|
||||||
|
val legacyCordappsWithoutContracts = listOf(smokeTestResource("corda-finance-workflows-4.11.jar"))
|
||||||
// The current version finance CorDapp jars
|
// The current version finance CorDapp jars
|
||||||
val currentCordapps = listOf(unsignedResourceJar("corda-finance-contracts.jar"), smokeTestResource("corda-finance-workflows.jar"))
|
val currentCordapps = listOf(unsignedResourceJar("corda-finance-contracts.jar"), smokeTestResource("corda-finance-workflows.jar"))
|
||||||
|
|
||||||
notary = factory.createNotaries(nodeParams(DUMMY_NOTARY_NAME, currentCordapps))[0]
|
notary = factory.createNotaries(nodeParams(DUMMY_NOTARY_NAME, currentCordapps, legacyCordapps))[0]
|
||||||
oldNode = factory.createNode(nodeParams(
|
oldNode = factory.createNode(nodeParams(
|
||||||
CordaX500Name("Old", "Delhi", "IN"),
|
CordaX500Name("Old", "Delhi", "IN"),
|
||||||
legacyCordapps,
|
legacyCordapps,
|
||||||
clientRpcConfig = CordaRPCClientConfiguration(minimumServerProtocolVersion = 13),
|
clientRpcConfig = CordaRPCClientConfiguration(minimumServerProtocolVersion = 13),
|
||||||
version = "4.11"
|
version = "4.11"
|
||||||
))
|
))
|
||||||
newNode = factory.createNode(nodeParams(CordaX500Name("New", "York", "US"), currentCordapps))
|
newNode = factory.createNode(nodeParams(CordaX500Name("New", "York", "US"), currentCordapps, legacyCordappsWithoutContracts))
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
|
@ -24,8 +24,10 @@ 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.*
|
||||||
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,9 +47,11 @@ 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 {
|
||||||
|
@ -240,6 +240,7 @@ class ResolveTransactionsFlowTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
|
@Ignore("Need to pass legacy contracts to internal mock network & need to create a legacy contract for test below")
|
||||||
fun `can resolve a chain of transactions containing a contract upgrade transaction`() {
|
fun `can resolve a chain of transactions containing a contract upgrade transaction`() {
|
||||||
val tx = contractUpgradeChain()
|
val tx = contractUpgradeChain()
|
||||||
var numUpdates = 0
|
var numUpdates = 0
|
||||||
|
@ -60,6 +60,9 @@ data class RotatedKeys(val rotatedSigningKeys: List<List<SecureHash>> = emptyLis
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun canBeTransitioned(inputKeys: List<PublicKey>, outputKeys: List<PublicKey>): Boolean {
|
fun canBeTransitioned(inputKeys: List<PublicKey>, outputKeys: List<PublicKey>): Boolean {
|
||||||
|
if (inputKeys.isEmpty() && outputKeys.isEmpty()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
return canBeTransitioned(CompositeKey.Builder().addKeys(inputKeys).build(), CompositeKey.Builder().addKeys(outputKeys).build())
|
return canBeTransitioned(CompositeKey.Builder().addKeys(inputKeys).build(), CompositeKey.Builder().addKeys(outputKeys).build())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,9 +220,11 @@ class JarScanningCordappLoader(private val cordappJars: Set<Path>,
|
|||||||
private fun checkSignersMatch(legacyCordapp: CordappImpl, nonLegacyCordapp: CordappImpl) {
|
private fun checkSignersMatch(legacyCordapp: CordappImpl, nonLegacyCordapp: CordappImpl) {
|
||||||
val legacySigners = legacyCordapp.jarPath.openStream().let(::JarInputStream).use(JarSignatureCollector::collectSigners)
|
val legacySigners = legacyCordapp.jarPath.openStream().let(::JarInputStream).use(JarSignatureCollector::collectSigners)
|
||||||
val nonLegacySigners = nonLegacyCordapp.jarPath.openStream().let(::JarInputStream).use(JarSignatureCollector::collectSigners)
|
val nonLegacySigners = nonLegacyCordapp.jarPath.openStream().let(::JarInputStream).use(JarSignatureCollector::collectSigners)
|
||||||
check(rotatedKeys.canBeTransitioned(legacySigners, nonLegacySigners)) {
|
if (legacySigners.isNotEmpty() || nonLegacySigners.isNotEmpty()) {
|
||||||
"Newer contract CorDapp '${nonLegacyCordapp.jarFile}' signers do not match legacy contract CorDapp " +
|
check(rotatedKeys.canBeTransitioned(legacySigners, nonLegacySigners)) {
|
||||||
"'${legacyCordapp.jarFile}' signers."
|
"Newer contract CorDapp '${nonLegacyCordapp.jarFile}' signers do not match legacy contract CorDapp " +
|
||||||
|
"'${legacyCordapp.jarFile}' signers."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ 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.
|
||||||
@ -116,6 +117,12 @@ 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.
|
||||||
|
@ -52,7 +52,7 @@ sealed class ExternalVerifierInbound {
|
|||||||
val ctx: CoreTransaction,
|
val ctx: CoreTransaction,
|
||||||
val ctxInputsAndReferences: Map<StateRef, SerializedTransactionState>
|
val ctxInputsAndReferences: Map<StateRef, SerializedTransactionState>
|
||||||
) : ExternalVerifierInbound() {
|
) : ExternalVerifierInbound() {
|
||||||
override fun toString(): String = "VerificationRequest(ctx=$ctx)"
|
override fun toString(): String = "VerificationRequest(transaction id=${ctx.id})"
|
||||||
}
|
}
|
||||||
|
|
||||||
data class PartiesResult(val parties: List<Party?>) : ExternalVerifierInbound()
|
data class PartiesResult(val parties: List<Party?>) : ExternalVerifierInbound()
|
||||||
|
@ -124,7 +124,7 @@ class ExternalVerifier(private val baseDirectory: Path, private val channel: Soc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createAppClassLoader(): ClassLoader {
|
private fun createAppClassLoader(): ClassLoader {
|
||||||
val cordappJarUrls = (baseDirectory / "cordapps").listDirectoryEntries("*.jar")
|
val cordappJarUrls = (baseDirectory / "legacy-contracts").listDirectoryEntries("*.jar")
|
||||||
.stream()
|
.stream()
|
||||||
.map { it.toUri().toURL() }
|
.map { it.toUri().toURL() }
|
||||||
.toTypedArray()
|
.toTypedArray()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user