mirror of
https://github.com/corda/corda.git
synced 2025-03-15 08:41:04 +00:00
Merge remote-tracking branch 'remotes/open/master' into merges/july-23-11-26
# Conflicts: # confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt # docs/source/changelog.rst # node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt # node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt # node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt # node/src/integration-test/kotlin/net/corda/node/services/BFTSMaRtTests.kt # node/src/main/kotlin/net/corda/node/internal/Node.kt # node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt # node/src/main/kotlin/net/corda/node/internal/cordapp/ManifestUtils.kt # node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt # node/src/test/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoaderTest.kt # node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt # node/src/test/kotlin/net/corda/node/services/TimedFlowTests.kt # settings.gradle # testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt # testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt # testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt
This commit is contained in:
commit
a4d65dae22
@ -5825,6 +5825,8 @@ public interface net.corda.testing.driver.DriverDSL
|
||||
@NotNull
|
||||
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.NodeHandle> startNode(net.corda.testing.driver.NodeParameters, net.corda.core.identity.CordaX500Name, java.util.List<net.corda.testing.node.User>, net.corda.testing.driver.VerifierType, java.util.Map<String, ?>, Boolean, String)
|
||||
@NotNull
|
||||
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.NodeHandle> startNode(net.corda.testing.driver.NodeParameters, net.corda.core.identity.CordaX500Name, java.util.List<net.corda.testing.node.User>, net.corda.testing.driver.VerifierType, java.util.Map<String, ?>, Boolean, String, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>, boolean)
|
||||
@NotNull
|
||||
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.WebserverHandle> startWebserver(net.corda.testing.driver.NodeHandle)
|
||||
@NotNull
|
||||
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.WebserverHandle> startWebserver(net.corda.testing.driver.NodeHandle, String)
|
||||
@ -5832,6 +5834,11 @@ public interface net.corda.testing.driver.DriverDSL
|
||||
public final class net.corda.testing.driver.DriverParameters extends java.lang.Object
|
||||
public <init>()
|
||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters)
|
||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, boolean)
|
||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, boolean, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, boolean, boolean)
|
||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, boolean, boolean, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
||||
public final boolean component1()
|
||||
@NotNull
|
||||
public final java.util.List<String> component10()
|
||||
@ -6024,6 +6031,75 @@ public static final class net.corda.testing.driver.PortAllocation$Incremental ex
|
||||
public final java.util.concurrent.atomic.AtomicInteger getPortCounter()
|
||||
public int nextPort()
|
||||
##
|
||||
@DoNotImplement
|
||||
public interface net.corda.testing.driver.TestCorDapp
|
||||
@NotNull
|
||||
public abstract java.util.Set<Class<?>> getClasses()
|
||||
@NotNull
|
||||
public abstract String getName()
|
||||
@NotNull
|
||||
public abstract java.util.Set<java.net.URL> getResources()
|
||||
@NotNull
|
||||
public abstract String getTitle()
|
||||
@NotNull
|
||||
public abstract String getVendor()
|
||||
@NotNull
|
||||
public abstract String getVersion()
|
||||
@NotNull
|
||||
public abstract java.nio.file.Path packageAsJarInDirectory(java.nio.file.Path)
|
||||
public abstract void packageAsJarWithPath(java.nio.file.Path)
|
||||
##
|
||||
public static final class net.corda.testing.driver.TestCorDapp$Factory extends java.lang.Object
|
||||
public <init>()
|
||||
@NotNull
|
||||
public static final net.corda.testing.driver.TestCorDapp$Mutable create(String, String, String, String, java.util.Set<? extends Class<?>>, kotlin.jvm.functions.Function2<? super String, ? super java.net.URL, Boolean>)
|
||||
public static final net.corda.testing.driver.TestCorDapp$Factory$Companion Companion
|
||||
##
|
||||
public static final class net.corda.testing.driver.TestCorDapp$Factory$Companion extends java.lang.Object
|
||||
@NotNull
|
||||
public final net.corda.testing.driver.TestCorDapp$Mutable create(String, String, String, String, java.util.Set<? extends Class<?>>, kotlin.jvm.functions.Function2<? super String, ? super java.net.URL, Boolean>)
|
||||
##
|
||||
@DoNotImplement
|
||||
public static interface net.corda.testing.driver.TestCorDapp$Mutable extends net.corda.testing.driver.TestCorDapp
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minus(Class<?>)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusPackage(Package)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusPackage(String)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusPackages(Package, Package...)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusPackages(String, String...)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusPackages(java.util.Set<String>)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusResource(String, java.net.URL)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plus(Class<?>)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusPackage(Package)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusPackage(String)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusPackages(Package, Package...)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusPackages(String, String...)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusPackages(java.util.Set<String>)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusResource(String, java.net.URL)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable withClasses(java.util.Set<? extends Class<?>>)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable withName(String)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable withTitle(String)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable withVendor(String)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable withVersion(String)
|
||||
##
|
||||
public final class net.corda.testing.driver.VerifierType extends java.lang.Enum
|
||||
protected <init>()
|
||||
public static net.corda.testing.driver.VerifierType valueOf(String)
|
||||
@ -6167,6 +6243,8 @@ public class net.corda.testing.node.MockNetwork extends java.lang.Object
|
||||
@NotNull
|
||||
public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
||||
@NotNull
|
||||
public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
||||
@NotNull
|
||||
public final net.corda.testing.node.StartedMockNode createNode(net.corda.testing.node.MockNodeParameters)
|
||||
@NotNull
|
||||
public final net.corda.testing.node.StartedMockNode createPartyNode(net.corda.core.identity.CordaX500Name)
|
||||
@ -6183,6 +6261,8 @@ public class net.corda.testing.node.MockNetwork extends java.lang.Object
|
||||
@NotNull
|
||||
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
||||
@NotNull
|
||||
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
||||
@NotNull
|
||||
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.testing.node.MockNodeParameters)
|
||||
@NotNull
|
||||
public final java.util.List<String> getCordappPackages()
|
||||
@ -6263,6 +6343,7 @@ public final class net.corda.testing.node.MockNodeParameters extends java.lang.O
|
||||
public <init>()
|
||||
public <init>(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>)
|
||||
public <init>(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
||||
public <init>(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
||||
@Nullable
|
||||
public final Integer component1()
|
||||
@Nullable
|
||||
@ -6275,6 +6356,8 @@ public final class net.corda.testing.node.MockNodeParameters extends java.lang.O
|
||||
public final net.corda.testing.node.MockNodeParameters copy(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>)
|
||||
@NotNull
|
||||
public final net.corda.testing.node.MockNodeParameters copy(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
||||
@NotNull
|
||||
public final net.corda.testing.node.MockNodeParameters copy(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
||||
public boolean equals(Object)
|
||||
@NotNull
|
||||
public final kotlin.jvm.functions.Function1<net.corda.node.services.config.NodeConfiguration, Object> getConfigOverrides()
|
||||
@ -6297,14 +6380,14 @@ public final class net.corda.testing.node.MockNodeParameters extends java.lang.O
|
||||
##
|
||||
public class net.corda.testing.node.MockServices extends java.lang.Object implements net.corda.core.node.ServiceHub
|
||||
public <init>()
|
||||
public <init>(java.util.List<String>)
|
||||
public <init>(java.util.List<String>, net.corda.core.identity.CordaX500Name)
|
||||
public <init>(java.util.List<String>, net.corda.core.identity.CordaX500Name, java.security.KeyPair, java.security.KeyPair...)
|
||||
public <init>(java.util.List<String>, net.corda.core.identity.CordaX500Name, net.corda.core.node.services.IdentityService)
|
||||
public <init>(java.util.List<String>, net.corda.core.identity.CordaX500Name, net.corda.core.node.services.IdentityService, java.security.KeyPair, java.security.KeyPair...)
|
||||
public <init>(java.util.List<String>, net.corda.testing.core.TestIdentity, net.corda.core.node.services.IdentityService, net.corda.core.node.NetworkParameters, java.security.KeyPair...)
|
||||
public <init>(java.util.List<String>, net.corda.testing.core.TestIdentity, net.corda.core.node.services.IdentityService, java.security.KeyPair...)
|
||||
public <init>(java.util.List<String>, net.corda.testing.core.TestIdentity, java.security.KeyPair...)
|
||||
public <init>(Iterable<String>)
|
||||
public <init>(Iterable<String>, net.corda.core.identity.CordaX500Name)
|
||||
public <init>(Iterable<String>, net.corda.core.identity.CordaX500Name, java.security.KeyPair, java.security.KeyPair...)
|
||||
public <init>(Iterable<String>, net.corda.core.identity.CordaX500Name, net.corda.core.node.services.IdentityService)
|
||||
public <init>(Iterable<String>, net.corda.core.identity.CordaX500Name, net.corda.core.node.services.IdentityService, java.security.KeyPair, java.security.KeyPair...)
|
||||
public <init>(Iterable<String>, net.corda.testing.core.TestIdentity, net.corda.core.node.services.IdentityService, net.corda.core.node.NetworkParameters, java.security.KeyPair...)
|
||||
public <init>(Iterable<String>, net.corda.testing.core.TestIdentity, net.corda.core.node.services.IdentityService, java.security.KeyPair...)
|
||||
public <init>(Iterable<String>, net.corda.testing.core.TestIdentity, java.security.KeyPair...)
|
||||
public <init>(net.corda.core.identity.CordaX500Name)
|
||||
public <init>(net.corda.core.identity.CordaX500Name, java.security.KeyPair, java.security.KeyPair...)
|
||||
public <init>(net.corda.core.identity.CordaX500Name, net.corda.core.node.services.IdentityService)
|
||||
|
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -3,9 +3,9 @@
|
||||
|
||||
# PR Checklist:
|
||||
|
||||
- [ ] Have you run the unit, integration and smoke tests as described here? https://docs.corda.net/head/testing.html
|
||||
- [ ] If you added/changed public APIs, did you write/update the JavaDocs?
|
||||
- [ ] If the changes are of interest to application developers, have you added them to the changelog, and potentially release notes?
|
||||
- [ ] If you are contributing for the first time, please read the agreement in CONTRIBUTING.md now and add to this Pull Request that you agree to it.
|
||||
- [ ] Have you run the unit, integration and smoke tests as described [here](https://docs.corda.net/head/testing.html)?
|
||||
- [ ] If you added public APIs, did you write the JavaDocs?
|
||||
- [ ] If the changes are of interest to application developers, have you added them to the [changelog](https://github.com/corda/corda/blob/master/docs/source/changelog.rst), and potentially the [release notes](https://github.com/corda/corda/blob/master/docs/source/release-notes.rst)?
|
||||
- [ ] If you are contributing for the first time, please read the agreement in [CONTRIBUTING.md](https://github.com/corda/corda/blob/master/CONTRIBUTING.md) now and add a comment to this pull request stating that your PR is in accordance with the [Developer's Certificate of Origin](https://github.com/corda/corda/blob/master/CONTRIBUTING.md#developer-certificate-of-origin).
|
||||
|
||||
Thanks for your code, it's appreciated! :)
|
||||
|
@ -185,7 +185,7 @@ allprojects {
|
||||
jvmTarget = "1.8"
|
||||
javaParameters = true // Useful for reflection.
|
||||
freeCompilerArgs = ['-Xjvm-default=compatibility']
|
||||
allWarningsAsErrors = true
|
||||
allWarningsAsErrors = project.hasProperty('compilation.allWarningsAsErrors') ? project.property('compilation.allWarningsAsErrors').toBoolean() : true
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,7 +201,7 @@ allprojects {
|
||||
}
|
||||
|
||||
tasks.withType(Test) {
|
||||
failFast = true
|
||||
failFast = project.hasProperty('tests.failFast') ? project.property('tests.failFast').toBoolean() : true
|
||||
|
||||
// Prevent the project from creating temporary files outside of the build directory.
|
||||
systemProperty 'java.io.tmpdir', buildDir.absolutePath
|
||||
|
@ -29,6 +29,7 @@ import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.CHARLIE_NAME
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
@ -44,7 +45,7 @@ class IdentitySyncFlowTests {
|
||||
fun before() {
|
||||
// We run this in parallel threads to help catch any race conditions that may exist.
|
||||
mockNet = InternalMockNetwork(
|
||||
cordappPackages = listOf("net.corda.finance.contracts.asset", "net.corda.finance.schemas"),
|
||||
cordappsForAllNodes = cordappsForPackages("net.corda.finance.contracts.asset", "net.corda.finance.schemas"),
|
||||
networkSendManuallyPumped = false,
|
||||
threadPerNode = true
|
||||
)
|
||||
|
@ -137,7 +137,7 @@ class AttachmentTests : WithMockNet {
|
||||
// Makes a node that doesn't do sanity checking at load time.
|
||||
private fun makeBadNode(name: CordaX500Name) = mockNet.createNode(
|
||||
InternalMockNodeParameters(legalName = randomise(name)),
|
||||
nodeFactory = { args ->
|
||||
nodeFactory = { args, _ ->
|
||||
object : InternalMockNetwork.MockNode(args) {
|
||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false }
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import net.corda.testing.core.*
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Test
|
||||
|
||||
@ -37,7 +38,7 @@ class CollectSignaturesFlowTests : WithContracts {
|
||||
companion object {
|
||||
private val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB"))
|
||||
private val miniCorpServices = MockServices(listOf("net.corda.testing.contracts"), miniCorp, rigorousMock())
|
||||
private val classMockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.core.flows"))
|
||||
private val classMockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts", "net.corda.core.flows"))
|
||||
|
||||
private const val MAGIC_NUMBER = 1337
|
||||
|
||||
|
@ -30,10 +30,7 @@ import org.junit.Test
|
||||
|
||||
class ContractUpgradeFlowRPCTest : WithContracts, WithFinality {
|
||||
companion object {
|
||||
private val classMockNet = InternalMockNetwork(cordappPackages = listOf(
|
||||
"net.corda.testing.contracts",
|
||||
"net.corda.finance.contracts.asset",
|
||||
"net.corda.core.flows"))
|
||||
private val classMockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows"))
|
||||
|
||||
@JvmStatic
|
||||
@AfterClass
|
||||
@ -99,7 +96,7 @@ class ContractUpgradeFlowRPCTest : WithContracts, WithFinality {
|
||||
).get()
|
||||
}
|
||||
|
||||
private fun RPCDriverDSL.createTestUser() = rpcTestUser.copy(permissions = setOf(
|
||||
private fun createTestUser() = rpcTestUser.copy(permissions = setOf(
|
||||
startFlow<WithFinality.FinalityInvoker>(),
|
||||
startFlow<ContractUpgradeFlow.Initiate<*, *>>(),
|
||||
startFlow<ContractUpgradeFlow.Authorise>(),
|
||||
|
@ -35,6 +35,7 @@ import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Test
|
||||
@ -42,10 +43,7 @@ import java.util.*
|
||||
|
||||
class ContractUpgradeFlowTest : WithContracts, WithFinality {
|
||||
companion object {
|
||||
private val classMockNet = InternalMockNetwork(cordappPackages = listOf(
|
||||
"net.corda.testing.contracts",
|
||||
"net.corda.finance.contracts.asset",
|
||||
"net.corda.core.flows"))
|
||||
private val classMockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows"))
|
||||
|
||||
@JvmStatic
|
||||
@AfterClass
|
||||
|
@ -24,13 +24,14 @@ import net.corda.finance.issuedBy
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Test
|
||||
|
||||
class FinalityFlowTests : WithFinality {
|
||||
companion object {
|
||||
private val CHARLIE = TestIdentity(CHARLIE_NAME, 90).party
|
||||
private val classMockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
||||
private val classMockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.finance.contracts.asset"))
|
||||
|
||||
@JvmStatic
|
||||
@AfterClass
|
||||
|
@ -171,7 +171,7 @@ class AttachmentSerializationTest {
|
||||
}
|
||||
|
||||
private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String {
|
||||
client = mockNet.restartNode(client) { args ->
|
||||
client = mockNet.restartNode(client) { args, _ ->
|
||||
object : InternalMockNetwork.MockNode(args) {
|
||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ release, see :doc:`upgrade-notes`.
|
||||
|
||||
Unreleased
|
||||
----------
|
||||
* Introduced ``TestCorDapp`` and utilities to support asymmetric setups for nodes through ``DriverDSL``, ``MockNetwork`` and ``MockServices``.
|
||||
|
||||
* ``freeLocalHostAndPort``, ``freePort``, and ``getFreeLocalPorts`` from ``TestUtils`` have been deprecated as they
|
||||
don't provide any guarantee the returned port will be available which can result in flaky tests. Use ``PortAllocation.Incremental``
|
||||
|
@ -158,9 +158,9 @@ Installing the CorDapp JAR
|
||||
.. note:: Before installing a CorDapp, you must create one or more nodes to install it on. For instructions, please see
|
||||
:doc:`generating-a-node`.
|
||||
|
||||
At runtime, nodes will load any CorDapps present in their ``cordapps`` folder. Therefore in order to install a CorDapp on
|
||||
a node, the CorDapp JAR must be added to the ``<node_dir>/cordapps/`` folder, where ``node_dir`` is the folder in which
|
||||
the node's JAR and configuration files are stored.
|
||||
At start-up, nodes will load any CorDapps present in their ``cordapps`` folder. Therefore, in order to install a CorDapp on
|
||||
a node, the CorDapp JAR must be added to the ``<node_dir>/cordapps/`` folder (where ``node_dir`` is the folder in which
|
||||
the node's JAR and configuration files are stored) and the node restarted.
|
||||
|
||||
CorDapp configuration files
|
||||
---------------------------
|
||||
|
@ -317,7 +317,7 @@ Assuming all went well, you can view the newly-created IOU by accessing the vaul
|
||||
*Via web/example:*
|
||||
|
||||
* PartyA: Navigate to http://localhost:10009/web/example and hit the "refresh" button
|
||||
* PartyA: Navigate to http://localhost:10012/web/example and hit the "refresh" button
|
||||
* PartyB: Navigate to http://localhost:10012/web/example and hit the "refresh" button
|
||||
|
||||
The vault and web front-end of PartyC (at ``localhost:10015``) will not display any IOUs. This is because PartyC was
|
||||
not involved in this transaction.
|
||||
|
@ -12,6 +12,7 @@ import net.corda.finance.contracts.asset.cash.selection.AbstractCashSelection
|
||||
import net.corda.finance.contracts.getCashBalance
|
||||
import net.corda.finance.issuedBy
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
@ -19,7 +20,7 @@ import org.junit.After
|
||||
import org.junit.Test
|
||||
|
||||
class CashSelectionTest {
|
||||
private val mockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.finance"), threadPerNode = true)
|
||||
private val mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.finance"), threadPerNode = true)
|
||||
|
||||
@After
|
||||
fun cleanUp() {
|
||||
|
@ -21,18 +21,25 @@ import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.MockCordappConfigProvider
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.getTimestampAsDirectoryName
|
||||
import net.corda.testing.node.internal.packageInDirectory
|
||||
import net.corda.testing.services.MockAttachmentStorage
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
class AttachmentsClassLoaderStaticContractTests {
|
||||
private companion object {
|
||||
@ -70,7 +77,7 @@ class AttachmentsClassLoaderStaticContractTests {
|
||||
}
|
||||
|
||||
private val serviceHub = rigorousMock<ServicesForResolution>().also {
|
||||
doReturn(CordappProviderImpl(CordappLoader.createWithTestPackages(listOf("net.corda.nodeapi.internal")), MockCordappConfigProvider(), MockAttachmentStorage(), testNetworkParameters().whitelistedContractImplementations)).whenever(it).cordappProvider
|
||||
doReturn(CordappProviderImpl(cordappLoaderForPackages(listOf("net.corda.nodeapi.internal")), MockCordappConfigProvider(), MockAttachmentStorage(), testNetworkParameters().whitelistedContractImplementations)).whenever(it).cordappProvider
|
||||
doReturn(testNetworkParameters()).whenever(it).networkParameters
|
||||
}
|
||||
|
||||
@ -92,4 +99,18 @@ class AttachmentsClassLoaderStaticContractTests {
|
||||
|
||||
assertNotNull(contract)
|
||||
}
|
||||
|
||||
private fun cordappLoaderForPackages(packages: Iterable<String>): CordappLoader {
|
||||
|
||||
val cordapps = cordappsForPackages(packages)
|
||||
return testDirectory().let { directory ->
|
||||
cordapps.packageInDirectory(directory)
|
||||
JarScanningCordappLoader.fromDirectories(listOf(directory))
|
||||
}
|
||||
}
|
||||
|
||||
private fun testDirectory(): Path {
|
||||
|
||||
return Paths.get("build", getTimestampAsDirectoryName())
|
||||
}
|
||||
}
|
@ -144,6 +144,7 @@ dependencies {
|
||||
testCompile project(':test-utils')
|
||||
testCompile project(':client:jfx')
|
||||
testCompile project(':finance')
|
||||
testCompile project(':finance:isolated')
|
||||
|
||||
// sample test schemas
|
||||
testCompile project(path: ':finance', configuration: 'testArtifacts')
|
||||
|
@ -0,0 +1,117 @@
|
||||
package net.corda.node
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import com.google.common.base.Stopwatch
|
||||
import net.corda.client.rpc.CordaRPCClient
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.minutes
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.flows.CashPaymentFlow
|
||||
import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.InProcess
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.driver.internal.internalServices
|
||||
import net.corda.testing.internal.performance.div
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.node.User
|
||||
import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages
|
||||
import net.corda.testing.node.internal.internalDriver
|
||||
import net.corda.testing.node.internal.performance.startPublishingFixedRateInjector
|
||||
import net.corda.testing.node.internal.performance.startReporter
|
||||
import net.corda.testing.node.internal.performance.startTightLoopInjector
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.streams.toList
|
||||
|
||||
@Ignore("Run these locally")
|
||||
class NodePerformanceTests {
|
||||
@StartableByRPC
|
||||
class EmptyFlow : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
}
|
||||
}
|
||||
|
||||
private data class FlowMeasurementResult(
|
||||
val flowPerSecond: Double,
|
||||
val averageMs: Double
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `empty flow per second`() {
|
||||
driver(DriverParameters(startNodesInProcess = true)) {
|
||||
val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlow<EmptyFlow>())))).get()
|
||||
|
||||
CordaRPCClient(a.rpcAddress).use("A", "A") { connection ->
|
||||
val timings = Collections.synchronizedList(ArrayList<Long>())
|
||||
val N = 10000
|
||||
val overallTiming = Stopwatch.createStarted().apply {
|
||||
startTightLoopInjector(
|
||||
parallelism = 8,
|
||||
numberOfInjections = N,
|
||||
queueBound = 50
|
||||
) {
|
||||
val timing = Stopwatch.createStarted().apply {
|
||||
connection.proxy.startFlow(::EmptyFlow).returnValue.get()
|
||||
}.stop().elapsed(TimeUnit.MICROSECONDS)
|
||||
timings.add(timing)
|
||||
}
|
||||
}.stop().elapsed(TimeUnit.MICROSECONDS)
|
||||
println(
|
||||
FlowMeasurementResult(
|
||||
flowPerSecond = N / (overallTiming * 0.000001),
|
||||
averageMs = timings.average() * 0.001
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `empty flow rate`() {
|
||||
internalDriver(startNodesInProcess = true) {
|
||||
val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlow<EmptyFlow>())))).get()
|
||||
a as InProcess
|
||||
val metricRegistry = startReporter(this.shutdownManager, a.internalServices.monitoringService.metrics)
|
||||
CordaRPCClient(a.rpcAddress).use("A", "A") { connection ->
|
||||
startPublishingFixedRateInjector(metricRegistry, 8, 5.minutes, 2000L / TimeUnit.SECONDS) {
|
||||
connection.proxy.startFlow(::EmptyFlow).returnValue.get()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `self pay rate`() {
|
||||
val user = User("A", "A", setOf(startFlow<CashIssueFlow>(), startFlow<CashPaymentFlow>()))
|
||||
internalDriver(
|
||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, rpcUsers = listOf(user))),
|
||||
startNodesInProcess = true,
|
||||
cordappsForAllNodes = cordappsInCurrentAndAdditionalPackages("net.corda.finance")
|
||||
) {
|
||||
val notary = defaultNotaryNode.getOrThrow() as InProcess
|
||||
val metricRegistry = startReporter(this.shutdownManager, notary.internalServices.monitoringService.metrics)
|
||||
CordaRPCClient(notary.rpcAddress).use("A", "A") { connection ->
|
||||
println("ISSUING")
|
||||
val doneFutures = (1..100).toList().parallelStream().map {
|
||||
connection.proxy.startFlow(::CashIssueFlow, 1.DOLLARS, OpaqueBytes.of(0), defaultNotaryIdentity).returnValue
|
||||
}.toList()
|
||||
doneFutures.transpose().get()
|
||||
println("STARTING PAYMENT")
|
||||
startPublishingFixedRateInjector(metricRegistry, 8, 5.minutes, 100L / TimeUnit.SECONDS) {
|
||||
connection.proxy.startFlow(::CashPaymentFlow, 1.DOLLARS, defaultNotaryIdentity).returnValue.get()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package net.corda.node.flows
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.internal.packageName
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.unwrap
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import net.corda.testing.driver.driver
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class AsymmetricCorDappsTests {
|
||||
|
||||
@StartableByRPC
|
||||
@InitiatingFlow
|
||||
class Ping(private val pongParty: Party, val times: Int) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
val pongSession = initiateFlow(pongParty)
|
||||
pongSession.sendAndReceive<Unit>(times)
|
||||
for (i in 1..times) {
|
||||
val j = pongSession.sendAndReceive<Int>(i).unwrap { it }
|
||||
assertEquals(i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@InitiatedBy(Ping::class)
|
||||
class Pong(private val pingSession: FlowSession) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
val times = pingSession.sendAndReceive<Int>(Unit).unwrap { it }
|
||||
for (i in 1..times) {
|
||||
val j = pingSession.sendAndReceive<Int>(i).unwrap { it }
|
||||
assertEquals(i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun noSharedCorDappsWithAsymmetricSpecificClasses() {
|
||||
|
||||
driver(DriverParameters(startNodesInProcess = false, cordappsForAllNodes = emptySet())) {
|
||||
|
||||
val nodeA = startNode(additionalCordapps = setOf(TestCorDapp.Factory.create("Szymon CorDapp", "1.0", classes = setOf(Ping::class.java)))).getOrThrow()
|
||||
val nodeB = startNode(additionalCordapps = setOf(TestCorDapp.Factory.create("Szymon CorDapp", "1.0", classes = setOf(Ping::class.java, Pong::class.java)))).getOrThrow()
|
||||
nodeA.rpc.startFlow(::Ping, nodeB.nodeInfo.singleIdentity(), 1).returnValue.getOrThrow()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sharedCorDappsWithAsymmetricSpecificClasses() {
|
||||
|
||||
val resourceName = "cordapp.properties"
|
||||
val cordappPropertiesResource = this::class.java.getResource(resourceName)
|
||||
val sharedCordapp = TestCorDapp.Factory.create("shared", "1.0", classes = setOf(Ping::class.java)).plusResource("${AsymmetricCorDappsTests::class.java.packageName}.$resourceName", cordappPropertiesResource)
|
||||
val cordappForNodeB = TestCorDapp.Factory.create("nodeB_only", "1.0", classes = setOf(Pong::class.java))
|
||||
driver(DriverParameters(startNodesInProcess = false, cordappsForAllNodes = setOf(sharedCordapp))) {
|
||||
|
||||
val (nodeA, nodeB) = listOf(startNode(), startNode(additionalCordapps = setOf(cordappForNodeB))).transpose().getOrThrow()
|
||||
nodeA.rpc.startFlow(::Ping, nodeB.nodeInfo.singleIdentity(), 1).returnValue.getOrThrow()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sharedCorDappsWithAsymmetricSpecificClassesInProcess() {
|
||||
|
||||
val resourceName = "cordapp.properties"
|
||||
val cordappPropertiesResource = this::class.java.getResource(resourceName)
|
||||
val sharedCordapp = TestCorDapp.Factory.create("shared", "1.0", classes = setOf(Ping::class.java)).plusResource("${AsymmetricCorDappsTests::class.java.packageName}.$resourceName", cordappPropertiesResource)
|
||||
val cordappForNodeB = TestCorDapp.Factory.create("nodeB_only", "1.0", classes = setOf(Pong::class.java))
|
||||
driver(DriverParameters(startNodesInProcess = true, cordappsForAllNodes = setOf(sharedCordapp))) {
|
||||
|
||||
val (nodeA, nodeB) = listOf(startNode(), startNode(additionalCordapps = setOf(cordappForNodeB))).transpose().getOrThrow()
|
||||
nodeA.rpc.startFlow(::Ping, nodeB.nodeInfo.singleIdentity(), 1).returnValue.getOrThrow()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
package net.corda.node.flows
|
||||
|
||||
import net.corda.client.rpc.CordaRPCClient
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.list
|
||||
import net.corda.core.internal.readLines
|
||||
import net.corda.core.messaging.startTrackedFlow
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.internal.CheckpointIncompatibleException
|
||||
import net.corda.node.internal.NodeStartup
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.testMessage.Message
|
||||
import net.corda.testMessage.MessageState
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.node.User
|
||||
import net.corda.testing.node.internal.ListenProcessDeathException
|
||||
import net.test.cordapp.v1.SendMessageFlow
|
||||
import org.junit.Test
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
class FlowCheckpointVersionNodeStartupCheckTest {
|
||||
companion object {
|
||||
val message = Message("Hello world!")
|
||||
val classes = setOf(net.corda.testMessage.MessageState::class.java,
|
||||
net.corda.testMessage.MessageContract::class.java,
|
||||
net.test.cordapp.v1.SendMessageFlow::class.java,
|
||||
net.test.cordapp.v1.Record::class.java)
|
||||
val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery"), invokeRpc("vaultTrack")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `restart node successfully with suspended flow`() {
|
||||
|
||||
val cordapps = setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes))
|
||||
|
||||
return driver(DriverParameters(isDebug = true, startNodesInProcess = false, inMemoryDB = false, cordappsForAllNodes = cordapps)) {
|
||||
{
|
||||
val alice = startNode(rpcUsers = listOf(user), providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(rpcUsers = listOf(user), providedName = BOB_NAME).getOrThrow()
|
||||
alice.stop()
|
||||
CordaRPCClient(bob.rpcAddress).start(user.username, user.password).use {
|
||||
val flowTracker = it.proxy.startTrackedFlow(::SendMessageFlow, message, defaultNotaryIdentity, alice.nodeInfo.singleIdentity()).progress
|
||||
//wait until Bob progresses as far as possible because alice node is off
|
||||
flowTracker.takeFirst { it == SendMessageFlow.Companion.FINALISING_TRANSACTION.label }.toBlocking().single()
|
||||
}
|
||||
bob.stop()
|
||||
}()
|
||||
val result = {
|
||||
//Bob will resume the flow
|
||||
val alice = startNode(rpcUsers = listOf(user), providedName = ALICE_NAME, customOverrides = mapOf("devMode" to false)).getOrThrow()
|
||||
startNode(providedName = BOB_NAME, rpcUsers = listOf(user), customOverrides = mapOf("devMode" to false)).getOrThrow()
|
||||
CordaRPCClient(alice.rpcAddress).start(user.username, user.password).use {
|
||||
val page = it.proxy.vaultTrack(MessageState::class.java)
|
||||
if (page.snapshot.states.isNotEmpty()) {
|
||||
page.snapshot.states.first()
|
||||
} else {
|
||||
val r = page.updates.timeout(5, TimeUnit.SECONDS).take(1).toBlocking().single()
|
||||
if (r.consumed.isNotEmpty()) r.consumed.first() else r.produced.first()
|
||||
}
|
||||
}
|
||||
}()
|
||||
assertNotNull(result)
|
||||
assertEquals(message, result.state.data.message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertNodeRestartFailure(
|
||||
cordapps: Set<TestCorDapp>?,
|
||||
cordappsVersionAtStartup: Set<TestCorDapp>,
|
||||
cordappsVersionAtRestart: Set<TestCorDapp>,
|
||||
reuseAdditionalCordappsAtRestart: Boolean,
|
||||
assertNodeLogs: String
|
||||
) {
|
||||
|
||||
return driver(DriverParameters(
|
||||
startNodesInProcess = false, // start nodes in separate processes to ensure CordappLoader is not shared between restarts
|
||||
inMemoryDB = false, // ensure database is persisted between node restarts so we can keep suspended flow in Bob's node
|
||||
cordappsForAllNodes = cordapps)
|
||||
) {
|
||||
val bobLogFolder = {
|
||||
val alice = startNode(rpcUsers = listOf(user), providedName = ALICE_NAME, additionalCordapps = cordappsVersionAtStartup).getOrThrow()
|
||||
val bob = startNode(rpcUsers = listOf(user), providedName = BOB_NAME, additionalCordapps = cordappsVersionAtStartup).getOrThrow()
|
||||
alice.stop()
|
||||
CordaRPCClient(bob.rpcAddress).start(user.username, user.password).use {
|
||||
val flowTracker = it.proxy.startTrackedFlow(::SendMessageFlow, message, defaultNotaryIdentity, alice.nodeInfo.singleIdentity()).progress
|
||||
// wait until Bob progresses as far as possible because Alice node is offline
|
||||
flowTracker.takeFirst { it == SendMessageFlow.Companion.FINALISING_TRANSACTION.label }.toBlocking().single()
|
||||
}
|
||||
val logFolder = bob.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME
|
||||
// SendMessageFlow suspends in Bob node
|
||||
bob.stop()
|
||||
logFolder
|
||||
}()
|
||||
|
||||
startNode(rpcUsers = listOf(user), providedName = ALICE_NAME, customOverrides = mapOf("devMode" to false),
|
||||
additionalCordapps = cordappsVersionAtRestart, regenerateCordappsOnStart = !reuseAdditionalCordappsAtRestart).getOrThrow()
|
||||
|
||||
assertFailsWith(ListenProcessDeathException::class) {
|
||||
startNode(providedName = BOB_NAME, rpcUsers = listOf(user), customOverrides = mapOf("devMode" to false),
|
||||
additionalCordapps = cordappsVersionAtRestart, regenerateCordappsOnStart = !reuseAdditionalCordappsAtRestart).getOrThrow()
|
||||
}
|
||||
|
||||
val logFile = bobLogFolder.list { it.filter { it.fileName.toString().endsWith(".log") }.findAny().get() }
|
||||
val numberOfNodesThatLogged = logFile.readLines { it.filter { assertNodeLogs in it }.count() }
|
||||
assertEquals(1, numberOfNodesThatLogged)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `restart nodes with incompatible version of suspended flow due to different jar name`() {
|
||||
|
||||
assertNodeRestartFailure(
|
||||
emptySet(),
|
||||
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
|
||||
setOf(TestCorDapp.Factory.create("testJar2", "1.0", classes = classes)),
|
||||
false,
|
||||
CheckpointIncompatibleException.FlowNotInstalledException(SendMessageFlow::class.java).message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `restart nodes with incompatible version of suspended flow`() {
|
||||
|
||||
assertNodeRestartFailure(
|
||||
emptySet(),
|
||||
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
|
||||
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes + net.test.cordapp.v1.SendMessageFlow::class.java)),
|
||||
false,
|
||||
// the part of the log message generated by CheckpointIncompatibleException.FlowVersionIncompatibleException
|
||||
"that is incompatible with the current installed version of")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `restart nodes with incompatible version of suspended flow due to different timestamps only`() {
|
||||
|
||||
assertNodeRestartFailure(
|
||||
emptySet(),
|
||||
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
|
||||
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
|
||||
false,
|
||||
// the part of the log message generated by CheckpointIncompatibleException.FlowVersionIncompatibleException
|
||||
"that is incompatible with the current installed version of")
|
||||
}
|
||||
}
|
@ -58,7 +58,7 @@ class AttachmentLoadingTests : IntegrationTest() {
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
private val attachments = MockAttachmentStorage()
|
||||
private val provider = CordappProviderImpl(CordappLoader.createDevMode(listOf(isolatedJAR)), MockCordappConfigProvider(), attachments, testNetworkParameters().whitelistedContractImplementations)
|
||||
private val provider = CordappProviderImpl(JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR)), MockCordappConfigProvider(), attachments, testNetworkParameters().whitelistedContractImplementations)
|
||||
private val cordapp get() = provider.cordapps.first()
|
||||
private val attachmentId get() = provider.getCordappAttachmentId(cordapp)!!
|
||||
private val appContext get() = provider.getAppContext(cordapp)
|
||||
@ -86,13 +86,6 @@ class AttachmentLoadingTests : IntegrationTest() {
|
||||
startNode(providedName = bankBName)
|
||||
).transpose().getOrThrow()
|
||||
}
|
||||
|
||||
private fun DriverDSL.installIsolatedCordappTo(nodeName: CordaX500Name) {
|
||||
// Copy the app jar to the first node. The second won't have it.
|
||||
val path = (baseDirectory(nodeName) / "cordapps").createDirectories() / "isolated.jar"
|
||||
logger.info("Installing isolated jar to $path")
|
||||
isolatedJAR.openStream().use { it.copyTo(path) }
|
||||
}
|
||||
}
|
||||
|
||||
private val services = object : ServicesForResolution {
|
||||
@ -125,9 +118,9 @@ class AttachmentLoadingTests : IntegrationTest() {
|
||||
@Test
|
||||
fun `test that attachments retrieved over the network are not used for code`() {
|
||||
withoutTestSerialization {
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
|
||||
installIsolatedCordappTo(bankAName)
|
||||
val (bankA, bankB) = createTwoNodes()
|
||||
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList(), cordappsForAllNodes = emptySet())) {
|
||||
val bankA = startNode(providedName = bankAName, additionalCordapps = cordappsForPackages("net.corda.finance.contracts.isolated")).getOrThrow()
|
||||
val bankB = startNode(providedName = bankBName, additionalCordapps = cordappsForPackages("net.corda.finance.contracts.isolated")).getOrThrow()
|
||||
assertFailsWith<CordaRuntimeException>("Party C=CH,L=Zurich,O=BankB rejected session request: Don't know net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator") {
|
||||
bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
|
||||
}
|
||||
@ -135,16 +128,4 @@ class AttachmentLoadingTests : IntegrationTest() {
|
||||
Unit
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `tests that if the attachment is loaded on both sides already that a flow can run`() {
|
||||
withoutTestSerialization {
|
||||
driver {
|
||||
installIsolatedCordappTo(bankAName)
|
||||
installIsolatedCordappTo(bankBName)
|
||||
val (bankA, bankB) = createTwoNodes()
|
||||
bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -16,11 +16,7 @@ import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TimeWindow
|
||||
import net.corda.core.crypto.CompositeKey
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.crypto.isFulfilledBy
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.flows.NotaryError
|
||||
import net.corda.core.flows.NotaryException
|
||||
import net.corda.core.flows.NotaryFlow
|
||||
@ -49,6 +45,7 @@ import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.node.TestClock
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNetwork.MockNode
|
||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
||||
@ -63,18 +60,8 @@ import java.nio.file.Paths
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.util.concurrent.ExecutionException
|
||||
import kotlin.collections.List
|
||||
import kotlin.collections.component1
|
||||
import kotlin.collections.component2
|
||||
import kotlin.collections.distinct
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.last
|
||||
import kotlin.collections.listOf
|
||||
import kotlin.collections.map
|
||||
import kotlin.collections.mapIndexedNotNull
|
||||
import kotlin.collections.plus
|
||||
import kotlin.collections.single
|
||||
import kotlin.collections.zip
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertTrue
|
||||
@ -94,7 +81,7 @@ class BFTNotaryServiceTests {
|
||||
@JvmStatic
|
||||
fun before() {
|
||||
IntegrationTest.globalSetUp() //Enterprise only - remote db setup
|
||||
mockNet = InternalMockNetwork(listOf("net.corda.testing.contracts"))
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts"))
|
||||
val clusterSize = minClusterSize(1)
|
||||
val started = startBftClusterAndNode(clusterSize, mockNet)
|
||||
notary = started.first
|
||||
|
@ -14,6 +14,7 @@ import net.corda.testing.core.dummyCommand
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNetwork.MockNode
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
@ -34,7 +35,7 @@ class BFTSMaRtTests : IntegrationTest() {
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
mockNet = InternalMockNetwork(listOf("net.corda.testing.contracts"))
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts"))
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -32,6 +32,7 @@ import net.corda.testing.internal.DEV_ROOT_CA
|
||||
import net.corda.testing.internal.IntegrationTest
|
||||
import net.corda.testing.internal.IntegrationTestSchemas
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages
|
||||
import net.corda.testing.node.internal.SharedCompatibilityZoneParams
|
||||
import net.corda.testing.node.internal.internalDriver
|
||||
import net.corda.testing.node.internal.network.NetworkMapServer
|
||||
@ -100,7 +101,7 @@ class NodeRegistrationTest : IntegrationTest() {
|
||||
compatibilityZone = compatibilityZone,
|
||||
initialiseSerialization = false,
|
||||
notarySpecs = listOf(NotarySpec(notaryName)),
|
||||
extraCordappPackagesToScan = listOf("net.corda.finance"),
|
||||
cordappsForAllNodes = cordappsInCurrentAndAdditionalPackages("net.corda.finance"),
|
||||
notaryCustomOverrides = mapOf("devMode" to false)
|
||||
) {
|
||||
val (alice, genevieve) = listOf(
|
||||
|
@ -0,0 +1,68 @@
|
||||
package net.test.cordapp.v1
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.contracts.Command
|
||||
import net.corda.core.contracts.StateAndContract
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.StatesToRecord
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.testMessage.MESSAGE_CONTRACT_PROGRAM_ID
|
||||
import net.corda.testMessage.Message
|
||||
import net.corda.testMessage.MessageContract
|
||||
import net.corda.testMessage.MessageState
|
||||
|
||||
@StartableByRPC
|
||||
@InitiatingFlow
|
||||
class SendMessageFlow(private val message: Message, private val notary: Party, private val reciepent: Party? = null) : FlowLogic<SignedTransaction>() {
|
||||
companion object {
|
||||
object GENERATING_TRANSACTION : ProgressTracker.Step("Generating transaction based on the message.")
|
||||
object VERIFYING_TRANSACTION : ProgressTracker.Step("Verifying contract constraints.")
|
||||
object SIGNING_TRANSACTION : ProgressTracker.Step("Signing transaction with our private key.")
|
||||
object FINALISING_TRANSACTION : ProgressTracker.Step("Obtaining notary signature and recording transaction.") {
|
||||
override fun childProgressTracker() = FinalityFlow.tracker()
|
||||
}
|
||||
|
||||
fun tracker() = ProgressTracker(GENERATING_TRANSACTION, VERIFYING_TRANSACTION, SIGNING_TRANSACTION, FINALISING_TRANSACTION)
|
||||
}
|
||||
|
||||
override val progressTracker = tracker()
|
||||
|
||||
@Suspendable
|
||||
override fun call(): SignedTransaction {
|
||||
progressTracker.currentStep = GENERATING_TRANSACTION
|
||||
|
||||
val messageState = MessageState(message = message, by = ourIdentity)
|
||||
val txCommand = Command(MessageContract.Commands.Send(), messageState.participants.map { it.owningKey })
|
||||
val txBuilder = TransactionBuilder(notary).withItems(StateAndContract(messageState, MESSAGE_CONTRACT_PROGRAM_ID), txCommand)
|
||||
|
||||
progressTracker.currentStep = VERIFYING_TRANSACTION
|
||||
txBuilder.toWireTransaction(serviceHub).toLedgerTransaction(serviceHub).verify()
|
||||
|
||||
progressTracker.currentStep = SIGNING_TRANSACTION
|
||||
val signedTx = serviceHub.signInitialTransaction(txBuilder)
|
||||
|
||||
progressTracker.currentStep = FINALISING_TRANSACTION
|
||||
|
||||
if (reciepent != null) {
|
||||
val session = initiateFlow(reciepent)
|
||||
subFlow(SendTransactionFlow(session, signedTx))
|
||||
return subFlow(FinalityFlow(signedTx, setOf(reciepent), FINALISING_TRANSACTION.childProgressTracker()))
|
||||
} else {
|
||||
return subFlow(FinalityFlow(signedTx, FINALISING_TRANSACTION.childProgressTracker()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@InitiatedBy(SendMessageFlow::class)
|
||||
class Record(private val session: FlowSession) : FlowLogic<Unit>() {
|
||||
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
val tx = subFlow(ReceiveTransactionFlow(session, statesToRecord = StatesToRecord.ALL_VISIBLE))
|
||||
serviceHub.addSignature(tx)
|
||||
}
|
||||
}
|
32
node/src/main/kotlin/net/corda/node/cordapp/CordappLoader.kt
Normal file
32
node/src/main/kotlin/net/corda/node/cordapp/CordappLoader.kt
Normal file
@ -0,0 +1,32 @@
|
||||
package net.corda.node.cordapp
|
||||
|
||||
import net.corda.core.cordapp.Cordapp
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
|
||||
/**
|
||||
* Handles loading [Cordapp]s.
|
||||
*/
|
||||
interface CordappLoader {
|
||||
|
||||
/**
|
||||
* Returns all [Cordapp]s found.
|
||||
*/
|
||||
val cordapps: List<Cordapp>
|
||||
|
||||
/**
|
||||
* Returns a [ClassLoader] containing all types from all [Cordapp]s.
|
||||
*/
|
||||
val appClassLoader: ClassLoader
|
||||
|
||||
/**
|
||||
* Returns a map between flow class and owning [Cordapp].
|
||||
* The mappings are unique, and the node will not start otherwise.
|
||||
*/
|
||||
val flowCordappMap: Map<Class<out FlowLogic<*>>, Cordapp>
|
||||
|
||||
/**
|
||||
* Returns all [MappedSchema] found inside the [Cordapp]s.
|
||||
*/
|
||||
val cordappSchemas: Set<MappedSchema>
|
||||
}
|
@ -67,10 +67,11 @@ import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.minutes
|
||||
import net.corda.node.CordaClock
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.cordapp.CordappLoader
|
||||
import net.corda.node.internal.CheckpointVerifier.verifyCheckpointsCompatible
|
||||
import net.corda.node.internal.classloading.requireAnnotation
|
||||
import net.corda.node.internal.cordapp.CordappConfigFileProvider
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||
import net.corda.node.internal.cordapp.CordappProviderInternal
|
||||
import net.corda.node.internal.rpc.proxies.AuthenticatedRpcOpsProxy
|
||||
|
@ -22,7 +22,6 @@ import net.corda.core.internal.Emoji
|
||||
import net.corda.core.internal.concurrent.thenMatch
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.RelayConfiguration
|
||||
import net.corda.node.services.statemachine.MultiThreadedStateMachineExecutor
|
||||
@ -38,9 +37,8 @@ import java.util.concurrent.TimeUnit
|
||||
|
||||
open class EnterpriseNode(configuration: NodeConfiguration,
|
||||
versionInfo: VersionInfo,
|
||||
initialiseSerialization: Boolean = true,
|
||||
cordappLoader: CordappLoader = makeCordappLoader(configuration, versionInfo)
|
||||
) : Node(configuration, versionInfo, initialiseSerialization, cordappLoader) {
|
||||
initialiseSerialization: Boolean = true
|
||||
) : Node(configuration, versionInfo, initialiseSerialization) {
|
||||
companion object {
|
||||
private val logger by lazy { loggerFor<EnterpriseNode>() }
|
||||
|
||||
|
@ -35,8 +35,9 @@ import net.corda.node.SimpleClock
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.artemis.ArtemisBroker
|
||||
import net.corda.node.internal.artemis.BrokerAddresses
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.core.internal.errors.AddressBindingException
|
||||
import net.corda.node.cordapp.CordappLoader
|
||||
import net.corda.node.internal.security.RPCSecurityManagerImpl
|
||||
import net.corda.node.internal.security.RPCSecurityManagerWithAdditionalUser
|
||||
import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
|
||||
@ -124,15 +125,11 @@ open class Node(configuration: NodeConfiguration,
|
||||
}
|
||||
|
||||
private val sameVmNodeCounter = AtomicInteger()
|
||||
const val scanPackagesSystemProperty = "net.corda.node.cordapp.scan.packages"
|
||||
const val scanPackagesSeparator = ","
|
||||
|
||||
@JvmStatic
|
||||
protected fun makeCordappLoader(configuration: NodeConfiguration, versionInfo: VersionInfo): CordappLoader {
|
||||
|
||||
return System.getProperty(scanPackagesSystemProperty)?.let { scanPackages ->
|
||||
CordappLoader.createDefaultWithTestPackages(configuration, scanPackages.split(scanPackagesSeparator), versionInfo)
|
||||
} ?: CordappLoader.createDefault(configuration.baseDirectory, versionInfo)
|
||||
return JarScanningCordappLoader.fromDirectories(configuration.cordappDirectories, versionInfo)
|
||||
}
|
||||
// TODO: make this configurable.
|
||||
const val MAX_RPC_MESSAGE_SIZE = 10485760
|
||||
|
@ -23,6 +23,7 @@ import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.core.node.services.AttachmentStorage
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.cordapp.CordappLoader
|
||||
import java.net.URL
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
|
@ -1,16 +1,5 @@
|
||||
/*
|
||||
* R3 Proprietary and Confidential
|
||||
*
|
||||
* Copyright (c) 2018 R3 Limited. All rights reserved.
|
||||
*
|
||||
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
|
||||
*
|
||||
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||
*/
|
||||
|
||||
package net.corda.node.internal.cordapp
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
|
||||
import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult
|
||||
import net.corda.core.cordapp.Cordapp
|
||||
@ -18,50 +7,25 @@ import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.flows.ContractUpgradeFlow
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.InitiatedBy
|
||||
import net.corda.core.flows.SchedulableFlow
|
||||
import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.flows.StartableByService
|
||||
import net.corda.core.internal.VisibleForTesting
|
||||
import net.corda.core.internal.copyTo
|
||||
import net.corda.core.internal.cordapp.CordappImpl
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.internal.isRegularFile
|
||||
import net.corda.core.internal.list
|
||||
import net.corda.core.internal.objectOrNewInstance
|
||||
import net.corda.core.internal.outputStream
|
||||
import net.corda.core.internal.toPath
|
||||
import net.corda.core.internal.toTypedArray
|
||||
import net.corda.core.internal.walk
|
||||
import net.corda.core.node.services.CordaService
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.serialization.SerializationCustomSerializer
|
||||
import net.corda.core.serialization.SerializationWhitelist
|
||||
import net.corda.core.serialization.SerializeAsToken
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.cordapp.CordappLoader
|
||||
import net.corda.node.internal.classloading.requireAnnotation
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.nodeapi.internal.coreContractClasses
|
||||
import net.corda.serialization.internal.DefaultWhitelist
|
||||
import org.apache.commons.collections4.map.LRUMap
|
||||
import java.lang.reflect.Modifier
|
||||
import java.net.JarURLConnection
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.nio.file.attribute.FileTime
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.jar.JarInputStream
|
||||
import java.util.jar.JarOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.streams.toList
|
||||
|
||||
@ -70,20 +34,11 @@ import kotlin.streams.toList
|
||||
*
|
||||
* @property cordappJarPaths The classpath of cordapp JARs
|
||||
*/
|
||||
class CordappLoader private constructor(private val cordappJarPaths: List<RestrictedURL>, versionInfo: VersionInfo) {
|
||||
val cordapps: List<Cordapp> by lazy { loadCordapps() + coreCordapp }
|
||||
val appClassLoader: ClassLoader = URLClassLoader(cordappJarPaths.stream().map { it.url }.toTypedArray(), javaClass.classLoader)
|
||||
class JarScanningCordappLoader private constructor(private val cordappJarPaths: List<RestrictedURL>, versionInfo: VersionInfo = VersionInfo.UNKNOWN) : CordappLoaderTemplate() {
|
||||
|
||||
// Create a map of the CorDapps that provide a Flow. If a flow is not in this map it is a Core flow.
|
||||
// It also checks that there is only one CorDapp containing that flow class
|
||||
val flowCordappMap: Map<Class<out FlowLogic<*>>, Cordapp> by lazy {
|
||||
cordapps.flatMap { corDapp -> corDapp.allFlows.map { flow -> flow to corDapp } }
|
||||
.groupBy { it.first }
|
||||
.mapValues {
|
||||
if(it.value.size > 1) { throw MultipleCordappsForFlowException("There are multiple CorDapp JARs on the classpath for flow ${it.value.first().first.name}: [ ${it.value.joinToString { it.second.name }} ].") }
|
||||
it.value.single().second
|
||||
}
|
||||
}
|
||||
override val cordapps: List<Cordapp> by lazy { loadCordapps() + coreCordapp }
|
||||
|
||||
override val appClassLoader: ClassLoader = URLClassLoader(cordappJarPaths.stream().map { it.url }.toTypedArray(), javaClass.classLoader)
|
||||
|
||||
init {
|
||||
if (cordappJarPaths.isEmpty()) {
|
||||
@ -93,7 +48,47 @@ class CordappLoader private constructor(private val cordappJarPaths: List<Restri
|
||||
}
|
||||
}
|
||||
|
||||
val cordappSchemas: Set<MappedSchema> get() = cordapps.flatMap { it.customSchemas }.toSet()
|
||||
companion object {
|
||||
private val logger = contextLogger()
|
||||
|
||||
/**
|
||||
* Creates a CordappLoader from multiple directories.
|
||||
*
|
||||
* @param corDappDirectories Directories used to scan for CorDapp JARs.
|
||||
*/
|
||||
fun fromDirectories(corDappDirectories: Iterable<Path>, versionInfo: VersionInfo = VersionInfo.UNKNOWN): JarScanningCordappLoader {
|
||||
|
||||
logger.info("Looking for CorDapps in ${corDappDirectories.distinct().joinToString(", ", "[", "]")}")
|
||||
return JarScanningCordappLoader(corDappDirectories.distinct().flatMap(this::jarUrlsInDirectory).map { it.restricted() }, versionInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CordappLoader loader out of a list of JAR URLs.
|
||||
*
|
||||
* @param scanJars Uses the JAR URLs provided for classpath scanning and Cordapp detection.
|
||||
*/
|
||||
fun fromJarUrls(scanJars: List<URL>, versionInfo: VersionInfo = VersionInfo.UNKNOWN) = JarScanningCordappLoader(scanJars.map { it.restricted() }, versionInfo)
|
||||
|
||||
private fun URL.restricted(rootPackageName: String? = null) = RestrictedURL(this, rootPackageName)
|
||||
|
||||
private fun jarUrlsInDirectory(directory: Path): List<URL> {
|
||||
|
||||
return if (!directory.exists()) {
|
||||
emptyList()
|
||||
} else {
|
||||
directory.list { paths ->
|
||||
// `toFile()` can't be used here...
|
||||
paths.filter { it.toString().endsWith(".jar") }.map { it.toUri().toURL() }.toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A list of the core RPC flows present in Corda */
|
||||
private val coreRPCFlows = listOf(
|
||||
ContractUpgradeFlow.Initiate::class.java,
|
||||
ContractUpgradeFlow.Authorise::class.java,
|
||||
ContractUpgradeFlow.Deauthorise::class.java)
|
||||
}
|
||||
|
||||
/** A Cordapp representing the core package which is not scanned automatically. */
|
||||
@VisibleForTesting
|
||||
@ -107,165 +102,36 @@ class CordappLoader private constructor(private val cordappJarPaths: List<Restri
|
||||
serializationWhitelists = listOf(),
|
||||
serializationCustomSerializers = listOf(),
|
||||
customSchemas = setOf(),
|
||||
jarPath = ContractUpgradeFlow.javaClass.protectionDomain.codeSource.location, // Core JAR location
|
||||
info = CordappImpl.Info("corda-core", versionInfo.vendor, versionInfo.releaseVersion),
|
||||
allFlows = listOf(),
|
||||
jarPath = ContractUpgradeFlow.javaClass.location, // Core JAR location
|
||||
jarHash = SecureHash.allOnesHash
|
||||
)
|
||||
|
||||
companion object {
|
||||
private val logger = contextLogger()
|
||||
/**
|
||||
* Default cordapp dir name
|
||||
*/
|
||||
private const val CORDAPPS_DIR_NAME = "cordapps"
|
||||
|
||||
/**
|
||||
* Creates a default CordappLoader intended to be used in non-dev or non-test environments.
|
||||
*
|
||||
* @param baseDir The directory that this node is running in. Will use this to resolve the cordapps directory
|
||||
* for classpath scanning.
|
||||
*/
|
||||
fun createDefault(baseDir: Path, versionInfo: VersionInfo = VersionInfo.UNKNOWN) = CordappLoader(getNodeCordappURLs(baseDir), versionInfo)
|
||||
|
||||
// Cache for CordappLoaders to avoid costly classpath scanning
|
||||
private val cordappLoadersCache = Caffeine.newBuilder().softValues().build<List<RestrictedURL>, CordappLoader>()
|
||||
private val generatedCordapps = ConcurrentHashMap<URL, Path>()
|
||||
|
||||
private fun simplifyScanPackages(scanPackages: List<String>): List<String> {
|
||||
return scanPackages.sorted().fold(emptyList()) { listSoFar, packageName ->
|
||||
when {
|
||||
listSoFar.isEmpty() -> listOf(packageName)
|
||||
packageName.startsWith(listSoFar.last()) -> listSoFar // Squash ["com.foo", "com.foo.bar"] into just ["com.foo"]
|
||||
else -> listSoFar + packageName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a dev mode CordappLoader for test environments that creates and loads cordapps from the classpath
|
||||
* and cordapps directory. This is intended mostly for use by the driver.
|
||||
*
|
||||
* @param testPackages See [createWithTestPackages]
|
||||
*/
|
||||
@VisibleForTesting
|
||||
fun createDefaultWithTestPackages(configuration: NodeConfiguration, testPackages: List<String>, versionInfo: VersionInfo = VersionInfo.UNKNOWN): CordappLoader {
|
||||
if (!configuration.devMode) {
|
||||
logger.warn("Package scanning should only occur in dev mode!")
|
||||
}
|
||||
val urls = getNodeCordappURLs(configuration.baseDirectory) + simplifyScanPackages(testPackages).flatMap(this::getPackageURLs)
|
||||
return cordappLoadersCache.asMap().computeIfAbsent(urls) { values -> CordappLoader(values, versionInfo) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a dev mode CordappLoader for test environments that creates and loads cordapps from the classpath.
|
||||
* This is intended for use in unit and integration tests.
|
||||
*
|
||||
* @param testPackages List of package names that contain CorDapp classes that can be automatically turned into
|
||||
* CorDapps.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
fun createWithTestPackages(testPackages: List<String>, versionInfo: VersionInfo = VersionInfo.UNKNOWN): CordappLoader {
|
||||
val urls = simplifyScanPackages(testPackages).flatMap(this::getPackageURLs)
|
||||
return cordappLoadersCache.asMap().computeIfAbsent(urls) { values -> CordappLoader(values, versionInfo) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a dev mode CordappLoader intended only to be used in test environments
|
||||
*
|
||||
* @param scanJars Uses the JAR URLs provided for classpath scanning and Cordapp detection
|
||||
*/
|
||||
@VisibleForTesting
|
||||
fun createDevMode(scanJars: List<URL>, versionInfo: VersionInfo = VersionInfo.UNKNOWN) = CordappLoader(scanJars.map { RestrictedURL(it, null) }, versionInfo)
|
||||
|
||||
private fun getPackageURLs(scanPackage: String): List<RestrictedURL> {
|
||||
val resource = scanPackage.replace('.', '/')
|
||||
return this::class.java.classLoader.getResources(resource)
|
||||
.asSequence()
|
||||
// This is to only scan classes from test folders.
|
||||
.filter { url ->
|
||||
!url.toString().contains("main/$resource") || listOf("net.corda.core", "net.corda.node", "net.corda.finance").none { scanPackage.startsWith(it) }
|
||||
}
|
||||
.map { url ->
|
||||
if (url.protocol == "jar") {
|
||||
// When running tests from gradle this may be a corda module jar, so restrict to scanPackage:
|
||||
RestrictedURL((url.openConnection() as JarURLConnection).jarFileURL, scanPackage)
|
||||
} else {
|
||||
// No need to restrict as createDevCordappJar has already done that:
|
||||
RestrictedURL(createDevCordappJar(scanPackage, url, resource).toUri().toURL(), null)
|
||||
}
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
|
||||
/** Takes a package of classes and creates a JAR from them - only use in tests. */
|
||||
private fun createDevCordappJar(scanPackage: String, url: URL, resource: String): Path {
|
||||
return generatedCordapps.computeIfAbsent(url) {
|
||||
// TODO Using the driver in out-of-process mode causes each node to have their own copy of the same dev CorDapps
|
||||
val cordappDir = (Paths.get("build") / "tmp" / "generated-test-cordapps").createDirectories()
|
||||
val uuid = UUID.randomUUID()
|
||||
val cordappJar = cordappDir / "$scanPackage-$uuid.jar"
|
||||
logger.info("Generating a test-only CorDapp of classes discovered for package $scanPackage in $url: $cordappJar")
|
||||
val manifest = createTestManifest(resource, scanPackage, uuid)
|
||||
val scanDir = url.toPath()
|
||||
JarOutputStream(cordappJar.outputStream(), manifest).use { jos ->
|
||||
scanDir.walk {
|
||||
it.forEach {
|
||||
val entryPath = "$resource/${scanDir.relativize(it).toString().replace('\\', '/')}"
|
||||
val time = FileTime.from(Instant.EPOCH)
|
||||
val entry = ZipEntry(entryPath).setCreationTime(time).setLastAccessTime(time).setLastModifiedTime(time)
|
||||
jos.putNextEntry(entry)
|
||||
if (it.isRegularFile()) {
|
||||
it.copyTo(jos)
|
||||
}
|
||||
jos.closeEntry()
|
||||
}
|
||||
}
|
||||
}
|
||||
cordappJar
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNodeCordappURLs(baseDir: Path): List<RestrictedURL> {
|
||||
val cordappsDir = baseDir / CORDAPPS_DIR_NAME
|
||||
return if (!cordappsDir.exists()) {
|
||||
emptyList()
|
||||
} else {
|
||||
cordappsDir.list {
|
||||
it.filter { it.toString().endsWith(".jar") }.map { RestrictedURL(it.toUri().toURL(), null) }.toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A list of the core RPC flows present in Corda */
|
||||
private val coreRPCFlows = listOf(
|
||||
ContractUpgradeFlow.Initiate::class.java,
|
||||
ContractUpgradeFlow.Authorise::class.java,
|
||||
ContractUpgradeFlow.Deauthorise::class.java)
|
||||
private fun loadCordapps(): List<Cordapp> {
|
||||
return cordappJarPaths.map { scanCordapp(it).toCordapp(it) }
|
||||
}
|
||||
|
||||
private fun loadCordapps(): List<Cordapp> {
|
||||
return cordappJarPaths.map {
|
||||
val url = it.url
|
||||
val name = url.toPath().fileName.toString().removeSuffix(".jar")
|
||||
val info = url.openStream().let(::JarInputStream).use { it.manifest }.toCordappInfo(name)
|
||||
val scanResult = scanCordapp(it)
|
||||
CordappImpl(contractClassNames = findContractClassNames(scanResult),
|
||||
initiatedFlows = findInitiatedFlows(scanResult),
|
||||
rpcFlows = findRPCFlows(scanResult),
|
||||
serviceFlows = findServiceFlows(scanResult),
|
||||
schedulableFlows = findSchedulableFlows(scanResult),
|
||||
services = findServices(scanResult),
|
||||
serializationWhitelists = findPlugins(it),
|
||||
serializationCustomSerializers = findSerializers(scanResult),
|
||||
customSchemas = findCustomSchemas(scanResult),
|
||||
allFlows = findAllFlows(scanResult),
|
||||
jarPath = it.url,
|
||||
info = info,
|
||||
jarHash = getJarHash(it.url),
|
||||
name = name
|
||||
)
|
||||
}
|
||||
private fun RestrictedScanResult.toCordapp(url: RestrictedURL): Cordapp {
|
||||
|
||||
val name = url.url.toPath().fileName.toString().removeSuffix(".jar")
|
||||
val info = url.url.openStream().let(::JarInputStream).use { it.manifest }.toCordappInfo(name)
|
||||
return CordappImpl(
|
||||
findContractClassNames(this),
|
||||
findInitiatedFlows(this),
|
||||
findRPCFlows(this),
|
||||
findServiceFlows(this),
|
||||
findSchedulableFlows(this),
|
||||
findServices(this),
|
||||
findPlugins(url),
|
||||
findSerializers(this),
|
||||
findCustomSchemas(this),
|
||||
findAllFlows(this),
|
||||
url.url,
|
||||
info,
|
||||
getJarHash(url.url),
|
||||
name
|
||||
)
|
||||
}
|
||||
|
||||
private fun getJarHash(url: URL): SecureHash.SHA256 = url.openStream().readFully().sha256()
|
||||
@ -328,11 +194,12 @@ class CordappLoader private constructor(private val cordappJarPaths: List<Restri
|
||||
}
|
||||
|
||||
private val cachedScanResult = LRUMap<RestrictedURL, RestrictedScanResult>(1000)
|
||||
|
||||
private fun scanCordapp(cordappJarPath: RestrictedURL): RestrictedScanResult {
|
||||
logger.info("Scanning CorDapp in ${cordappJarPath.url}")
|
||||
return cachedScanResult.computeIfAbsent(cordappJarPath, {
|
||||
return cachedScanResult.computeIfAbsent(cordappJarPath) {
|
||||
RestrictedScanResult(FastClasspathScanner().addClassLoader(appClassLoader).overrideClasspath(cordappJarPath.url).scan(), cordappJarPath.qualifiedNamePrefix)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private class FlowTypeHierarchyComparator(val initiatingFlow: Class<out FlowLogic<*>>) : Comparator<Class<out FlowLogic<*>>> {
|
||||
@ -406,3 +273,26 @@ class CordappLoader private constructor(private val cordappJarPaths: List<Restri
|
||||
* Thrown when scanning CorDapps.
|
||||
*/
|
||||
class MultipleCordappsForFlowException(message: String) : Exception(message)
|
||||
|
||||
abstract class CordappLoaderTemplate : CordappLoader {
|
||||
|
||||
override val flowCordappMap: Map<Class<out FlowLogic<*>>, Cordapp> by lazy {
|
||||
cordapps.flatMap { corDapp -> corDapp.allFlows.map { flow -> flow to corDapp } }
|
||||
.groupBy { it.first }
|
||||
.mapValues {
|
||||
if(it.value.size > 1) { throw MultipleCordappsForFlowException("There are multiple CorDapp JARs on the classpath for flow ${it.value.first().first.name}: [ ${it.value.joinToString { it.second.name }} ].") }
|
||||
it.value.single().second
|
||||
}
|
||||
}
|
||||
|
||||
override val cordappSchemas: Set<MappedSchema> by lazy {
|
||||
|
||||
cordapps.flatMap { it.customSchemas }.toSet()
|
||||
}
|
||||
|
||||
override val appClassLoader: ClassLoader by lazy {
|
||||
|
||||
URLClassLoader(cordapps.stream().map { it.jarPath }.toTypedArray(), javaClass.classLoader)
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,9 @@ import java.util.*
|
||||
import java.util.jar.Attributes
|
||||
import java.util.jar.Manifest
|
||||
|
||||
internal fun createTestManifest(name: String, title: String, jarUUID: UUID): Manifest {
|
||||
fun createTestManifest(name: String, title: String, version: String, vendor: String): Manifest {
|
||||
|
||||
val manifest = Manifest()
|
||||
val version = "test-$jarUUID"
|
||||
val vendor = "R3"
|
||||
|
||||
// Mandatory manifest attribute. If not present, all other entries are silently skipped.
|
||||
manifest.mainAttributes[Attributes.Name.MANIFEST_VERSION] = "1.0"
|
||||
@ -37,11 +36,18 @@ internal fun createTestManifest(name: String, title: String, jarUUID: UUID): Man
|
||||
return manifest
|
||||
}
|
||||
|
||||
internal operator fun Manifest.set(key: String, value: String) {
|
||||
internal fun createTestManifest(name: String, title: String, jarUUID: UUID): Manifest {
|
||||
|
||||
return createTestManifest(name, title, "test-$jarUUID", "R3")
|
||||
}
|
||||
|
||||
operator fun Manifest.set(key: String, value: String) {
|
||||
|
||||
mainAttributes.putValue(key, value)
|
||||
}
|
||||
|
||||
internal fun Manifest?.toCordappInfo(defaultShortName: String): Cordapp.Info {
|
||||
|
||||
var unknown = CordappImpl.Info.UNKNOWN
|
||||
(this?.mainAttributes?.getValue("Name") ?: defaultShortName).let { shortName ->
|
||||
unknown = unknown.copy(shortName = shortName)
|
||||
|
@ -15,6 +15,7 @@ import com.typesafe.config.ConfigException
|
||||
import net.corda.core.context.AuthServiceId
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.TimedFlow
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.seconds
|
||||
@ -39,6 +40,7 @@ val Int.MB: Long get() = this * 1024L * 1024L
|
||||
|
||||
private val DEFAULT_FLOW_MONITOR_PERIOD_MILLIS: Duration = Duration.ofMinutes(1)
|
||||
private val DEFAULT_FLOW_MONITOR_SUSPENSION_LOGGING_THRESHOLD_MILLIS: Duration = Duration.ofMinutes(1)
|
||||
private const val CORDAPPS_DIR_NAME_DEFAULT = "cordapps"
|
||||
|
||||
interface NodeConfiguration : NodeSSLConfiguration {
|
||||
val myLegalName: CordaX500Name
|
||||
@ -83,6 +85,7 @@ interface NodeConfiguration : NodeSSLConfiguration {
|
||||
val effectiveH2Settings: NodeH2Settings?
|
||||
val flowMonitorPeriodMillis: Duration get() = DEFAULT_FLOW_MONITOR_PERIOD_MILLIS
|
||||
val flowMonitorSuspensionLoggingThresholdMillis: Duration get() = DEFAULT_FLOW_MONITOR_SUSPENSION_LOGGING_THRESHOLD_MILLIS
|
||||
val cordappDirectories: List<Path> get() = listOf(baseDirectory / CORDAPPS_DIR_NAME_DEFAULT)
|
||||
|
||||
fun validate(): List<String>
|
||||
|
||||
@ -97,6 +100,8 @@ interface NodeConfiguration : NodeSSLConfiguration {
|
||||
|
||||
val defaultAttachmentContentCacheSize: Long = 10.MB
|
||||
const val defaultAttachmentCacheBound = 1024L
|
||||
|
||||
const val cordappDirectoriesKey = "cordappDirectories"
|
||||
}
|
||||
}
|
||||
|
||||
@ -262,7 +267,8 @@ data class NodeConfigurationImpl(
|
||||
// do not use or remove (used by Capsule)
|
||||
private val jarDirs: List<String> = emptyList(),
|
||||
override val flowMonitorPeriodMillis: Duration = DEFAULT_FLOW_MONITOR_PERIOD_MILLIS,
|
||||
override val flowMonitorSuspensionLoggingThresholdMillis: Duration = DEFAULT_FLOW_MONITOR_SUSPENSION_LOGGING_THRESHOLD_MILLIS
|
||||
override val flowMonitorSuspensionLoggingThresholdMillis: Duration = DEFAULT_FLOW_MONITOR_SUSPENSION_LOGGING_THRESHOLD_MILLIS,
|
||||
override val cordappDirectories: List<Path> = listOf(baseDirectory / CORDAPPS_DIR_NAME_DEFAULT)
|
||||
) : NodeConfiguration {
|
||||
companion object {
|
||||
private val logger = loggerFor<NodeConfigurationImpl>()
|
||||
|
@ -0,0 +1 @@
|
||||
key=value
|
@ -49,6 +49,7 @@ import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.expect
|
||||
import net.corda.testing.core.expectEvents
|
||||
import net.corda.testing.core.sequence
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNetwork.MockNode
|
||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
||||
@ -91,7 +92,7 @@ class CordaRPCOpsImplTest {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset", "net.corda.finance.schemas"))
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.finance.contracts.asset", "net.corda.finance.schemas"))
|
||||
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
|
||||
rpc = aliceNode.rpcOps
|
||||
CURRENT_RPC_CONTEXT.set(RpcAuthContext(InvocationContext.rpc(testActor()), buildSubject("TEST_USER", emptySet())))
|
||||
|
@ -15,6 +15,7 @@ import net.corda.core.node.services.CordaService
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
@ -27,7 +28,7 @@ class NodeUnloadHandlerTests {
|
||||
val shutdownLatch = CountDownLatch(1)
|
||||
}
|
||||
|
||||
private val mockNet = InternalMockNetwork(cordappPackages = listOf(javaClass.packageName), notarySpecs = emptyList())
|
||||
private val mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(javaClass.packageName), notarySpecs = emptyList())
|
||||
|
||||
@After
|
||||
fun cleanUp() {
|
||||
|
@ -85,7 +85,7 @@ class CordappProviderImplTests {
|
||||
fun `test cordapp configuration`() {
|
||||
val configProvider = MockCordappConfigProvider()
|
||||
configProvider.cordappConfigs[isolatedCordappName] = validConfig
|
||||
val loader = CordappLoader.createDevMode(listOf(isolatedJAR))
|
||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR))
|
||||
val provider = CordappProviderImpl(loader, configProvider, attachmentStore, whitelistedContractImplementations)
|
||||
|
||||
val expected = provider.getAppContext(provider.cordapps.first()).config
|
||||
@ -94,7 +94,7 @@ class CordappProviderImplTests {
|
||||
}
|
||||
|
||||
private fun newCordappProvider(vararg urls: URL): CordappProviderImpl {
|
||||
val loader = CordappLoader.createDevMode(urls.toList())
|
||||
val loader = JarScanningCordappLoader.fromJarUrls(urls.toList())
|
||||
return CordappProviderImpl(loader, stubConfigProvider, attachmentStore, whitelistedContractImplementations)
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,13 @@ package net.corda.node.internal.cordapp
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.node.cordapp.CordappLoader
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.getTimestampAsDirectoryName
|
||||
import net.corda.testing.node.internal.packageInDirectory
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
@InitiatingFlow
|
||||
@ -40,7 +45,7 @@ class DummyRPCFlow : FlowLogic<Unit>() {
|
||||
override fun call() = Unit
|
||||
}
|
||||
|
||||
class CordappLoaderTest {
|
||||
class JarScanningCordappLoaderTest {
|
||||
private companion object {
|
||||
const val testScanPackage = "net.corda.node.internal.cordapp"
|
||||
const val isolatedContractId = "net.corda.finance.contracts.isolated.AnotherDummyContract"
|
||||
@ -50,14 +55,14 @@ class CordappLoaderTest {
|
||||
@Test
|
||||
fun `test that classes that aren't in cordapps aren't loaded`() {
|
||||
// Basedir will not be a corda node directory so the dummy flow shouldn't be recognised as a part of a cordapp
|
||||
val loader = CordappLoader.createDefault(Paths.get("."))
|
||||
val loader = JarScanningCordappLoader.fromDirectories(listOf(Paths.get(".")))
|
||||
assertThat(loader.cordapps).containsOnly(loader.coreCordapp)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isolated JAR contains a CorDapp with a contract and plugin`() {
|
||||
val isolatedJAR = CordappLoaderTest::class.java.getResource("isolated.jar")!!
|
||||
val loader = CordappLoader.createDevMode(listOf(isolatedJAR))
|
||||
val isolatedJAR = JarScanningCordappLoaderTest::class.java.getResource("isolated.jar")!!
|
||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR))
|
||||
|
||||
val actual = loader.cordapps.toTypedArray()
|
||||
assertThat(actual).hasSize(2)
|
||||
@ -75,13 +80,11 @@ class CordappLoaderTest {
|
||||
|
||||
@Test
|
||||
fun `flows are loaded by loader`() {
|
||||
val loader = CordappLoader.createWithTestPackages(listOf(testScanPackage))
|
||||
val loader = cordappLoaderForPackages(listOf(testScanPackage))
|
||||
|
||||
val actual = loader.cordapps.toTypedArray()
|
||||
// One core cordapp, one cordapp from this source tree, and two others due to identically named locations
|
||||
// in resources and the non-test part of node. This is okay due to this being test code. In production this
|
||||
// cannot happen. In gradle it will also pick up the node jar.
|
||||
assertThat(actual.size == 4 || actual.size == 5).isTrue()
|
||||
// One core cordapp, one cordapp from this source tree. In gradle it will also pick up the node jar.
|
||||
assertThat(actual.size == 2 || actual.size == 3).isTrue()
|
||||
|
||||
val actualCordapp = actual.single { !it.initiatedFlows.isEmpty() }
|
||||
assertThat(actualCordapp.initiatedFlows).first().hasSameClassAs(DummyFlow::class.java)
|
||||
@ -91,14 +94,14 @@ class CordappLoaderTest {
|
||||
|
||||
@Test
|
||||
fun `duplicate packages are ignored`() {
|
||||
val loader = CordappLoader.createWithTestPackages(listOf(testScanPackage, testScanPackage))
|
||||
val loader = cordappLoaderForPackages(listOf(testScanPackage, testScanPackage))
|
||||
val cordapps = loader.cordapps.filter { LoaderTestFlow::class.java in it.initiatedFlows }
|
||||
assertThat(cordapps).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sub-packages are ignored`() {
|
||||
val loader = CordappLoader.createWithTestPackages(listOf("net.corda", testScanPackage))
|
||||
val loader = cordappLoaderForPackages(listOf("net.corda", testScanPackage))
|
||||
val cordapps = loader.cordapps.filter { LoaderTestFlow::class.java in it.initiatedFlows }
|
||||
assertThat(cordapps).hasSize(1)
|
||||
}
|
||||
@ -107,10 +110,24 @@ class CordappLoaderTest {
|
||||
// being used internally. Later iterations will use a classloader per cordapp and this test can be retired.
|
||||
@Test
|
||||
fun `cordapp classloader can load cordapp classes`() {
|
||||
val isolatedJAR = CordappLoaderTest::class.java.getResource("isolated.jar")!!
|
||||
val loader = CordappLoader.createDevMode(listOf(isolatedJAR))
|
||||
val isolatedJAR = JarScanningCordappLoaderTest::class.java.getResource("isolated.jar")!!
|
||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR))
|
||||
|
||||
loader.appClassLoader.loadClass(isolatedContractId)
|
||||
loader.appClassLoader.loadClass(isolatedFlowName)
|
||||
}
|
||||
|
||||
private fun cordappLoaderForPackages(packages: Iterable<String>): CordappLoader {
|
||||
|
||||
val cordapps = cordappsForPackages(packages)
|
||||
return testDirectory().let { directory ->
|
||||
cordapps.packageInDirectory(directory)
|
||||
JarScanningCordappLoader.fromDirectories(listOf(directory))
|
||||
}
|
||||
}
|
||||
|
||||
private fun testDirectory(): Path {
|
||||
|
||||
return Paths.get("build", getTimestampAsDirectoryName())
|
||||
}
|
||||
}
|
@ -60,6 +60,7 @@ import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.vault.VaultFiller
|
||||
import net.corda.testing.node.InMemoryMessagingNetwork
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
||||
import net.corda.testing.node.internal.pumpReceive
|
||||
@ -89,7 +90,7 @@ import kotlin.test.assertTrue
|
||||
@RunWith(Parameterized::class)
|
||||
class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
companion object {
|
||||
private val cordappPackages = listOf("net.corda.finance.contracts", "net.corda.finance.schemas")
|
||||
private val cordappPackages = setOf("net.corda.finance.contracts")
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "Anonymous = {0}")
|
||||
fun data(): Collection<Boolean> = listOf(true, false)
|
||||
@ -117,7 +118,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
// We run this in parallel threads to help catch any race conditions that may exist. The other tests
|
||||
// we run in the unit test thread exclusively to speed things up, ensure deterministic results and
|
||||
// allow interruption half way through.
|
||||
mockNet = InternalMockNetwork(cordappPackages = cordappPackages, threadPerNode = true)
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(cordappPackages), threadPerNode = true)
|
||||
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
|
||||
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
|
||||
val notaryNode = mockNet.defaultNotaryNode
|
||||
@ -169,7 +170,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
|
||||
@Test(expected = InsufficientBalanceException::class)
|
||||
fun `trade cash for commercial paper fails using soft locking`() {
|
||||
mockNet = InternalMockNetwork(cordappPackages = cordappPackages, threadPerNode = true)
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(cordappPackages), threadPerNode = true)
|
||||
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
|
||||
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
|
||||
val notaryNode = mockNet.defaultNotaryNode
|
||||
@ -227,7 +228,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
|
||||
@Test
|
||||
fun `shutdown and restore`() {
|
||||
mockNet = InternalMockNetwork(cordappPackages = cordappPackages)
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(cordappPackages))
|
||||
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
|
||||
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
|
||||
val notaryNode = mockNet.defaultNotaryNode
|
||||
@ -326,11 +327,20 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
// of gets and puts.
|
||||
private fun makeNodeWithTracking(name: CordaX500Name): StartedNode<InternalMockNetwork.MockNode> {
|
||||
// Create a node in the mock network ...
|
||||
return mockNet.createNode(InternalMockNodeParameters(legalName = name), nodeFactory = { args ->
|
||||
object : InternalMockNetwork.MockNode(args) {
|
||||
// That constructs a recording tx storage
|
||||
override fun makeTransactionStorage(database: CordaPersistence, transactionCacheSizeBytes: Long): WritableTransactionStorage {
|
||||
return RecordingTransactionStorage(database, super.makeTransactionStorage(database, transactionCacheSizeBytes))
|
||||
return mockNet.createNode(InternalMockNodeParameters(legalName = name), nodeFactory = { args, cordappLoader ->
|
||||
if (cordappLoader != null) {
|
||||
object : InternalMockNetwork.MockNode(args, cordappLoader) {
|
||||
// That constructs a recording tx storage
|
||||
override fun makeTransactionStorage(database: CordaPersistence, transactionCacheSizeBytes: Long): WritableTransactionStorage {
|
||||
return RecordingTransactionStorage(database, super.makeTransactionStorage(database, transactionCacheSizeBytes))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
object : InternalMockNetwork.MockNode(args) {
|
||||
// That constructs a recording tx storage
|
||||
override fun makeTransactionStorage(database: CordaPersistence, transactionCacheSizeBytes: Long): WritableTransactionStorage {
|
||||
return RecordingTransactionStorage(database, super.makeTransactionStorage(database, transactionCacheSizeBytes))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -338,7 +348,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
|
||||
@Test
|
||||
fun `check dependencies of sale asset are resolved`() {
|
||||
mockNet = InternalMockNetwork(cordappPackages = cordappPackages)
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(cordappPackages))
|
||||
val notaryNode = mockNet.defaultNotaryNode
|
||||
val aliceNode = makeNodeWithTracking(ALICE_NAME)
|
||||
val bobNode = makeNodeWithTracking(BOB_NAME)
|
||||
@ -442,7 +452,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
|
||||
@Test
|
||||
fun `track works`() {
|
||||
mockNet = InternalMockNetwork(cordappPackages = cordappPackages)
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(cordappPackages))
|
||||
val notaryNode = mockNet.defaultNotaryNode
|
||||
val aliceNode = makeNodeWithTracking(ALICE_NAME)
|
||||
val bobNode = makeNodeWithTracking(BOB_NAME)
|
||||
@ -520,7 +530,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
|
||||
@Test
|
||||
fun `dependency with error on buyer side`() {
|
||||
mockNet = InternalMockNetwork(cordappPackages = cordappPackages)
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(cordappPackages))
|
||||
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
|
||||
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
|
||||
runWithError(ledgerIdentityService, true, false, "at least one cash input")
|
||||
@ -529,7 +539,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
|
||||
@Test
|
||||
fun `dependency with error on seller side`() {
|
||||
mockNet = InternalMockNetwork(cordappPackages = cordappPackages)
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(cordappPackages))
|
||||
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
|
||||
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
|
||||
runWithError(ledgerIdentityService, false, true, "Issuances have a time-window")
|
||||
|
@ -11,11 +11,7 @@
|
||||
package net.corda.node.modes.draining
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.contracts.LinearState
|
||||
import net.corda.core.contracts.SchedulableState
|
||||
import net.corda.core.contracts.ScheduledActivity
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.UniqueIdentifier
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.flows.FinalityFlow
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.FlowLogicRefFactory
|
||||
@ -30,6 +26,7 @@ import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.dummyCommand
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
@ -61,7 +58,7 @@ class ScheduledFlowsDrainingModeTest {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.testing.contracts"), threadPerNode = true)
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts"), threadPerNode = true)
|
||||
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
|
||||
bobNode = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME))
|
||||
notary = mockNet.defaultNotaryIdentity
|
||||
|
@ -16,6 +16,7 @@ import net.corda.node.services.statemachine.StaffedFlowHospital.MedicalRecord.Ke
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
@ -37,10 +38,8 @@ class FinalityHandlerTest {
|
||||
// CorDapp. Bob's FinalityHandler will error when validating the tx.
|
||||
mockNet = InternalMockNetwork()
|
||||
|
||||
val alice = mockNet.createNode(InternalMockNodeParameters(
|
||||
legalName = ALICE_NAME,
|
||||
extraCordappPackages = listOf("net.corda.finance.contracts.asset")
|
||||
))
|
||||
val assertCordapp = TestCorDapp.Factory.create("net.corda.finance.contracts.asset", "1.0").plusPackage("net.corda.finance.contracts.asset")
|
||||
val alice = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME, additionalCordapps = setOf(assertCordapp)))
|
||||
|
||||
var bob = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME))
|
||||
|
||||
|
@ -14,6 +14,7 @@ import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.issuedBy
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
@ -24,7 +25,7 @@ import java.util.concurrent.CountDownLatch
|
||||
|
||||
class ServiceHubConcurrentUsageTest {
|
||||
|
||||
private val mockNet = InternalMockNetwork(listOf(Cash::class.packageName))
|
||||
private val mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(Cash::class.packageName))
|
||||
|
||||
@After
|
||||
fun stopNodes() {
|
||||
|
@ -48,6 +48,7 @@ import net.corda.testing.node.InMemoryMessagingNetwork
|
||||
import net.corda.testing.node.MockNetworkParameters
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
import org.junit.Before
|
||||
import org.junit.ClassRule
|
||||
@ -108,8 +109,8 @@ class TimedFlowTestRule(val clusterSize: Int) : ExternalResource() {
|
||||
|
||||
override fun before() {
|
||||
mockNet = InternalMockNetwork(
|
||||
listOf("net.corda.testing.contracts", "net.corda.node.services"),
|
||||
MockNetworkParameters().withServicePeerAllocationStrategy(InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin()),
|
||||
cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts", "net.corda.node.services"),
|
||||
defaultParameters = MockNetworkParameters().withServicePeerAllocationStrategy(InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin()),
|
||||
threadPerNode = true
|
||||
)
|
||||
val started = startClusterAndNode(mockNet)
|
||||
|
@ -33,6 +33,7 @@ import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.dummyCommand
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNetwork.MockNode
|
||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
||||
@ -111,7 +112,7 @@ class ScheduledFlowTests {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.testing.contracts"), threadPerNode = true)
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts"), threadPerNode = true)
|
||||
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
|
||||
bobNode = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME))
|
||||
notary = mockNet.defaultNotaryIdentity
|
||||
|
@ -25,6 +25,7 @@ import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.driver.internal.InProcessImpl
|
||||
import net.corda.testing.internal.vault.DummyLinearStateSchemaV1
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import org.hibernate.annotations.Cascade
|
||||
import org.hibernate.annotations.CascadeType
|
||||
@ -41,7 +42,7 @@ class NodeSchemaServiceTest {
|
||||
*/
|
||||
@Test
|
||||
fun `registering custom schemas for testing with MockNode`() {
|
||||
val mockNet = InternalMockNetwork(cordappPackages = listOf(DummyLinearStateSchemaV1::class.packageName))
|
||||
val mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(DummyLinearStateSchemaV1::class.packageName))
|
||||
val mockNode = mockNet.createNode()
|
||||
val schemaService = mockNode.services.schemaService
|
||||
assertTrue(schemaService.schemaOptions.containsKey(DummyLinearStateSchemaV1))
|
||||
@ -74,8 +75,6 @@ class NodeSchemaServiceTest {
|
||||
|
||||
/**
|
||||
* Note: this test verifies auto-scanning to register identified [MappedSchema] schemas.
|
||||
* By default, Driver uses the caller package for auto-scanning:
|
||||
* System.setProperty("net.corda.node.cordapp.scan.packages", callerPackage)
|
||||
*/
|
||||
@Test
|
||||
fun `auto scanning of custom schemas for testing with Driver`() {
|
||||
|
@ -78,7 +78,7 @@ class FlowFrameworkTests {
|
||||
@JvmStatic
|
||||
fun beforeClass() {
|
||||
mockNet = InternalMockNetwork(
|
||||
cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts"),
|
||||
cordappsForAllNodes = cordappsForPackages("net.corda.finance.contracts", "net.corda.testing.contracts"),
|
||||
servicePeerAllocationStrategy = RoundRobin()
|
||||
)
|
||||
|
||||
@ -491,7 +491,7 @@ class FlowFrameworkTripartyTests {
|
||||
@JvmStatic
|
||||
fun beforeClass() {
|
||||
mockNet = InternalMockNetwork(
|
||||
cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts"),
|
||||
cordappsForAllNodes = cordappsForPackages("net.corda.finance.contracts", "net.corda.testing.contracts"),
|
||||
servicePeerAllocationStrategy = RoundRobin()
|
||||
)
|
||||
|
||||
@ -655,7 +655,7 @@ class FlowFrameworkPersistenceTests {
|
||||
@Before
|
||||
fun start() {
|
||||
mockNet = InternalMockNetwork(
|
||||
cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts"),
|
||||
cordappsForAllNodes = cordappsForPackages("net.corda.finance.contracts", "net.corda.testing.contracts"),
|
||||
servicePeerAllocationStrategy = RoundRobin()
|
||||
)
|
||||
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
|
||||
|
@ -14,6 +14,7 @@ import net.corda.core.utilities.seconds
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.services.config.FlowTimeoutConfiguration
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
@ -37,7 +38,7 @@ class IdempotentFlowTests {
|
||||
|
||||
@Before
|
||||
fun start() {
|
||||
mockNet = InternalMockNetwork(threadPerNode = true, cordappPackages = listOf(this.javaClass.packageName))
|
||||
mockNet = InternalMockNetwork(threadPerNode = true, cordappsForAllNodes = cordappsForPackages(this.javaClass.packageName))
|
||||
nodeA = mockNet.createNode(InternalMockNodeParameters(
|
||||
legalName = CordaX500Name("Alice", "AliceCorp", "GB"),
|
||||
configOverrides = {
|
||||
|
@ -16,6 +16,7 @@ import net.corda.node.services.FinalityHandler
|
||||
import net.corda.node.services.messaging.Message
|
||||
import net.corda.node.services.persistence.DBTransactionStorage
|
||||
import net.corda.nodeapi.internal.persistence.contextTransaction
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNetwork.MockNode
|
||||
import net.corda.testing.node.internal.MessagingServiceSpy
|
||||
@ -42,7 +43,7 @@ class RetryFlowMockTest {
|
||||
|
||||
@Before
|
||||
fun start() {
|
||||
mockNet = InternalMockNetwork(threadPerNode = true, cordappPackages = listOf(this.javaClass.packageName))
|
||||
mockNet = InternalMockNetwork(threadPerNode = true, cordappsForAllNodes = cordappsForPackages(this.javaClass.packageName))
|
||||
nodeA = mockNet.createNode()
|
||||
nodeB = mockNet.createNode()
|
||||
mockNet.startNodes()
|
||||
|
@ -25,6 +25,7 @@ import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.node.MockNetworkNotarySpec
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
@ -43,7 +44,7 @@ class NotaryServiceTests {
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = InternalMockNetwork(
|
||||
cordappPackages = listOf("net.corda.testing.contracts"),
|
||||
cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts"),
|
||||
notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME, validating = false))
|
||||
)
|
||||
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
|
||||
|
@ -46,6 +46,7 @@ import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.dummyCommand
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.node.TestClock
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.InMemoryMessage
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
||||
@ -72,7 +73,7 @@ class ValidatingNotaryServiceTests {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = InternalMockNetwork(cordappPackages = listOf("net.corda.testing.contracts"))
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts"))
|
||||
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
|
||||
notaryNode = mockNet.defaultNotaryNode
|
||||
notary = mockNet.defaultNotaryIdentity
|
||||
|
@ -39,6 +39,7 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
import org.junit.After
|
||||
@ -90,7 +91,7 @@ class VaultSoftLockManagerTest {
|
||||
private val mockVault = rigorousMock<VaultServiceInternal>().also {
|
||||
doNothing().whenever(it).softLockRelease(any(), anyOrNull())
|
||||
}
|
||||
private val mockNet = InternalMockNetwork(cordappPackages = listOf(ContractImpl::class.packageName), defaultFactory = { args ->
|
||||
private val mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(ContractImpl::class.packageName), defaultFactory = { args, _ ->
|
||||
object : InternalMockNetwork.MockNode(args) {
|
||||
override fun makeVaultService(keyManagementService: KeyManagementService, services: ServicesForResolution, hibernateConfig: HibernateConfiguration, database: CordaPersistence): VaultServiceInternal {
|
||||
val node = this
|
||||
|
@ -21,6 +21,7 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.concurrent.fork
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.internal.packageName
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.node.services.Vault
|
||||
import net.corda.core.node.services.VaultService
|
||||
import net.corda.core.node.services.queryBy
|
||||
|
@ -23,6 +23,7 @@ import net.corda.node.internal.StartedNode
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
@ -40,7 +41,7 @@ class CashIssueAndPaymentFlowTests {
|
||||
@Before
|
||||
fun start() {
|
||||
mockNet = InternalMockNetwork(servicePeerAllocationStrategy = RoundRobin(),
|
||||
cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas"))
|
||||
cordappsForAllNodes = cordappsForPackages("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.singleIdentity()
|
||||
|
@ -22,9 +22,8 @@ import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.StartedMockNode
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
@ -51,7 +50,7 @@ class CashIssueAndPayNoSelectionTests(private val anonymous: Boolean) {
|
||||
@Before
|
||||
fun start() {
|
||||
mockNet = InternalMockNetwork(servicePeerAllocationStrategy = RoundRobin(),
|
||||
cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas"))
|
||||
cordappsForAllNodes = cordappsForPackages("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.singleIdentity()
|
||||
|
@ -23,6 +23,7 @@ import net.corda.node.internal.StartedNode
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
@ -42,7 +43,7 @@ class CashPaymentFlowTests {
|
||||
fun start() {
|
||||
mockNet = InternalMockNetwork(
|
||||
servicePeerAllocationStrategy = RoundRobin(),
|
||||
cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas"))
|
||||
cordappsForAllNodes = cordappsForPackages("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.singleIdentity()
|
||||
|
@ -64,10 +64,7 @@ import net.corda.testing.internal.LogHelper
|
||||
import net.corda.testing.internal.TEST_TX_TIME
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.node.*
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
||||
import net.corda.testing.node.internal.pumpReceive
|
||||
import net.corda.testing.node.internal.startFlow
|
||||
import net.corda.testing.node.internal.*
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
@ -99,8 +96,8 @@ internal fun CheckpointStorage.checkpoints(): List<SerializedBytes<Checkpoint>>
|
||||
@RunWith(Parameterized::class)
|
||||
class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
companion object {
|
||||
private val cordappPackages = listOf(
|
||||
"com.r3.corda.enterprise.perftestcordapp.contracts", "com.r3.corda.enterprise.perftestcordapp.schemas")
|
||||
private val cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts", "com.r3.corda.enterprise.perftestcordapp.schemas")
|
||||
private val cordappsForAllNodes = cordappsForPackages(cordappPackages)
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "Anonymous = {0}")
|
||||
fun data(): Collection<Boolean> = listOf(true, false)
|
||||
@ -128,7 +125,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
// We run this in parallel threads to help catch any race conditions that may exist. The other tests
|
||||
// we run in the unit test thread exclusively to speed things up, ensure deterministic results and
|
||||
// allow interruption half way through.
|
||||
mockNet = InternalMockNetwork(threadPerNode = true, cordappPackages = cordappPackages)
|
||||
mockNet = InternalMockNetwork(threadPerNode = true, cordappsForAllNodes = cordappsForAllNodes)
|
||||
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
|
||||
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
|
||||
val notaryNode = mockNet.defaultNotaryNode
|
||||
@ -181,7 +178,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
|
||||
@Test(expected = InsufficientBalanceException::class)
|
||||
fun `trade cash for commercial paper fails using soft locking`() {
|
||||
mockNet = InternalMockNetwork(threadPerNode = true, cordappPackages = cordappPackages)
|
||||
mockNet = InternalMockNetwork(threadPerNode = true, cordappsForAllNodes = cordappsForAllNodes)
|
||||
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
|
||||
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
|
||||
val notaryNode = mockNet.defaultNotaryNode
|
||||
@ -239,7 +236,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
|
||||
@Test
|
||||
fun `shutdown and restore`() {
|
||||
mockNet = InternalMockNetwork(cordappPackages = cordappPackages)
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForAllNodes)
|
||||
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
|
||||
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
|
||||
val notaryNode = mockNet.defaultNotaryNode
|
||||
@ -340,7 +337,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
private fun makeNodeWithTracking(
|
||||
name: CordaX500Name): StartedNode<InternalMockNetwork.MockNode> {
|
||||
// Create a node in the mock network ...
|
||||
return mockNet.createNode(InternalMockNodeParameters(legalName = name), nodeFactory = { args ->
|
||||
return mockNet.createNode(InternalMockNodeParameters(legalName = name), nodeFactory = { args, _ ->
|
||||
object : InternalMockNetwork.MockNode(args) {
|
||||
// That constructs a recording tx storage
|
||||
override fun makeTransactionStorage(database: CordaPersistence, transactionCacheSizeBytes: Long): WritableTransactionStorage {
|
||||
@ -352,7 +349,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
|
||||
@Test
|
||||
fun `check dependencies of sale asset are resolved`() {
|
||||
mockNet = InternalMockNetwork(cordappPackages = cordappPackages)
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForAllNodes)
|
||||
val notaryNode = mockNet.defaultNotaryNode
|
||||
val aliceNode = makeNodeWithTracking(ALICE_NAME)
|
||||
val bobNode = makeNodeWithTracking(BOB_NAME)
|
||||
@ -456,7 +453,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
|
||||
@Test
|
||||
fun `track works`() {
|
||||
mockNet = InternalMockNetwork(cordappPackages = cordappPackages)
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForAllNodes)
|
||||
val notaryNode = mockNet.defaultNotaryNode
|
||||
val aliceNode = makeNodeWithTracking(ALICE_NAME)
|
||||
val bobNode = makeNodeWithTracking(BOB_NAME)
|
||||
@ -534,7 +531,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
|
||||
@Test
|
||||
fun `dependency with error on buyer side`() {
|
||||
mockNet = InternalMockNetwork(cordappPackages = cordappPackages)
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForAllNodes)
|
||||
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
|
||||
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
|
||||
runWithError(ledgerIdentityService, true, false, "at least one cash input")
|
||||
@ -543,7 +540,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
|
||||
@Test
|
||||
fun `dependency with error on seller side`() {
|
||||
mockNet = InternalMockNetwork(cordappPackages = cordappPackages)
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForAllNodes)
|
||||
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
|
||||
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
|
||||
runWithError(ledgerIdentityService, false, true, "Issuances have a time-window")
|
||||
|
@ -23,7 +23,7 @@ import net.corda.core.node.services.AttachmentStorage
|
||||
import net.corda.core.serialization.*
|
||||
import net.corda.core.utilities.ByteSequence
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||
import net.corda.nodeapi.DummyContractBackdoor
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
@ -67,7 +67,7 @@ class AttachmentsClassLoaderTests {
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
private val attachments = MockAttachmentStorage()
|
||||
private val networkParameters = testNetworkParameters()
|
||||
private val cordappProvider = CordappProviderImpl(CordappLoader.createDevMode(listOf(ISOLATED_CONTRACTS_JAR_PATH)), MockCordappConfigProvider(), attachments, networkParameters.whitelistedContractImplementations)
|
||||
private val cordappProvider = CordappProviderImpl(JarScanningCordappLoader.fromJarUrls(listOf(ISOLATED_CONTRACTS_JAR_PATH)), MockCordappConfigProvider(), attachments, networkParameters.whitelistedContractImplementations)
|
||||
private val cordapp get() = cordappProvider.cordapps.first()
|
||||
private val attachmentId get() = cordappProvider.getCordappAttachmentId(cordapp)!!
|
||||
private val appContext get() = cordappProvider.getAppContext(cordapp)
|
||||
|
@ -86,3 +86,15 @@ include 'cordform-common'
|
||||
include 'hsm-tool'
|
||||
include 'launcher'
|
||||
include 'node:dist'
|
||||
|
||||
buildCache {
|
||||
local {
|
||||
enabled = false
|
||||
}
|
||||
remote(HttpBuildCache) {
|
||||
// url = 'http://localhost:5071/cache/'
|
||||
// CI server: gradle-build-cache
|
||||
url = 'http://40.114.193.246:80/cache/'
|
||||
push = true
|
||||
}
|
||||
}
|
||||
|
@ -30,10 +30,7 @@ import net.corda.testing.driver.PortAllocation.Incremental
|
||||
import net.corda.testing.driver.internal.internalServices
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.node.User
|
||||
import net.corda.testing.node.internal.DriverDSLImpl
|
||||
import net.corda.testing.node.internal.genericDriver
|
||||
import net.corda.testing.node.internal.getTimestampAsDirectoryName
|
||||
import net.corda.testing.node.internal.newContext
|
||||
import net.corda.testing.node.internal.*
|
||||
import rx.Observable
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
@ -146,6 +143,9 @@ abstract class PortAllocation {
|
||||
* @property startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
||||
* in. If null the Driver-level value will be used.
|
||||
* @property maximumHeapSize The maximum JVM heap size to use for the node.
|
||||
* @property logLevel Logging level threshold.
|
||||
* @property additionalCordapps Additional [TestCorDapp]s that this node will have available, in addition to the ones common to all nodes managed by the [DriverDSL].
|
||||
* @property regenerateCordappsOnStart Whether existing [TestCorDapp]s unique to this node will be re-generated on start. Useful when stopping and restarting the same node.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
data class NodeParameters(
|
||||
@ -155,8 +155,57 @@ data class NodeParameters(
|
||||
val customOverrides: Map<String, Any?> = emptyMap(),
|
||||
val startInSameProcess: Boolean? = null,
|
||||
val maximumHeapSize: String = "512m",
|
||||
val logLevel: String? = null
|
||||
val logLevel: String? = null,
|
||||
val additionalCordapps: Set<TestCorDapp> = emptySet(),
|
||||
val regenerateCordappsOnStart: Boolean = false
|
||||
) {
|
||||
/**
|
||||
* Helper builder for configuring a [Node] from Java.
|
||||
*
|
||||
* @param providedName Optional name of the node, which will be its legal name in [Party]. Defaults to something
|
||||
* random. Note that this must be unique as the driver uses it as a primary key!
|
||||
* @param rpcUsers List of users who are authorised to use the RPC system. Defaults to a single user with
|
||||
* all permissions.
|
||||
* @param verifierType The type of transaction verifier to use. See: [VerifierType]
|
||||
* @param customOverrides A map of custom node configuration overrides.
|
||||
* @param startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
||||
* in. If null the Driver-level value will be used.
|
||||
* @param maximumHeapSize The maximum JVM heap size to use for the node.
|
||||
* @param logLevel Logging level threshold.
|
||||
*/
|
||||
constructor(
|
||||
providedName: CordaX500Name?,
|
||||
rpcUsers: List<User>,
|
||||
verifierType: VerifierType,
|
||||
customOverrides: Map<String, Any?>,
|
||||
startInSameProcess: Boolean?,
|
||||
maximumHeapSize: String,
|
||||
logLevel: String? = null
|
||||
) : this(
|
||||
providedName,
|
||||
rpcUsers,
|
||||
verifierType,
|
||||
customOverrides,
|
||||
startInSameProcess,
|
||||
maximumHeapSize,
|
||||
logLevel,
|
||||
additionalCordapps = emptySet(),
|
||||
regenerateCordappsOnStart = false
|
||||
)
|
||||
|
||||
/**
|
||||
* Helper builder for configuring a [Node] from Java.
|
||||
*
|
||||
* @param providedName Optional name of the node, which will be its legal name in [Party]. Defaults to something
|
||||
* random. Note that this must be unique as the driver uses it as a primary key!
|
||||
* @param rpcUsers List of users who are authorised to use the RPC system. Defaults to a single user with
|
||||
* all permissions.
|
||||
* @param verifierType The type of transaction verifier to use. See: [VerifierType]
|
||||
* @param customOverrides A map of custom node configuration overrides.
|
||||
* @param startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
||||
* in. If null the Driver-level value will be used.
|
||||
* @param maximumHeapSize The maximum JVM heap size to use for the node.
|
||||
*/
|
||||
constructor(
|
||||
providedName: CordaX500Name?,
|
||||
rpcUsers: List<User>,
|
||||
@ -171,7 +220,44 @@ data class NodeParameters(
|
||||
customOverrides,
|
||||
startInSameProcess,
|
||||
maximumHeapSize,
|
||||
null)
|
||||
null,
|
||||
additionalCordapps = emptySet(),
|
||||
regenerateCordappsOnStart = false)
|
||||
|
||||
/**
|
||||
* Helper builder for configuring a [Node] from Java.
|
||||
*
|
||||
* @param providedName Optional name of the node, which will be its legal name in [Party]. Defaults to something
|
||||
* random. Note that this must be unique as the driver uses it as a primary key!
|
||||
* @param rpcUsers List of users who are authorised to use the RPC system. Defaults to a single user with
|
||||
* all permissions.
|
||||
* @param verifierType The type of transaction verifier to use. See: [VerifierType]
|
||||
* @param customOverrides A map of custom node configuration overrides.
|
||||
* @param startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
||||
* in. If null the Driver-level value will be used.
|
||||
* @param maximumHeapSize The maximum JVM heap size to use for the node.
|
||||
* @param additionalCordapps Additional [TestCorDapp]s that this node will have available, in addition to the ones common to all nodes managed by the [DriverDSL].
|
||||
* @param regenerateCordappsOnStart Whether existing [TestCorDapp]s unique to this node will be re-generated on start. Useful when stopping and restarting the same node.
|
||||
*/
|
||||
constructor(
|
||||
providedName: CordaX500Name?,
|
||||
rpcUsers: List<User>,
|
||||
verifierType: VerifierType,
|
||||
customOverrides: Map<String, Any?>,
|
||||
startInSameProcess: Boolean?,
|
||||
maximumHeapSize: String,
|
||||
additionalCordapps: Set<TestCorDapp> = emptySet(),
|
||||
regenerateCordappsOnStart: Boolean = false
|
||||
) : this(
|
||||
providedName,
|
||||
rpcUsers,
|
||||
verifierType,
|
||||
customOverrides,
|
||||
startInSameProcess,
|
||||
maximumHeapSize,
|
||||
null,
|
||||
additionalCordapps,
|
||||
regenerateCordappsOnStart)
|
||||
|
||||
fun copy(
|
||||
providedName: CordaX500Name?,
|
||||
@ -189,6 +275,25 @@ data class NodeParameters(
|
||||
maximumHeapSize,
|
||||
null)
|
||||
|
||||
fun copy(
|
||||
providedName: CordaX500Name?,
|
||||
rpcUsers: List<User>,
|
||||
verifierType: VerifierType,
|
||||
customOverrides: Map<String, Any?>,
|
||||
startInSameProcess: Boolean?,
|
||||
maximumHeapSize: String,
|
||||
logLevel: String?
|
||||
) = this.copy(
|
||||
providedName,
|
||||
rpcUsers,
|
||||
verifierType,
|
||||
customOverrides,
|
||||
startInSameProcess,
|
||||
maximumHeapSize,
|
||||
logLevel,
|
||||
additionalCordapps = additionalCordapps,
|
||||
regenerateCordappsOnStart = regenerateCordappsOnStart)
|
||||
|
||||
fun withProvidedName(providedName: CordaX500Name?): NodeParameters = copy(providedName = providedName)
|
||||
fun withRpcUsers(rpcUsers: List<User>): NodeParameters = copy(rpcUsers = rpcUsers)
|
||||
fun withVerifierType(verifierType: VerifierType): NodeParameters = copy(verifierType = verifierType)
|
||||
@ -196,6 +301,8 @@ data class NodeParameters(
|
||||
fun withStartInSameProcess(startInSameProcess: Boolean?): NodeParameters = copy(startInSameProcess = startInSameProcess)
|
||||
fun withMaximumHeapSize(maximumHeapSize: String): NodeParameters = copy(maximumHeapSize = maximumHeapSize)
|
||||
fun withLogLevel(logLevel: String?): NodeParameters = copy(logLevel = logLevel)
|
||||
fun withAdditionalCordapps(additionalCordapps: Set<TestCorDapp>): NodeParameters = copy(additionalCordapps = additionalCordapps)
|
||||
fun withDeleteExistingCordappsDirectory(regenerateCordappsOnStart: Boolean): NodeParameters = copy(regenerateCordappsOnStart = regenerateCordappsOnStart)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -240,12 +347,12 @@ fun <A> driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr
|
||||
startNodesInProcess = defaultParameters.startNodesInProcess,
|
||||
waitForAllNodesToFinish = defaultParameters.waitForAllNodesToFinish,
|
||||
notarySpecs = defaultParameters.notarySpecs,
|
||||
extraCordappPackagesToScan = defaultParameters.extraCordappPackagesToScan,
|
||||
jmxPolicy = defaultParameters.jmxPolicy,
|
||||
compatibilityZone = null,
|
||||
networkParameters = defaultParameters.networkParameters,
|
||||
notaryCustomOverrides = defaultParameters.notaryCustomOverrides,
|
||||
inMemoryDB = defaultParameters.inMemoryDB
|
||||
inMemoryDB = defaultParameters.inMemoryDB,
|
||||
cordappsForAllNodes = defaultParameters.cordappsForAllNodes()
|
||||
),
|
||||
coerce = { it },
|
||||
dsl = dsl,
|
||||
@ -282,6 +389,7 @@ fun <A> driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr
|
||||
* @property inMemoryDB Whether to use in-memory H2 for new nodes rather then on-disk (the node starts quicker, however
|
||||
* the data is not persisted between node restarts). Has no effect if node is configured
|
||||
* in any way to use database other than H2.
|
||||
* @property cordappsForAllNodes [TestCorDapp]s that will be added to each node started by the [DriverDSL].
|
||||
*/
|
||||
@Suppress("unused")
|
||||
data class DriverParameters(
|
||||
@ -299,8 +407,44 @@ data class DriverParameters(
|
||||
val networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()),
|
||||
val notaryCustomOverrides: Map<String, Any?> = emptyMap(),
|
||||
val initialiseSerialization: Boolean = true,
|
||||
val inMemoryDB: Boolean = true
|
||||
val inMemoryDB: Boolean = true,
|
||||
val cordappsForAllNodes: Set<TestCorDapp>? = null
|
||||
) {
|
||||
constructor(
|
||||
isDebug: Boolean = false,
|
||||
driverDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()),
|
||||
portAllocation: PortAllocation = PortAllocation.Incremental(10000),
|
||||
debugPortAllocation: PortAllocation = PortAllocation.Incremental(5005),
|
||||
systemProperties: Map<String, String> = emptyMap(),
|
||||
useTestClock: Boolean = false,
|
||||
startNodesInProcess: Boolean = false,
|
||||
waitForAllNodesToFinish: Boolean = false,
|
||||
notarySpecs: List<NotarySpec> = listOf(NotarySpec(DUMMY_NOTARY_NAME)),
|
||||
extraCordappPackagesToScan: List<String> = emptyList(),
|
||||
jmxPolicy: JmxPolicy = JmxPolicy(),
|
||||
networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()),
|
||||
notaryCustomOverrides: Map<String, Any?> = emptyMap(),
|
||||
initialiseSerialization: Boolean = true,
|
||||
inMemoryDB: Boolean = true
|
||||
) : this(
|
||||
isDebug,
|
||||
driverDirectory,
|
||||
portAllocation,
|
||||
debugPortAllocation,
|
||||
systemProperties,
|
||||
useTestClock,
|
||||
startNodesInProcess,
|
||||
waitForAllNodesToFinish,
|
||||
notarySpecs,
|
||||
extraCordappPackagesToScan,
|
||||
jmxPolicy,
|
||||
networkParameters,
|
||||
notaryCustomOverrides,
|
||||
initialiseSerialization,
|
||||
inMemoryDB,
|
||||
cordappsForAllNodes = null
|
||||
)
|
||||
|
||||
constructor(
|
||||
isDebug: Boolean,
|
||||
driverDirectory: Path,
|
||||
@ -329,7 +473,41 @@ data class DriverParameters(
|
||||
networkParameters,
|
||||
emptyMap(),
|
||||
true,
|
||||
true
|
||||
true,
|
||||
cordappsForAllNodes = null
|
||||
)
|
||||
|
||||
constructor(
|
||||
isDebug: Boolean,
|
||||
driverDirectory: Path,
|
||||
portAllocation: PortAllocation,
|
||||
debugPortAllocation: PortAllocation,
|
||||
systemProperties: Map<String, String>,
|
||||
useTestClock: Boolean,
|
||||
startNodesInProcess: Boolean,
|
||||
waitForAllNodesToFinish: Boolean,
|
||||
notarySpecs: List<NotarySpec>,
|
||||
extraCordappPackagesToScan: List<String>,
|
||||
jmxPolicy: JmxPolicy,
|
||||
networkParameters: NetworkParameters,
|
||||
cordappsForAllNodes: Set<TestCorDapp>? = null
|
||||
) : this(
|
||||
isDebug,
|
||||
driverDirectory,
|
||||
portAllocation,
|
||||
debugPortAllocation,
|
||||
systemProperties,
|
||||
useTestClock,
|
||||
startNodesInProcess,
|
||||
waitForAllNodesToFinish,
|
||||
notarySpecs,
|
||||
extraCordappPackagesToScan,
|
||||
jmxPolicy,
|
||||
networkParameters,
|
||||
emptyMap(),
|
||||
true,
|
||||
true,
|
||||
cordappsForAllNodes
|
||||
)
|
||||
|
||||
constructor(
|
||||
@ -362,7 +540,43 @@ data class DriverParameters(
|
||||
networkParameters,
|
||||
emptyMap(),
|
||||
initialiseSerialization,
|
||||
inMemoryDB
|
||||
inMemoryDB,
|
||||
cordappsForAllNodes = null
|
||||
)
|
||||
|
||||
constructor(
|
||||
isDebug: Boolean,
|
||||
driverDirectory: Path,
|
||||
portAllocation: PortAllocation,
|
||||
debugPortAllocation: PortAllocation,
|
||||
systemProperties: Map<String, String>,
|
||||
useTestClock: Boolean,
|
||||
startNodesInProcess: Boolean,
|
||||
waitForAllNodesToFinish: Boolean,
|
||||
notarySpecs: List<NotarySpec>,
|
||||
extraCordappPackagesToScan: List<String>,
|
||||
jmxPolicy: JmxPolicy,
|
||||
networkParameters: NetworkParameters,
|
||||
initialiseSerialization: Boolean,
|
||||
inMemoryDB: Boolean,
|
||||
cordappsForAllNodes: Set<TestCorDapp>? = null
|
||||
) : this(
|
||||
isDebug,
|
||||
driverDirectory,
|
||||
portAllocation,
|
||||
debugPortAllocation,
|
||||
systemProperties,
|
||||
useTestClock,
|
||||
startNodesInProcess,
|
||||
waitForAllNodesToFinish,
|
||||
notarySpecs,
|
||||
extraCordappPackagesToScan,
|
||||
jmxPolicy,
|
||||
networkParameters,
|
||||
emptyMap(),
|
||||
initialiseSerialization,
|
||||
inMemoryDB,
|
||||
cordappsForAllNodes
|
||||
)
|
||||
|
||||
fun withIsDebug(isDebug: Boolean): DriverParameters = copy(isDebug = isDebug)
|
||||
@ -380,6 +594,7 @@ data class DriverParameters(
|
||||
fun withNetworkParameters(networkParameters: NetworkParameters): DriverParameters = copy(networkParameters = networkParameters)
|
||||
fun withNotaryCustomOverrides(notaryCustomOverrides: Map<String, Any?>): DriverParameters = copy(notaryCustomOverrides = notaryCustomOverrides)
|
||||
fun withInMemoryDB(inMemoryDB: Boolean): DriverParameters = copy(inMemoryDB = inMemoryDB)
|
||||
fun withCordappsForAllNodes(cordappsForAllNodes: Set<TestCorDapp>?): DriverParameters = copy(cordappsForAllNodes = cordappsForAllNodes)
|
||||
|
||||
fun copy(
|
||||
isDebug: Boolean,
|
||||
@ -441,4 +656,69 @@ data class DriverParameters(
|
||||
notaryCustomOverrides = emptyMap(),
|
||||
initialiseSerialization = initialiseSerialization
|
||||
)
|
||||
|
||||
fun copy(
|
||||
isDebug: Boolean,
|
||||
driverDirectory: Path,
|
||||
portAllocation: PortAllocation,
|
||||
debugPortAllocation: PortAllocation,
|
||||
systemProperties: Map<String, String>,
|
||||
useTestClock: Boolean,
|
||||
startNodesInProcess: Boolean,
|
||||
waitForAllNodesToFinish: Boolean,
|
||||
notarySpecs: List<NotarySpec>,
|
||||
extraCordappPackagesToScan: List<String>,
|
||||
jmxPolicy: JmxPolicy,
|
||||
networkParameters: NetworkParameters,
|
||||
cordappsForAllNodes: Set<TestCorDapp>?
|
||||
) = this.copy(
|
||||
isDebug = isDebug,
|
||||
driverDirectory = driverDirectory,
|
||||
portAllocation = portAllocation,
|
||||
debugPortAllocation = debugPortAllocation,
|
||||
systemProperties = systemProperties,
|
||||
useTestClock = useTestClock,
|
||||
startNodesInProcess = startNodesInProcess,
|
||||
waitForAllNodesToFinish = waitForAllNodesToFinish,
|
||||
notarySpecs = notarySpecs,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
jmxPolicy = jmxPolicy,
|
||||
networkParameters = networkParameters,
|
||||
notaryCustomOverrides = emptyMap(),
|
||||
initialiseSerialization = true,
|
||||
cordappsForAllNodes = cordappsForAllNodes
|
||||
)
|
||||
|
||||
fun copy(
|
||||
isDebug: Boolean,
|
||||
driverDirectory: Path,
|
||||
portAllocation: PortAllocation,
|
||||
debugPortAllocation: PortAllocation,
|
||||
systemProperties: Map<String, String>,
|
||||
useTestClock: Boolean,
|
||||
startNodesInProcess: Boolean,
|
||||
waitForAllNodesToFinish: Boolean,
|
||||
notarySpecs: List<NotarySpec>,
|
||||
extraCordappPackagesToScan: List<String>,
|
||||
jmxPolicy: JmxPolicy,
|
||||
networkParameters: NetworkParameters,
|
||||
initialiseSerialization: Boolean,
|
||||
cordappsForAllNodes: Set<TestCorDapp>?
|
||||
) = this.copy(
|
||||
isDebug = isDebug,
|
||||
driverDirectory = driverDirectory,
|
||||
portAllocation = portAllocation,
|
||||
debugPortAllocation = debugPortAllocation,
|
||||
systemProperties = systemProperties,
|
||||
useTestClock = useTestClock,
|
||||
startNodesInProcess = startNodesInProcess,
|
||||
waitForAllNodesToFinish = waitForAllNodesToFinish,
|
||||
notarySpecs = notarySpecs,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
jmxPolicy = jmxPolicy,
|
||||
networkParameters = networkParameters,
|
||||
notaryCustomOverrides = emptyMap(),
|
||||
initialiseSerialization = initialiseSerialization,
|
||||
cordappsForAllNodes = cordappsForAllNodes
|
||||
)
|
||||
}
|
||||
|
@ -90,6 +90,38 @@ interface DriverDSL {
|
||||
maximumHeapSize: String = defaultParameters.maximumHeapSize
|
||||
): CordaFuture<NodeHandle>
|
||||
|
||||
/**
|
||||
* Start a node.
|
||||
*
|
||||
* @param defaultParameters The default parameters for the node. Allows the node to be configured in builder style
|
||||
* when called from Java code.
|
||||
* @param providedName Optional name of the node, which will be its legal name in [Party]. Defaults to something
|
||||
* random. Note that this must be unique as the driver uses it as a primary key!
|
||||
* @param rpcUsers List of users who are authorised to use the RPC system. Defaults to empty list.
|
||||
* @param verifierType The type of transaction verifier to use. See: [VerifierType].
|
||||
* @param customOverrides A map of custom node configuration overrides.
|
||||
* @param startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
||||
* in. If null the Driver-level value will be used.
|
||||
* @param maximumHeapSize The maximum JVM heap size to use for the node as a [String]. By default a number is interpreted
|
||||
* as being in bytes. Append the letter 'k' or 'K' to the value to indicate Kilobytes, 'm' or 'M' to indicate
|
||||
* megabytes, and 'g' or 'G' to indicate gigabytes. The default value is "512m" = 512 megabytes.
|
||||
* @param additionalCordapps Additional [TestCorDapp]s that this node will have available, in addition to the ones common to all nodes managed by the [DriverDSL].
|
||||
* @param regenerateCordappsOnStart Whether existing [TestCorDapp]s unique to this node will be re-generated on start. Useful when stopping and restarting the same node.
|
||||
* @return A [CordaFuture] on the [NodeHandle] to the node. The future will complete when the node is available and
|
||||
* it sees all previously started nodes, including the notaries.
|
||||
*/
|
||||
fun startNode(
|
||||
defaultParameters: NodeParameters = NodeParameters(),
|
||||
providedName: CordaX500Name? = defaultParameters.providedName,
|
||||
rpcUsers: List<User> = defaultParameters.rpcUsers,
|
||||
verifierType: VerifierType = defaultParameters.verifierType,
|
||||
customOverrides: Map<String, Any?> = defaultParameters.customOverrides,
|
||||
startInSameProcess: Boolean? = defaultParameters.startInSameProcess,
|
||||
maximumHeapSize: String = defaultParameters.maximumHeapSize,
|
||||
additionalCordapps: Set<TestCorDapp> = defaultParameters.additionalCordapps,
|
||||
regenerateCordappsOnStart: Boolean = defaultParameters.regenerateCordappsOnStart
|
||||
): CordaFuture<NodeHandle>
|
||||
|
||||
/**
|
||||
* Helper function for starting a [Node] with custom parameters from Java.
|
||||
*
|
||||
|
@ -0,0 +1,85 @@
|
||||
package net.corda.testing.driver
|
||||
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.testing.node.internal.MutableTestCorDapp
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
|
||||
/**
|
||||
* Represents information about a CorDapp. Used to generate CorDapp JARs in tests.
|
||||
*/
|
||||
@DoNotImplement
|
||||
interface TestCorDapp {
|
||||
|
||||
val name: String
|
||||
val title: String
|
||||
val version: String
|
||||
val vendor: String
|
||||
|
||||
val classes: Set<Class<*>>
|
||||
|
||||
val resources: Set<URL>
|
||||
|
||||
fun packageAsJarInDirectory(parentDirectory: Path): Path
|
||||
|
||||
fun packageAsJarWithPath(jarFilePath: Path)
|
||||
|
||||
/**
|
||||
* Responsible of creating [TestCorDapp]s.
|
||||
*/
|
||||
class Factory {
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Returns a builder-style [TestCorDapp] to easily generate different [TestCorDapp]s that have something in common.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun create(name: String, version: String, vendor: String = "R3", title: String = name, classes: Set<Class<*>> = emptySet(), willResourceBeAddedBeToCorDapp: (fullyQualifiedName: String, url: URL) -> Boolean = MutableTestCorDapp.Companion::filterTestCorDappClass): TestCorDapp.Mutable {
|
||||
|
||||
return MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedBeToCorDapp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DoNotImplement
|
||||
interface Mutable : TestCorDapp {
|
||||
|
||||
fun withName(name: String): TestCorDapp.Mutable
|
||||
|
||||
fun withTitle(title: String): TestCorDapp.Mutable
|
||||
|
||||
fun withVersion(version: String): TestCorDapp.Mutable
|
||||
|
||||
fun withVendor(vendor: String): TestCorDapp.Mutable
|
||||
|
||||
fun withClasses(classes: Set<Class<*>>): TestCorDapp.Mutable
|
||||
|
||||
fun plusPackages(pckgs: Set<String>): TestCorDapp.Mutable
|
||||
|
||||
fun minusPackages(pckgs: Set<String>): TestCorDapp.Mutable
|
||||
|
||||
fun plusPackage(pckg: String): TestCorDapp.Mutable = plusPackages(setOf(pckg))
|
||||
|
||||
fun minusPackage(pckg: String): TestCorDapp.Mutable = minusPackages(setOf(pckg))
|
||||
|
||||
fun plusPackage(pckg: Package): TestCorDapp.Mutable = plusPackages(pckg.name)
|
||||
|
||||
fun minusPackage(pckg: Package): TestCorDapp.Mutable = minusPackages(pckg.name)
|
||||
|
||||
operator fun plus(clazz: Class<*>): TestCorDapp.Mutable = withClasses(classes + clazz)
|
||||
|
||||
operator fun minus(clazz: Class<*>): TestCorDapp.Mutable = withClasses(classes - clazz)
|
||||
|
||||
fun plusPackages(pckg: String, vararg pckgs: String): TestCorDapp.Mutable = plusPackages(setOf(pckg, *pckgs))
|
||||
|
||||
fun plusPackages(pckg: Package, vararg pckgs: Package): TestCorDapp.Mutable = minusPackages(setOf(pckg, *pckgs).map { it.name }.toSet())
|
||||
|
||||
fun minusPackages(pckg: String, vararg pckgs: String): TestCorDapp.Mutable = minusPackages(setOf(pckg, *pckgs))
|
||||
|
||||
fun minusPackages(pckg: Package, vararg pckgs: Package): TestCorDapp.Mutable = minusPackages(setOf(pckg, *pckgs).map { it.name }.toSet())
|
||||
|
||||
fun plusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable
|
||||
|
||||
fun minusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable
|
||||
}
|
||||
}
|
@ -24,10 +24,8 @@ import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.node.internal.InternalMockMessagingService
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
||||
import net.corda.testing.node.internal.newContext
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import net.corda.testing.node.internal.*
|
||||
import rx.Observable
|
||||
import java.math.BigInteger
|
||||
import java.nio.file.Path
|
||||
@ -43,22 +41,36 @@ import java.nio.file.Path
|
||||
* @property entropyRoot the initial entropy value to use when generating keys. Defaults to an (insecure) random value,
|
||||
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
|
||||
* @property configOverrides Add/override behaviour of the [NodeConfiguration] mock object.
|
||||
* @property extraCordappPackages Extra CorDapp packages to include for this node.
|
||||
* @property additionalCordapps [TestCorDapp]s that will be added to this node in addition to the ones shared by all nodes, which get specified at [MockNetwork] level.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
data class MockNodeParameters @JvmOverloads constructor(
|
||||
data class MockNodeParameters constructor(
|
||||
val forcedID: Int? = null,
|
||||
val legalName: CordaX500Name? = null,
|
||||
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||
val configOverrides: (NodeConfiguration) -> Any? = {},
|
||||
val extraCordappPackages: List<String> = emptyList()) {
|
||||
val additionalCordapps: Set<TestCorDapp>) {
|
||||
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
forcedID: Int? = null,
|
||||
legalName: CordaX500Name? = null,
|
||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||
configOverrides: (NodeConfiguration) -> Any? = {},
|
||||
extraCordappPackages: List<String> = emptyList()
|
||||
) : this(forcedID, legalName, entropyRoot, configOverrides, additionalCordapps = cordappsForPackages(extraCordappPackages))
|
||||
|
||||
fun withForcedID(forcedID: Int?): MockNodeParameters = copy(forcedID = forcedID)
|
||||
fun withLegalName(legalName: CordaX500Name?): MockNodeParameters = copy(legalName = legalName)
|
||||
fun withEntropyRoot(entropyRoot: BigInteger): MockNodeParameters = copy(entropyRoot = entropyRoot)
|
||||
fun withConfigOverrides(configOverrides: (NodeConfiguration) -> Any?): MockNodeParameters = copy(configOverrides = configOverrides)
|
||||
fun withExtraCordappPackages(extraCordappPackages: List<String>): MockNodeParameters = copy(extraCordappPackages = extraCordappPackages)
|
||||
fun withExtraCordappPackages(extraCordappPackages: List<String>): MockNodeParameters = copy(forcedID = forcedID, legalName = legalName, entropyRoot = entropyRoot, configOverrides = configOverrides, extraCordappPackages = extraCordappPackages)
|
||||
fun withAdditionalCordapps(additionalCordapps: Set<TestCorDapp>): MockNodeParameters = copy(additionalCordapps = additionalCordapps)
|
||||
fun copy(forcedID: Int?, legalName: CordaX500Name?, entropyRoot: BigInteger, configOverrides: (NodeConfiguration) -> Any?): MockNodeParameters {
|
||||
return MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides)
|
||||
return MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, additionalCordapps = emptySet())
|
||||
}
|
||||
fun copy(forcedID: Int?, legalName: CordaX500Name?, entropyRoot: BigInteger, configOverrides: (NodeConfiguration) -> Any?, extraCordappPackages: List<String> = emptyList()): MockNodeParameters {
|
||||
return MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, extraCordappPackages)
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,6 +227,7 @@ class StartedMockNode private constructor(private val node: StartedNode<Internal
|
||||
* @property notarySpecs The notaries to use in the mock network. By default you get one mock notary and that is usually sufficient.
|
||||
* @property networkParameters The network parameters to be used by all the nodes. [NetworkParameters.notaries] must be
|
||||
* empty as notaries are defined by [notarySpecs].
|
||||
* @property cordappsForAllNodes [TestCorDapp]s that will be added to each node started by the [MockNetwork].
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate", "CanBeParameter")
|
||||
open class MockNetwork(
|
||||
@ -224,11 +237,23 @@ open class MockNetwork(
|
||||
val threadPerNode: Boolean = defaultParameters.threadPerNode,
|
||||
val servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy,
|
||||
val notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs,
|
||||
val networkParameters: NetworkParameters = defaultParameters.networkParameters) {
|
||||
val networkParameters: NetworkParameters = defaultParameters.networkParameters,
|
||||
val cordappsForAllNodes: Set<TestCorDapp> = cordappsForPackages(cordappPackages)) {
|
||||
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: List<String>, parameters: MockNetworkParameters = MockNetworkParameters()) : this(cordappPackages, defaultParameters = parameters)
|
||||
|
||||
private val internalMockNetwork: InternalMockNetwork = InternalMockNetwork(cordappPackages, defaultParameters, networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, notarySpecs, networkParameters)
|
||||
constructor(
|
||||
cordappPackages: List<String>,
|
||||
defaultParameters: MockNetworkParameters = MockNetworkParameters(),
|
||||
networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped,
|
||||
threadPerNode: Boolean = defaultParameters.threadPerNode,
|
||||
servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy,
|
||||
notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs,
|
||||
networkParameters: NetworkParameters = defaultParameters.networkParameters
|
||||
) : this(emptyList(), defaultParameters, networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, notarySpecs, networkParameters, cordappsForAllNodes = cordappsForPackages(cordappPackages))
|
||||
|
||||
private val internalMockNetwork: InternalMockNetwork = InternalMockNetwork(defaultParameters, networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, notarySpecs, networkParameters = networkParameters, cordappsForAllNodes = cordappsForAllNodes)
|
||||
|
||||
/** In a mock network, nodes have an incrementing integer ID. Real networks do not have this. Returns the next ID that will be used. */
|
||||
val nextNodeId get(): Int = internalMockNetwork.nextNodeId
|
||||
@ -272,7 +297,26 @@ open class MockNetwork(
|
||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||
configOverrides: (NodeConfiguration) -> Any? = {},
|
||||
extraCordappPackages: List<String> = emptyList()): StartedMockNode {
|
||||
val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, extraCordappPackages)
|
||||
|
||||
return createNode(legalName, forcedID, entropyRoot, configOverrides, cordappsForPackages(extraCordappPackages))
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a started node with the given parameters.
|
||||
*
|
||||
* @param legalName The node's legal name.
|
||||
* @param forcedID A unique identifier for the node.
|
||||
* @param entropyRoot The initial entropy value to use when generating keys. Defaults to an (insecure) random value,
|
||||
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
|
||||
* @param configOverrides Add/override behaviour of the [NodeConfiguration] mock object.
|
||||
* @param additionalCordapps Additional [TestCorDapp]s that this node will have available, in addition to the ones common to all nodes managed by the [MockNetwork].
|
||||
*/
|
||||
fun createNode(legalName: CordaX500Name? = null,
|
||||
forcedID: Int? = null,
|
||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||
configOverrides: (NodeConfiguration) -> Any? = {},
|
||||
additionalCordapps: Set<TestCorDapp>): StartedMockNode {
|
||||
val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, additionalCordapps)
|
||||
return StartedMockNode.create(internalMockNetwork.createNode(InternalMockNodeParameters(parameters)))
|
||||
}
|
||||
|
||||
@ -295,7 +339,26 @@ open class MockNetwork(
|
||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||
configOverrides: (NodeConfiguration) -> Any? = {},
|
||||
extraCordappPackages: List<String> = emptyList()): UnstartedMockNode {
|
||||
val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, extraCordappPackages)
|
||||
|
||||
return createUnstartedNode(legalName, forcedID, entropyRoot, configOverrides, cordappsForPackages(extraCordappPackages))
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an unstarted node with the given parameters.
|
||||
*
|
||||
* @param legalName The node's legal name.
|
||||
* @param forcedID A unique identifier for the node.
|
||||
* @param entropyRoot The initial entropy value to use when generating keys. Defaults to an (insecure) random value,
|
||||
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
|
||||
* @param configOverrides Add/override behaviour of the [NodeConfiguration] mock object.
|
||||
* @param additionalCordapps Additional [TestCorDapp]s that this node will have available, in addition to the ones common to all nodes managed by the [MockNetwork].
|
||||
*/
|
||||
fun createUnstartedNode(legalName: CordaX500Name? = null,
|
||||
forcedID: Int? = null,
|
||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||
configOverrides: (NodeConfiguration) -> Any? = {},
|
||||
additionalCordapps: Set<TestCorDapp> = emptySet()): UnstartedMockNode {
|
||||
val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, additionalCordapps)
|
||||
return UnstartedMockNode.create(internalMockNetwork.createUnstartedNode(InternalMockNodeParameters(parameters)))
|
||||
}
|
||||
|
||||
|
@ -28,9 +28,10 @@ import net.corda.core.node.services.*
|
||||
import net.corda.core.serialization.SerializeAsToken
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.cordapp.CordappLoader
|
||||
import net.corda.node.internal.ServicesForResolutionImpl
|
||||
import net.corda.node.internal.configureDatabase
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.node.services.api.*
|
||||
import net.corda.node.services.identity.InMemoryIdentityService
|
||||
import net.corda.node.services.schema.HibernateObserver
|
||||
@ -72,7 +73,15 @@ open class MockServices private constructor(
|
||||
private val initialIdentity: TestIdentity,
|
||||
private val moreKeys: Array<out KeyPair>
|
||||
) : ServiceHub {
|
||||
|
||||
companion object {
|
||||
|
||||
private fun cordappLoaderForPackages(packages: Iterable<String>): CordappLoader {
|
||||
|
||||
val cordappPaths = TestCordappDirectories.forPackages(packages)
|
||||
return JarScanningCordappLoader.fromDirectories(cordappPaths)
|
||||
}
|
||||
|
||||
/**
|
||||
* Make properties appropriate for creating a DataSource for unit tests.
|
||||
* Defaults configuration of in-memory H2 instance. If 'databaseProvider' system property is set then creates
|
||||
@ -102,7 +111,7 @@ open class MockServices private constructor(
|
||||
initialIdentity: TestIdentity,
|
||||
networkParameters: NetworkParameters = testNetworkParameters(),
|
||||
vararg moreKeys: KeyPair): Pair<CordaPersistence, MockServices> {
|
||||
val cordappLoader = CordappLoader.createWithTestPackages(cordappPackages)
|
||||
val cordappLoader = cordappLoaderForPackages(cordappPackages)
|
||||
val dataSourceProps = makeInternalTestDataSourceProperties(initialIdentity.name.organisation, SecureHash.randomSHA256().toString())
|
||||
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas)
|
||||
val database = configureDatabase(dataSourceProps, makeTestDatabaseProperties(initialIdentity.name.organisation), identityService::wellKnownPartyFromX500Name, identityService::wellKnownPartyFromAnonymous, schemaService)
|
||||
@ -124,14 +133,6 @@ open class MockServices private constructor(
|
||||
return Pair(database, mockService)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
private fun getCallerPackage(): String {
|
||||
// TODO: In Java 9 there's a new stack walker API that is better than this.
|
||||
// The magic number '3' here is to chop off this method, an invisible bridge method generated by the
|
||||
// compiler and then the c'tor itself.
|
||||
return Throwable().stackTrace[3].className.split('.').dropLast(1).joinToString(".")
|
||||
}
|
||||
|
||||
// Because Kotlin is dumb and makes not publicly visible objects public, thus changing the public API.
|
||||
private val mockStateMachineRecordedTransactionMappingStorage = MockStateMachineRecordedTransactionMappingStorage()
|
||||
}
|
||||
@ -155,39 +156,38 @@ open class MockServices private constructor(
|
||||
* (you can get one from [makeTestIdentityService]) and represents the given identity.
|
||||
*/
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: List<String>,
|
||||
constructor(cordappPackages: Iterable<String>,
|
||||
initialIdentity: TestIdentity,
|
||||
identityService: IdentityService = makeTestIdentityService(),
|
||||
vararg moreKeys: KeyPair) :
|
||||
this(CordappLoader.createWithTestPackages(cordappPackages), identityService, testNetworkParameters(), initialIdentity, moreKeys)
|
||||
this(cordappLoaderForPackages(cordappPackages), identityService, testNetworkParameters(), initialIdentity, moreKeys)
|
||||
|
||||
constructor(cordappPackages: List<String>,
|
||||
constructor(cordappPackages: Iterable<String>,
|
||||
initialIdentity: TestIdentity,
|
||||
identityService: IdentityService,
|
||||
networkParameters: NetworkParameters,
|
||||
vararg moreKeys: KeyPair) :
|
||||
this(CordappLoader.createWithTestPackages(cordappPackages), identityService, networkParameters, initialIdentity, moreKeys)
|
||||
this(cordappLoaderForPackages(cordappPackages), identityService, networkParameters, initialIdentity, moreKeys)
|
||||
|
||||
/**
|
||||
* Create a mock [ServiceHub] that looks for app code in the given package names, uses the provided identity service
|
||||
* (you can get one from [makeTestIdentityService]) and represents the given identity.
|
||||
*/
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: List<String>, initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService(), key: KeyPair, vararg moreKeys: KeyPair) :
|
||||
constructor(cordappPackages: Iterable<String>, initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService(), key: KeyPair, vararg moreKeys: KeyPair) :
|
||||
this(cordappPackages, TestIdentity(initialIdentityName, key), identityService, *moreKeys)
|
||||
|
||||
/**
|
||||
* Create a mock [ServiceHub] that can't load CorDapp code, which uses the provided identity service
|
||||
* (you can get one from [makeTestIdentityService]) and which represents the given identity.
|
||||
*/
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: List<String>, initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService()) :
|
||||
constructor(cordappPackages: Iterable<String>, initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService()) :
|
||||
this(cordappPackages, TestIdentity(initialIdentityName), identityService)
|
||||
|
||||
/**
|
||||
* Create a mock [ServiceHub] that can't load CorDapp code, and which uses a default service identity.
|
||||
*/
|
||||
constructor(cordappPackages: List<String>) : this(cordappPackages, CordaX500Name("TestIdentity", "", "GB"), makeTestIdentityService())
|
||||
constructor(cordappPackages: Iterable<String>) : this(cordappPackages, CordaX500Name("TestIdentity", "", "GB"), makeTestIdentityService())
|
||||
|
||||
/**
|
||||
* Create a mock [ServiceHub] which uses the package of the caller to find CorDapp code. It uses the provided identity service
|
||||
@ -195,7 +195,7 @@ open class MockServices private constructor(
|
||||
*/
|
||||
@JvmOverloads
|
||||
constructor(initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService(), key: KeyPair, vararg moreKeys: KeyPair)
|
||||
: this(listOf(getCallerPackage()), TestIdentity(initialIdentityName, key), identityService, *moreKeys)
|
||||
: this(listOf(getCallerPackage(MockServices::class)!!), TestIdentity(initialIdentityName, key), identityService, *moreKeys)
|
||||
|
||||
/**
|
||||
* Create a mock [ServiceHub] which uses the package of the caller to find CorDapp code. It uses the provided identity service
|
||||
@ -203,7 +203,7 @@ open class MockServices private constructor(
|
||||
*/
|
||||
@JvmOverloads
|
||||
constructor(initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService())
|
||||
: this(listOf(getCallerPackage()), TestIdentity(initialIdentityName), identityService)
|
||||
: this(listOf(getCallerPackage(MockServices::class)!!), TestIdentity(initialIdentityName), identityService)
|
||||
|
||||
/**
|
||||
* A helper constructor that requires at least one test identity to be registered, and which takes the package of
|
||||
@ -212,7 +212,7 @@ open class MockServices private constructor(
|
||||
* it is aware of.
|
||||
*/
|
||||
constructor(firstIdentity: TestIdentity, vararg moreIdentities: TestIdentity) : this(
|
||||
listOf(getCallerPackage()),
|
||||
listOf(getCallerPackage(MockServices::class)!!),
|
||||
firstIdentity,
|
||||
makeTestIdentityService(*listOf(firstIdentity, *moreIdentities).map { it.identity }.toTypedArray()),
|
||||
firstIdentity.keyPair
|
||||
@ -222,7 +222,7 @@ open class MockServices private constructor(
|
||||
* Create a mock [ServiceHub] which uses the package of the caller to find CorDapp code. It uses a default service
|
||||
* identity.
|
||||
*/
|
||||
constructor() : this(listOf(getCallerPackage()), CordaX500Name("TestIdentity", "", "GB"), makeTestIdentityService())
|
||||
constructor() : this(listOf(getCallerPackage(MockServices::class)!!), CordaX500Name("TestIdentity", "", "GB"), makeTestIdentityService())
|
||||
|
||||
override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable<SignedTransaction>) {
|
||||
txs.forEach {
|
||||
@ -241,7 +241,9 @@ open class MockServices private constructor(
|
||||
return NodeInfo(listOf(NetworkHostAndPort("mock.node.services", 10000)), listOf(initialIdentity.identity), 1, serial = 1L)
|
||||
}
|
||||
override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2)
|
||||
|
||||
private val mockCordappProvider: MockCordappProvider = MockCordappProvider(cordappLoader, attachments, networkParameters.whitelistedContractImplementations)
|
||||
|
||||
override val cordappProvider: CordappProvider get() = mockCordappProvider
|
||||
|
||||
protected val servicesForResolution: ServicesForResolution get() = ServicesForResolutionImpl(identityService, attachments, cordappProvider, networkParameters, validatedTransactions)
|
||||
|
@ -62,6 +62,7 @@ import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.node.User
|
||||
import net.corda.testing.node.internal.DriverDSLImpl.ClusterType.NON_VALIDATING_RAFT
|
||||
import net.corda.testing.node.internal.DriverDSLImpl.ClusterType.VALIDATING_RAFT
|
||||
import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import rx.Subscription
|
||||
@ -95,20 +96,19 @@ class DriverDSLImpl(
|
||||
val isDebug: Boolean,
|
||||
val startNodesInProcess: Boolean,
|
||||
val waitForAllNodesToFinish: Boolean,
|
||||
extraCordappPackagesToScan: List<String>,
|
||||
val jmxPolicy: JmxPolicy,
|
||||
val notarySpecs: List<NotarySpec>,
|
||||
val compatibilityZone: CompatibilityZoneParams?,
|
||||
val networkParameters: NetworkParameters,
|
||||
val notaryCustomOverrides: Map<String, Any?>,
|
||||
val inMemoryDB: Boolean
|
||||
val inMemoryDB: Boolean,
|
||||
val cordappsForAllNodes: Set<TestCorDapp>
|
||||
) : InternalDriverDSL {
|
||||
|
||||
private var _executorService: ScheduledExecutorService? = null
|
||||
val executorService get() = _executorService!!
|
||||
private var _shutdownManager: ShutdownManager? = null
|
||||
override val shutdownManager get() = _shutdownManager!!
|
||||
private val cordappPackages = extraCordappPackagesToScan + getCallerPackage()
|
||||
// Map from a nodes legal name to an observable emitting the number of nodes in its network map.
|
||||
private val networkVisibilityController = NetworkVisibilityController()
|
||||
/**
|
||||
@ -206,26 +206,7 @@ class DriverDSLImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun startNode(
|
||||
defaultParameters: NodeParameters,
|
||||
providedName: CordaX500Name?,
|
||||
rpcUsers: List<User>,
|
||||
verifierType: VerifierType,
|
||||
customOverrides: Map<String, Any?>,
|
||||
startInSameProcess: Boolean?,
|
||||
maximumHeapSize: String
|
||||
): CordaFuture<NodeHandle> {
|
||||
return startNode(
|
||||
defaultParameters,
|
||||
providedName,
|
||||
rpcUsers,
|
||||
verifierType,
|
||||
customOverrides,
|
||||
startInSameProcess,
|
||||
maximumHeapSize,
|
||||
null
|
||||
)
|
||||
}
|
||||
override fun startNode(defaultParameters: NodeParameters, providedName: CordaX500Name?, rpcUsers: List<User>, verifierType: VerifierType, customOverrides: Map<String, Any?>, startInSameProcess: Boolean?, maximumHeapSize: String) = startNode(defaultParameters, providedName, rpcUsers, verifierType, customOverrides, startInSameProcess, maximumHeapSize, defaultParameters.additionalCordapps, defaultParameters.regenerateCordappsOnStart, null)
|
||||
|
||||
override fun startNode(
|
||||
defaultParameters: NodeParameters,
|
||||
@ -235,6 +216,20 @@ class DriverDSLImpl(
|
||||
customOverrides: Map<String, Any?>,
|
||||
startInSameProcess: Boolean?,
|
||||
maximumHeapSize: String,
|
||||
additionalCordapps: Set<TestCorDapp>,
|
||||
regenerateCordappsOnStart: Boolean
|
||||
) = startNode(defaultParameters, providedName, rpcUsers, verifierType, customOverrides, startInSameProcess, maximumHeapSize, additionalCordapps, regenerateCordappsOnStart, null)
|
||||
|
||||
override fun startNode(
|
||||
defaultParameters: NodeParameters,
|
||||
providedName: CordaX500Name?,
|
||||
rpcUsers: List<User>,
|
||||
verifierType: VerifierType,
|
||||
customOverrides: Map<String, Any?>,
|
||||
startInSameProcess: Boolean?,
|
||||
maximumHeapSize: String,
|
||||
additionalCordapps: Set<TestCorDapp>,
|
||||
regenerateCordappsOnStart: Boolean,
|
||||
bytemanPort: Int?
|
||||
): CordaFuture<NodeHandle> {
|
||||
val p2pAddress = portAllocation.nextHostAndPort()
|
||||
@ -251,7 +246,7 @@ class DriverDSLImpl(
|
||||
return registrationFuture.flatMap {
|
||||
networkMapAvailability.flatMap {
|
||||
// But starting the node proper does require the network map
|
||||
startRegisteredNode(name, it, rpcUsers, verifierType, customOverrides, startInSameProcess, maximumHeapSize, p2pAddress, bytemanPort)
|
||||
startRegisteredNode(name, it, rpcUsers, verifierType, customOverrides, startInSameProcess, maximumHeapSize, p2pAddress, additionalCordapps, regenerateCordappsOnStart, bytemanPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -264,6 +259,8 @@ class DriverDSLImpl(
|
||||
startInSameProcess: Boolean? = null,
|
||||
maximumHeapSize: String = "512m",
|
||||
p2pAddress: NetworkHostAndPort = portAllocation.nextHostAndPort(),
|
||||
additionalCordapps: Set<TestCorDapp> = emptySet(),
|
||||
regenerateCordappsOnStart: Boolean = false,
|
||||
bytemanPort: Int? = null): CordaFuture<NodeHandle> {
|
||||
val rpcAddress = portAllocation.nextHostAndPort()
|
||||
val rpcAdminAddress = portAllocation.nextHostAndPort()
|
||||
@ -293,7 +290,7 @@ class DriverDSLImpl(
|
||||
allowMissingConfig = true,
|
||||
configOverrides = if (overrides.hasPath("devMode")) overrides else overrides + mapOf("devMode" to true)
|
||||
)).checkAndOverrideForInMemoryDB()
|
||||
return startNodeInternal(config, webAddress, startInSameProcess, maximumHeapSize, localNetworkMap, bytemanPort)
|
||||
return startNodeInternal(config, webAddress, startInSameProcess, maximumHeapSize, localNetworkMap, additionalCordapps, regenerateCordappsOnStart, bytemanPort)
|
||||
}
|
||||
|
||||
private fun startNodeRegistration(providedName: CordaX500Name, rootCert: X509Certificate, compatibilityZoneURL: URL): CordaFuture<NodeConfig> {
|
||||
@ -347,6 +344,7 @@ class DriverDSLImpl(
|
||||
NON_VALIDATING_BFT(false, CordaX500Name("BFT", "Zurich", "CH"))
|
||||
}
|
||||
|
||||
// TODO remove this
|
||||
internal fun startCordformNodes(cordforms: List<CordformNode>): CordaFuture<*> {
|
||||
check(notarySpecs.isEmpty()) { "Specify notaries in the CordformDefinition" }
|
||||
check(compatibilityZone == null) { "Cordform nodes cannot be run with compatibilityZoneURL" }
|
||||
@ -404,6 +402,7 @@ class DriverDSLImpl(
|
||||
}.transpose()
|
||||
}
|
||||
|
||||
// TODO remove this
|
||||
private fun startCordformNode(cordform: CordformNode, localNetworkMap: LocalNetworkMap): CordaFuture<NodeHandle> {
|
||||
val name = CordaX500Name.parse(cordform.name)
|
||||
// TODO We shouldn't have to allocate an RPC or web address if they're not specified. We're having to do this because of startNodeInternal
|
||||
@ -432,9 +431,8 @@ class DriverDSLImpl(
|
||||
allowMissingConfig = true,
|
||||
configOverrides = rawConfig.toNodeOnly()
|
||||
)
|
||||
val cordaConfig = typesafe.parseAsNodeConfiguration()
|
||||
val config = NodeConfig(rawConfig, cordaConfig).checkAndOverrideForInMemoryDB()
|
||||
return startNodeInternal(config, webAddress, null, "512m", localNetworkMap, null)
|
||||
val config = NodeConfig(typesafe).checkAndOverrideForInMemoryDB()
|
||||
return startNodeInternal(config, webAddress, null, "512m", localNetworkMap, emptySet())
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@ -656,7 +654,6 @@ class DriverDSLImpl(
|
||||
bytemanJarPath,
|
||||
null,
|
||||
systemProperties,
|
||||
cordappPackages,
|
||||
"512m",
|
||||
*extraCmdLineFlag
|
||||
)
|
||||
@ -666,14 +663,20 @@ class DriverDSLImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun startNodeInternal(config: NodeConfig,
|
||||
private val sharedCordappsDirectories: Iterable<Path> by lazy {
|
||||
TestCordappDirectories.cached(cordappsForAllNodes)
|
||||
}
|
||||
|
||||
private fun startNodeInternal(specifiedConfig: NodeConfig,
|
||||
webAddress: NetworkHostAndPort,
|
||||
startInProcess: Boolean?,
|
||||
maximumHeapSize: String,
|
||||
localNetworkMap: LocalNetworkMap?,
|
||||
bytemanPort: Int?): CordaFuture<NodeHandle> {
|
||||
val visibilityHandle = networkVisibilityController.register(config.corda.myLegalName)
|
||||
val baseDirectory = config.corda.baseDirectory.createDirectories()
|
||||
additionalCordapps: Set<TestCorDapp>,
|
||||
regenerateCordappsOnStart: Boolean = false,
|
||||
bytemanPort: Int? = null): CordaFuture<NodeHandle> {
|
||||
val visibilityHandle = networkVisibilityController.register(specifiedConfig.corda.myLegalName)
|
||||
val baseDirectory = specifiedConfig.corda.baseDirectory.createDirectories()
|
||||
localNetworkMap?.networkParametersCopier?.install(baseDirectory)
|
||||
localNetworkMap?.nodeInfosCopier?.addConfig(baseDirectory)
|
||||
|
||||
@ -682,10 +685,16 @@ class DriverDSLImpl(
|
||||
visibilityHandle.close()
|
||||
}
|
||||
|
||||
val useHTTPS = config.typesafe.run { hasPath("useHTTPS") && getBoolean("useHTTPS") }
|
||||
val useHTTPS = specifiedConfig.typesafe.run { hasPath("useHTTPS") && getBoolean("useHTTPS") }
|
||||
|
||||
val existingCorDappDirectoriesOption = if (regenerateCordappsOnStart) emptyList<String>() else if (specifiedConfig.typesafe.hasPath(NodeConfiguration.cordappDirectoriesKey)) specifiedConfig.typesafe.getStringList(NodeConfiguration.cordappDirectoriesKey) else emptyList()
|
||||
|
||||
val cordappDirectories = existingCorDappDirectoriesOption + sharedCordappsDirectories.map { it.toString() } + TestCordappDirectories.cached(additionalCordapps, regenerateCordappsOnStart).map { it.toString() }
|
||||
|
||||
val config = NodeConfig(specifiedConfig.typesafe.withValue(NodeConfiguration.cordappDirectoriesKey, ConfigValueFactory.fromIterable(cordappDirectories)))
|
||||
|
||||
if (startInProcess ?: startNodesInProcess) {
|
||||
val nodeAndThreadFuture = startInProcessNode(executorService, config, cordappPackages)
|
||||
val nodeAndThreadFuture = startInProcessNode(executorService, config)
|
||||
shutdownManager.registerShutdown(
|
||||
nodeAndThreadFuture.map { (node, thread) ->
|
||||
{
|
||||
@ -712,7 +721,7 @@ class DriverDSLImpl(
|
||||
} else {
|
||||
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
||||
val monitorPort = if (jmxPolicy.startJmxHttpServer) jmxPolicy.jmxHttpServerPortAllocation?.nextPort() else null
|
||||
val process = startOutOfProcessNode(config, quasarJarPath, debugPort, jolokiaJarPath, monitorPort, bytemanJarPath, bytemanPort, systemProperties, cordappPackages, maximumHeapSize)
|
||||
val process = startOutOfProcessNode(config, quasarJarPath, debugPort, jolokiaJarPath, monitorPort, bytemanJarPath, bytemanPort, systemProperties, maximumHeapSize)
|
||||
|
||||
// Destroy the child process when the parent exits.This is needed even when `waitForAllNodesToFinish` is
|
||||
// true because we don't want orphaned processes in the case that the parent process is terminated by the
|
||||
@ -806,10 +815,12 @@ class DriverDSLImpl(
|
||||
|
||||
private fun <A> oneOf(array: Array<A>) = array[Random().nextInt(array.size)]
|
||||
|
||||
fun cordappsInCurrentAndAdditionalPackages(packagesToScan: Iterable<String> = emptySet()): Set<TestCorDapp> = cordappsForPackages(getCallerPackage() + packagesToScan)
|
||||
fun cordappsInCurrentAndAdditionalPackages(firstPackage: String, vararg otherPackages: String): Set<TestCorDapp> = cordappsInCurrentAndAdditionalPackages(otherPackages.toList() + firstPackage)
|
||||
|
||||
private fun startInProcessNode(
|
||||
executorService: ScheduledExecutorService,
|
||||
config: NodeConfig,
|
||||
cordappPackages: List<String>
|
||||
config: NodeConfig
|
||||
): CordaFuture<Pair<StartedNode<Node>, Thread>> {
|
||||
return executorService.fork {
|
||||
log.info("Starting in-process Node ${config.corda.myLegalName.organisation}")
|
||||
@ -819,7 +830,7 @@ class DriverDSLImpl(
|
||||
// Write node.conf
|
||||
writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe.toNodeOnly())
|
||||
// TODO pass the version in?
|
||||
val node = InProcessNode(config.corda, MOCK_VERSION_INFO, cordappPackages).start()
|
||||
val node = InProcessNode(config.corda, MOCK_VERSION_INFO).start()
|
||||
val nodeThread = thread(name = config.corda.myLegalName.organisation) {
|
||||
node.internals.run()
|
||||
}
|
||||
@ -838,10 +849,10 @@ class DriverDSLImpl(
|
||||
bytemanJarPath: String?,
|
||||
bytemanPort: Int?,
|
||||
overriddenSystemProperties: Map<String, String>,
|
||||
cordappPackages: List<String>,
|
||||
maximumHeapSize: String,
|
||||
vararg extraCmdLineFlag: String
|
||||
): Process {
|
||||
|
||||
log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, " +
|
||||
"debug port is " + (debugPort ?: "not enabled") + ", " +
|
||||
"jolokia monitoring port is " + (monitorPort ?: "not enabled") + ", " +
|
||||
@ -856,11 +867,6 @@ class DriverDSLImpl(
|
||||
)
|
||||
|
||||
systemProperties += inheritFromParentProcess()
|
||||
|
||||
if (cordappPackages.isNotEmpty()) {
|
||||
systemProperties += Node.scanPackagesSystemProperty to cordappPackages.joinToString(Node.scanPackagesSeparator)
|
||||
}
|
||||
|
||||
systemProperties += overriddenSystemProperties
|
||||
|
||||
// See experimental/quasar-hook/README.md for how to generate.
|
||||
@ -1075,6 +1081,8 @@ interface InternalDriverDSL : DriverDSL, CordformContext {
|
||||
customOverrides: Map<String, Any?> = defaultParameters.customOverrides,
|
||||
startInSameProcess: Boolean? = defaultParameters.startInSameProcess,
|
||||
maximumHeapSize: String = defaultParameters.maximumHeapSize,
|
||||
additionalCordapps: Set<TestCorDapp> = defaultParameters.additionalCordapps,
|
||||
regenerateCordappsOnStart: Boolean = defaultParameters.regenerateCordappsOnStart,
|
||||
bytemanPort: Int? = null
|
||||
): CordaFuture<NodeHandle>
|
||||
}
|
||||
@ -1132,13 +1140,13 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
|
||||
isDebug = defaultParameters.isDebug,
|
||||
startNodesInProcess = defaultParameters.startNodesInProcess,
|
||||
waitForAllNodesToFinish = defaultParameters.waitForAllNodesToFinish,
|
||||
extraCordappPackagesToScan = defaultParameters.extraCordappPackagesToScan,
|
||||
jmxPolicy = defaultParameters.jmxPolicy,
|
||||
notarySpecs = defaultParameters.notarySpecs,
|
||||
compatibilityZone = null,
|
||||
networkParameters = defaultParameters.networkParameters,
|
||||
notaryCustomOverrides = defaultParameters.notaryCustomOverrides,
|
||||
inMemoryDB = defaultParameters.inMemoryDB
|
||||
inMemoryDB = defaultParameters.inMemoryDB,
|
||||
cordappsForAllNodes = defaultParameters.cordappsForAllNodes()
|
||||
)
|
||||
)
|
||||
val shutdownHook = addShutdownHook(driverDsl::shutdown)
|
||||
@ -1212,12 +1220,12 @@ fun <A> internalDriver(
|
||||
startNodesInProcess: Boolean = DriverParameters().startNodesInProcess,
|
||||
waitForAllNodesToFinish: Boolean = DriverParameters().waitForAllNodesToFinish,
|
||||
notarySpecs: List<NotarySpec> = DriverParameters().notarySpecs,
|
||||
extraCordappPackagesToScan: List<String> = DriverParameters().extraCordappPackagesToScan,
|
||||
jmxPolicy: JmxPolicy = DriverParameters().jmxPolicy,
|
||||
networkParameters: NetworkParameters = DriverParameters().networkParameters,
|
||||
compatibilityZone: CompatibilityZoneParams? = null,
|
||||
notaryCustomOverrides: Map<String, Any?> = DriverParameters().notaryCustomOverrides,
|
||||
inMemoryDB: Boolean = DriverParameters().inMemoryDB,
|
||||
cordappsForAllNodes: Set<TestCorDapp> = DriverParameters().cordappsForAllNodes(),
|
||||
dsl: DriverDSLImpl.() -> A
|
||||
): A {
|
||||
return genericDriver(
|
||||
@ -1231,12 +1239,12 @@ fun <A> internalDriver(
|
||||
startNodesInProcess = startNodesInProcess,
|
||||
waitForAllNodesToFinish = waitForAllNodesToFinish,
|
||||
notarySpecs = notarySpecs,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
jmxPolicy = jmxPolicy,
|
||||
compatibilityZone = compatibilityZone,
|
||||
networkParameters = networkParameters,
|
||||
notaryCustomOverrides = notaryCustomOverrides,
|
||||
inMemoryDB = inMemoryDB
|
||||
inMemoryDB = inMemoryDB,
|
||||
cordappsForAllNodes = cordappsForAllNodes
|
||||
),
|
||||
coerce = { it },
|
||||
dsl = dsl,
|
||||
@ -1257,6 +1265,8 @@ private fun Config.toNodeOnly(): Config {
|
||||
return if (hasPath("webAddress")) withoutPath("webAddress").withoutPath("useHTTPS") else this
|
||||
}
|
||||
|
||||
internal fun DriverParameters.cordappsForAllNodes(): Set<TestCorDapp> = cordappsForAllNodes ?: cordappsInCurrentAndAdditionalPackages(extraCordappPackagesToScan)
|
||||
|
||||
fun DriverDSL.startNode(providedName: CordaX500Name, devMode: Boolean, parameters: NodeParameters = NodeParameters()): CordaFuture<NodeHandle> {
|
||||
var customOverrides = emptyMap<String, String>()
|
||||
if (!devMode) {
|
||||
|
@ -22,10 +22,7 @@ import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.VisibleForTesting
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.createDirectory
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.messaging.MessageRecipients
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.messaging.SingleMessageRecipient
|
||||
@ -35,14 +32,12 @@ import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.node.services.KeyManagementService
|
||||
import net.corda.core.serialization.SerializationWhitelist
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.hours
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.core.utilities.*
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.cordapp.CordappLoader
|
||||
import net.corda.node.internal.AbstractNode
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.node.services.api.NodePropertiesStore
|
||||
import net.corda.node.services.api.SchemaService
|
||||
import net.corda.node.services.config.*
|
||||
@ -58,16 +53,17 @@ import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.setGlobalSerialization
|
||||
import net.corda.testing.internal.testThreadFactory
|
||||
import net.corda.testing.node.*
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
import org.apache.activemq.artemis.utils.ReusableLatch
|
||||
import org.apache.sshd.common.util.security.SecurityUtils
|
||||
import rx.internal.schedulers.CachedThreadScheduler
|
||||
import java.math.BigInteger
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.security.KeyPair
|
||||
import java.security.PublicKey
|
||||
import java.time.Clock
|
||||
@ -85,8 +81,7 @@ data class MockNodeArgs(
|
||||
val network: InternalMockNetwork,
|
||||
val id: Int,
|
||||
val entropyRoot: BigInteger,
|
||||
val version: VersionInfo = MOCK_VERSION_INFO,
|
||||
val extraCordappPackages: List<String> = emptyList()
|
||||
val version: VersionInfo = MOCK_VERSION_INFO
|
||||
)
|
||||
|
||||
data class InternalMockNodeParameters(
|
||||
@ -95,25 +90,26 @@ data class InternalMockNodeParameters(
|
||||
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||
val configOverrides: (NodeConfiguration) -> Any? = {},
|
||||
val version: VersionInfo = MOCK_VERSION_INFO,
|
||||
val extraCordappPackages: List<String> = emptyList()) {
|
||||
val additionalCordapps: Set<TestCorDapp>? = null) {
|
||||
constructor(mockNodeParameters: MockNodeParameters) : this(
|
||||
mockNodeParameters.forcedID,
|
||||
mockNodeParameters.legalName,
|
||||
mockNodeParameters.entropyRoot,
|
||||
mockNodeParameters.configOverrides,
|
||||
MOCK_VERSION_INFO,
|
||||
mockNodeParameters.extraCordappPackages
|
||||
mockNodeParameters.additionalCordapps
|
||||
)
|
||||
}
|
||||
|
||||
open class InternalMockNetwork(private val cordappPackages: List<String> = emptyList(),
|
||||
defaultParameters: MockNetworkParameters = MockNetworkParameters(),
|
||||
open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParameters(),
|
||||
val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped,
|
||||
val threadPerNode: Boolean = defaultParameters.threadPerNode,
|
||||
servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy,
|
||||
val notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs,
|
||||
val testDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()),
|
||||
networkParameters: NetworkParameters = testNetworkParameters(),
|
||||
val defaultFactory: (MockNodeArgs) -> MockNode = InternalMockNetwork::MockNode) {
|
||||
val defaultFactory: (MockNodeArgs, CordappLoader?) -> MockNode = { args, cordappLoader -> cordappLoader?.let { MockNode(args, it) } ?: MockNode(args) },
|
||||
val cordappsForAllNodes: Set<TestCorDapp> = emptySet()) {
|
||||
init {
|
||||
// Apache SSHD for whatever reason registers a SFTP FileSystemProvider - which gets loaded by JimFS.
|
||||
// This SFTP support loads BouncyCastle, which we want to avoid.
|
||||
@ -122,6 +118,10 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
require(networkParameters.notaries.isEmpty()) { "Define notaries using notarySpecs" }
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private val logger = loggerFor<InternalMockNetwork>()
|
||||
}
|
||||
|
||||
var nextNodeId = 0
|
||||
private set
|
||||
private val filesystem = Jimfs.newFileSystem(unix())
|
||||
@ -138,6 +138,10 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
}
|
||||
private val sharedUserCount = AtomicInteger(0)
|
||||
|
||||
private val sharedCorDappsDirectories: Iterable<Path> by lazy {
|
||||
TestCordappDirectories.cached(cordappsForAllNodes)
|
||||
}
|
||||
|
||||
/** A read only view of the current set of nodes. */
|
||||
val nodes: List<MockNode> get() = _nodes
|
||||
|
||||
@ -230,12 +234,11 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
}
|
||||
}
|
||||
|
||||
open class MockNode(args: MockNodeArgs) : AbstractNode(
|
||||
open class MockNode(args: MockNodeArgs, cordappLoader: CordappLoader = JarScanningCordappLoader.fromDirectories(args.config.cordappDirectories)) : AbstractNode(
|
||||
args.config,
|
||||
TestClock(Clock.systemUTC()),
|
||||
args.version,
|
||||
// Add the specified additional CorDapps.
|
||||
CordappLoader.createDefaultWithTestPackages(args.config, args.network.cordappPackages + args.extraCordappPackages),
|
||||
cordappLoader,
|
||||
args.network.busyLatch
|
||||
) {
|
||||
companion object {
|
||||
@ -365,7 +368,7 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
return createUnstartedNode(parameters, defaultFactory)
|
||||
}
|
||||
|
||||
fun <N : MockNode> createUnstartedNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs) -> N): N {
|
||||
fun <N : MockNode> createUnstartedNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs, CordappLoader?) -> N): N {
|
||||
return createNodeImpl(parameters, nodeFactory, false)
|
||||
}
|
||||
|
||||
@ -374,11 +377,11 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
}
|
||||
|
||||
/** Like the other [createNode] but takes a [nodeFactory] and propagates its [MockNode] subtype. */
|
||||
fun <N : MockNode> createNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs) -> N): StartedNode<N> {
|
||||
fun <N : MockNode> createNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs, CordappLoader?) -> N): StartedNode<N> {
|
||||
return uncheckedCast(createNodeImpl(parameters, nodeFactory, true).started)!!
|
||||
}
|
||||
|
||||
private fun <N : MockNode> createNodeImpl(parameters: InternalMockNodeParameters, nodeFactory: (MockNodeArgs) -> N, start: Boolean): N {
|
||||
private fun <N : MockNode> createNodeImpl(parameters: InternalMockNodeParameters, nodeFactory: (MockNodeArgs, CordappLoader?) -> N, start: Boolean): N {
|
||||
val id = parameters.forcedID ?: nextNodeId++
|
||||
val config = mockNodeConfiguration().also {
|
||||
doReturn(baseDirectory(id).createDirectories()).whenever(it).baseDirectory
|
||||
@ -388,7 +391,12 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
doReturn(emptyList<SecureHash>()).whenever(it).extraNetworkMapKeys
|
||||
parameters.configOverrides(it)
|
||||
}
|
||||
val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version, parameters.extraCordappPackages))
|
||||
|
||||
val cordapps: Set<TestCorDapp> = parameters.additionalCordapps ?: emptySet()
|
||||
val cordappDirectories = sharedCorDappsDirectories + TestCordappDirectories.cached(cordapps)
|
||||
doReturn(cordappDirectories).whenever(config).cordappDirectories
|
||||
|
||||
val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version), JarScanningCordappLoader.fromDirectories(cordappDirectories))
|
||||
_nodes += node
|
||||
if (start) {
|
||||
node.start()
|
||||
@ -396,7 +404,7 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
return node
|
||||
}
|
||||
|
||||
fun <N : MockNode> restartNode(node: StartedNode<N>, nodeFactory: (MockNodeArgs) -> N): StartedNode<N> {
|
||||
fun <N : MockNode> restartNode(node: StartedNode<N>, nodeFactory: (MockNodeArgs, CordappLoader?) -> N): StartedNode<N> {
|
||||
node.internals.disableDBCloseOnStop()
|
||||
node.dispose()
|
||||
return createNode(
|
||||
@ -407,7 +415,7 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
|
||||
fun restartNode(node: StartedNode<MockNode>): StartedNode<MockNode> = restartNode(node, defaultFactory)
|
||||
|
||||
fun baseDirectory(nodeId: Int): Path = filesystem.getPath("/nodes/$nodeId")
|
||||
fun baseDirectory(nodeId: Int): Path = testDirectory / "nodes/$nodeId"
|
||||
|
||||
/**
|
||||
* Asks every node in order to process any queued up inbound messages. This may in turn result in nodes
|
||||
@ -465,7 +473,6 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
|
||||
fun waitQuiescent() {
|
||||
busyLatch.await()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
open class MessagingServiceSpy(val messagingService: MessagingService) : MessagingService by messagingService
|
||||
|
@ -0,0 +1,73 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
|
||||
internal class MutableTestCorDapp private constructor(override val name: String, override val version: String, override val vendor: String, override val title: String, private val willResourceBeAddedToCorDapp: (String, URL) -> Boolean, private val jarEntries: Set<JarEntryInfo>) : TestCorDapp.Mutable {
|
||||
|
||||
constructor(name: String, version: String, vendor: String, title: String, classes: Set<Class<*>>, willResourceBeAddedToCorDapp: (String, URL) -> Boolean) : this(name, version, vendor, title, willResourceBeAddedToCorDapp, jarEntriesFromClasses(classes))
|
||||
|
||||
companion object {
|
||||
|
||||
private const val jarExtension = ".jar"
|
||||
private const val whitespace = " "
|
||||
private const val whitespaceReplacement = "_"
|
||||
private val productionPathSegments = setOf<(String) -> String>({ "out${File.separator}production${File.separator}classes" }, { fullyQualifiedName -> "main${File.separator}${fullyQualifiedName.packageToPath()}" })
|
||||
private val excludedCordaPackages = setOf("net.corda.core", "net.corda.node")
|
||||
|
||||
fun filterTestCorDappClass(fullyQualifiedName: String, url: URL): Boolean {
|
||||
|
||||
return isTestResource(fullyQualifiedName, url) || !isInExcludedCordaPackage(fullyQualifiedName)
|
||||
}
|
||||
|
||||
private fun isTestResource(fullyQualifiedName: String, url: URL): Boolean {
|
||||
|
||||
return productionPathSegments.map { it.invoke(fullyQualifiedName) }.none { url.toString().contains(it) }
|
||||
}
|
||||
|
||||
private fun isInExcludedCordaPackage(packageName: String): Boolean {
|
||||
|
||||
return excludedCordaPackages.any { packageName.startsWith(it) }
|
||||
}
|
||||
|
||||
private fun jarEntriesFromClasses(classes: Set<Class<*>>): Set<JarEntryInfo> {
|
||||
|
||||
val illegal = classes.filter { it.protectionDomain?.codeSource?.location == null }
|
||||
if (illegal.isNotEmpty()) {
|
||||
throw IllegalArgumentException("Some classes do not have a location, typically because they are part of Java or Kotlin. Offending types were: ${illegal.joinToString(", ", "[", "]") { it.simpleName }}")
|
||||
}
|
||||
return classes.map(Class<*>::jarEntryInfo).toSet()
|
||||
}
|
||||
}
|
||||
|
||||
override val classes: Set<Class<*>> = jarEntries.filterIsInstance(JarEntryInfo.ClassJarEntryInfo::class.java).map(JarEntryInfo.ClassJarEntryInfo::clazz).toSet()
|
||||
|
||||
override val resources: Set<URL> = jarEntries.map(JarEntryInfo::url).toSet()
|
||||
|
||||
override fun withName(name: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
||||
|
||||
override fun withTitle(title: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
||||
|
||||
override fun withVersion(version: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
||||
|
||||
override fun withVendor(vendor: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
||||
|
||||
override fun withClasses(classes: Set<Class<*>>) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
||||
|
||||
override fun plusPackages(pckgs: Set<String>) = withClasses(pckgs.map { allClassesForPackage(it) }.fold(classes) { all, packageClasses -> all + packageClasses })
|
||||
|
||||
override fun minusPackages(pckgs: Set<String>) = withClasses(pckgs.map { allClassesForPackage(it) }.fold(classes) { all, packageClasses -> all - packageClasses })
|
||||
|
||||
override fun plusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable = MutableTestCorDapp(name, version, vendor, title, willResourceBeAddedToCorDapp, jarEntries + JarEntryInfo.ResourceJarEntryInfo(fullyQualifiedName, url))
|
||||
|
||||
override fun minusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable = MutableTestCorDapp(name, version, vendor, title, willResourceBeAddedToCorDapp, jarEntries - JarEntryInfo.ResourceJarEntryInfo(fullyQualifiedName, url))
|
||||
|
||||
override fun packageAsJarWithPath(jarFilePath: Path) = jarEntries.packageToCorDapp(jarFilePath, name, version, vendor, title, willResourceBeAddedToCorDapp)
|
||||
|
||||
override fun packageAsJarInDirectory(parentDirectory: Path): Path = (parentDirectory / defaultJarName()).also { packageAsJarWithPath(it) }
|
||||
|
||||
private fun defaultJarName(): String = "${name}_$version$jarExtension".replace(whitespace, whitespaceReplacement)
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import com.typesafe.config.ConfigValueFactory
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.concurrent.fork
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
@ -17,11 +18,11 @@ import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.EnterpriseNode
|
||||
import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.services.config.*
|
||||
import net.corda.nodeapi.internal.config.toConfig
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
@ -41,10 +42,12 @@ import java.nio.file.Path
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
// TODO Some of the logic here duplicates what's in the driver
|
||||
// TODO Some of the logic here duplicates what's in the driver - the reason why it's not straightforward to replace it by using DriverDSLImpl in `init()` and `stopAllNodes()` is because of the platform version passed to nodes (driver doesn't support this, and it's a property of the Corda JAR)
|
||||
abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyList()) : IntegrationTest() {
|
||||
companion object {
|
||||
private val WHITESPACE = "\\s++".toRegex()
|
||||
|
||||
private val logger = loggerFor<NodeBasedTest>()
|
||||
}
|
||||
|
||||
@Rule
|
||||
@ -112,15 +115,24 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
|
||||
) + configOverrides
|
||||
)
|
||||
|
||||
val parsedConfig = config.parseAsNodeConfiguration().also { nodeConfiguration ->
|
||||
val cordapps = cordappsForPackages(getCallerPackage(NodeBasedTest::class)?.let { cordappPackages + it } ?: cordappPackages)
|
||||
|
||||
val existingCorDappDirectoriesOption = if (config.hasPath(NodeConfiguration.cordappDirectoriesKey)) config.getStringList(NodeConfiguration.cordappDirectoriesKey) else emptyList()
|
||||
|
||||
val cordappDirectories = existingCorDappDirectoriesOption + TestCordappDirectories.cached(cordapps).map { it.toString() }
|
||||
|
||||
val specificConfig = config.withValue(NodeConfiguration.cordappDirectoriesKey, ConfigValueFactory.fromIterable(cordappDirectories))
|
||||
|
||||
val parsedConfig = specificConfig.parseAsNodeConfiguration().also { nodeConfiguration ->
|
||||
val errors = nodeConfiguration.validate()
|
||||
if (errors.isNotEmpty()) {
|
||||
throw IllegalStateException("Invalid node configuration. Errors where:${System.lineSeparator()}${errors.joinToString(System.lineSeparator())}")
|
||||
}
|
||||
}
|
||||
|
||||
defaultNetworkParameters.install(baseDirectory)
|
||||
|
||||
return InProcessNode(parsedConfig, MOCK_VERSION_INFO.copy(platformVersion = platformVersion), cordappPackages)
|
||||
return InProcessNode(parsedConfig, MOCK_VERSION_INFO.copy(platformVersion = platformVersion))
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
@ -153,8 +165,7 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
|
||||
}
|
||||
}
|
||||
|
||||
class InProcessNode(
|
||||
configuration: NodeConfiguration, versionInfo: VersionInfo, cordappPackages: List<String>) : EnterpriseNode(
|
||||
configuration, versionInfo, false, CordappLoader.createDefaultWithTestPackages(configuration, cordappPackages)) {
|
||||
class InProcessNode(configuration: NodeConfiguration, versionInfo: VersionInfo) : EnterpriseNode(configuration, versionInfo, false) {
|
||||
|
||||
override fun getRxIoScheduler() = CachedThreadScheduler(testThreadFactory()).also { runOnStop += it::shutdown }
|
||||
}
|
||||
|
@ -37,8 +37,10 @@ import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.MAX_MESSAGE_SIZE
|
||||
import net.corda.testing.driver.JmxPolicy
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.node.User
|
||||
import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages
|
||||
import org.apache.activemq.artemis.api.core.SimpleString
|
||||
import org.apache.activemq.artemis.api.core.TransportConfiguration
|
||||
import org.apache.activemq.artemis.api.core.client.ActiveMQClient
|
||||
@ -119,13 +121,13 @@ fun <A> rpcDriver(
|
||||
useTestClock: Boolean = false,
|
||||
startNodesInProcess: Boolean = false,
|
||||
waitForNodesToFinish: Boolean = false,
|
||||
extraCordappPackagesToScan: List<String> = emptyList(),
|
||||
notarySpecs: List<NotarySpec> = emptyList(),
|
||||
externalTrace: Trace? = null,
|
||||
jmxPolicy: JmxPolicy = JmxPolicy(),
|
||||
networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()),
|
||||
notaryCustomOverrides: Map<String, Any?> = emptyMap(),
|
||||
inMemoryDB: Boolean = true,
|
||||
cordappsForAllNodes: Set<TestCorDapp> = cordappsInCurrentAndAdditionalPackages(),
|
||||
dsl: RPCDriverDSL.() -> A
|
||||
): A {
|
||||
return genericDriver(
|
||||
@ -139,13 +141,13 @@ fun <A> rpcDriver(
|
||||
isDebug = isDebug,
|
||||
startNodesInProcess = startNodesInProcess,
|
||||
waitForAllNodesToFinish = waitForNodesToFinish,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
notarySpecs = notarySpecs,
|
||||
jmxPolicy = jmxPolicy,
|
||||
compatibilityZone = null,
|
||||
networkParameters = networkParameters,
|
||||
notaryCustomOverrides = notaryCustomOverrides,
|
||||
inMemoryDB = inMemoryDB
|
||||
inMemoryDB = inMemoryDB,
|
||||
cordappsForAllNodes = cordappsForAllNodes
|
||||
), externalTrace
|
||||
),
|
||||
coerce = { it },
|
||||
|
@ -0,0 +1,70 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.deleteRecursively
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ConcurrentMap
|
||||
|
||||
internal object TestCordappDirectories {
|
||||
|
||||
private val logger = loggerFor<TestCordappDirectories>()
|
||||
|
||||
private const val whitespace = " "
|
||||
private const val whitespaceReplacement = "_"
|
||||
|
||||
private val cordappsCache: ConcurrentMap<List<String>, Path> = ConcurrentHashMap<List<String>, Path>()
|
||||
|
||||
internal fun cached(cordapps: Iterable<TestCorDapp>, replaceExistingOnes: Boolean = false, cordappsDirectory: Path = defaultCordappsDirectory): Iterable<Path> {
|
||||
|
||||
cordappsDirectory.toFile().deleteOnExit()
|
||||
return cordapps.map { cached(it, replaceExistingOnes, cordappsDirectory) }
|
||||
}
|
||||
|
||||
internal fun cached(cordapp: TestCorDapp, replaceExistingOnes: Boolean = false, cordappsDirectory: Path = defaultCordappsDirectory): Path {
|
||||
|
||||
cordappsDirectory.toFile().deleteOnExit()
|
||||
val cacheKey = cordapp.resources.map { it.toExternalForm() }.sorted()
|
||||
return if (replaceExistingOnes) {
|
||||
cordappsCache.remove(cacheKey)
|
||||
cordappsCache.getOrPut(cacheKey) {
|
||||
|
||||
val cordappDirectory = (cordappsDirectory / "${cordapp.name}_${cordapp.version}".replace(whitespace, whitespaceReplacement)).toAbsolutePath()
|
||||
cordappDirectory.createDirectories()
|
||||
cordapp.packageAsJarInDirectory(cordappDirectory)
|
||||
cordappDirectory
|
||||
}
|
||||
} else {
|
||||
cordappsCache.getOrPut(cacheKey) {
|
||||
|
||||
val cordappDirectory = (cordappsDirectory / "${cordapp.name}_${cordapp.version}".replace(whitespace, whitespaceReplacement)).toAbsolutePath()
|
||||
cordappDirectory.createDirectories()
|
||||
cordapp.packageAsJarInDirectory(cordappDirectory)
|
||||
cordappDirectory
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun forPackages(packages: Iterable<String>, replaceExistingOnes: Boolean = false, cordappsDirectory: Path = defaultCordappsDirectory): Iterable<Path> {
|
||||
|
||||
cordappsDirectory.toFile().deleteOnExit()
|
||||
val cordapps = simplifyScanPackages(packages).distinct().fold(emptySet<TestCorDapp>()) { all, packageName -> all + testCorDapp(packageName) }
|
||||
return cached(cordapps, replaceExistingOnes, cordappsDirectory)
|
||||
}
|
||||
|
||||
private val defaultCordappsDirectory: Path by lazy {
|
||||
|
||||
val cordappsDirectory = (Paths.get("build") / "tmp" / getTimestampAsDirectoryName() / "generated-test-cordapps").toAbsolutePath()
|
||||
logger.info("Initialising generated test CorDapps directory in $cordappsDirectory")
|
||||
if (cordappsDirectory.exists()) {
|
||||
cordappsDirectory.deleteRecursively()
|
||||
}
|
||||
cordappsDirectory.createDirectories()
|
||||
cordappsDirectory
|
||||
}
|
||||
}
|
@ -0,0 +1,214 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.deleteIfExists
|
||||
import net.corda.core.internal.outputStream
|
||||
import net.corda.node.internal.cordapp.createTestManifest
|
||||
import net.corda.testing.driver.TestCorDapp
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.File
|
||||
import java.io.OutputStream
|
||||
import java.net.URI
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.attribute.FileTime
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.jar.JarOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Packages some [JarEntryInfo] into a CorDapp JAR with specified [path].
|
||||
* @param path The path of the JAR.
|
||||
* @param willResourceBeAddedBeToCorDapp A filter for the inclusion of [JarEntryInfo] in the JAR.
|
||||
*/
|
||||
internal fun Iterable<JarEntryInfo>.packageToCorDapp(path: Path, name: String, version: String, vendor: String, title: String = name, willResourceBeAddedBeToCorDapp: (String, URL) -> Boolean = { _, _ -> true }) {
|
||||
|
||||
var hasContent = false
|
||||
try {
|
||||
hasContent = packageToCorDapp(path.outputStream(), name, version, vendor, title, willResourceBeAddedBeToCorDapp)
|
||||
} finally {
|
||||
if (!hasContent) {
|
||||
path.deleteIfExists()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Packages some [JarEntryInfo] into a CorDapp JAR using specified [outputStream].
|
||||
* @param outputStream The [OutputStream] for the JAR.
|
||||
* @param willResourceBeAddedBeToCorDapp A filter for the inclusion of [JarEntryInfo] in the JAR.
|
||||
*/
|
||||
internal fun Iterable<JarEntryInfo>.packageToCorDapp(outputStream: OutputStream, name: String, version: String, vendor: String, title: String = name, willResourceBeAddedBeToCorDapp: (String, URL) -> Boolean = { _, _ -> true }): Boolean {
|
||||
|
||||
val manifest = createTestManifest(name, title, version, vendor)
|
||||
return JarOutputStream(outputStream, manifest).use { jos -> zip(jos, willResourceBeAddedBeToCorDapp) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a [Class] into a [JarEntryInfo].
|
||||
*/
|
||||
internal fun Class<*>.jarEntryInfo(): JarEntryInfo {
|
||||
|
||||
return JarEntryInfo.ClassJarEntryInfo(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Packages some [TestCorDapp]s under a root [directory], each with it's own JAR.
|
||||
* @param directory The parent directory in which CorDapp JAR will be created.
|
||||
*/
|
||||
fun Iterable<TestCorDapp>.packageInDirectory(directory: Path) {
|
||||
|
||||
directory.createDirectories()
|
||||
forEach { cordapp -> cordapp.packageAsJarInDirectory(directory) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all classes within the [targetPackage].
|
||||
*/
|
||||
fun allClassesForPackage(targetPackage: String): Set<Class<*>> {
|
||||
|
||||
val scanResult = FastClasspathScanner(targetPackage).strictWhitelist().scan()
|
||||
return scanResult.namesOfAllClasses.filter { className -> className.startsWith(targetPackage) }.map(scanResult::classNameToClassRef).toSet()
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps each package to a [TestCorDapp] with resources found in that package.
|
||||
*/
|
||||
fun cordappsForPackages(packages: Iterable<String>): Set<TestCorDapp> {
|
||||
|
||||
return simplifyScanPackages(packages).toSet().fold(emptySet()) { all, packageName -> all + testCorDapp(packageName) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps each package to a [TestCorDapp] with resources found in that package.
|
||||
*/
|
||||
fun cordappsForPackages(firstPackage: String, vararg otherPackages: String): Set<TestCorDapp> {
|
||||
|
||||
return cordappsForPackages(setOf(*otherPackages) + firstPackage)
|
||||
}
|
||||
|
||||
fun getCallerClass(directCallerClass: KClass<*>): Class<*>? {
|
||||
|
||||
val stackTrace = Throwable().stackTrace
|
||||
val index = stackTrace.indexOfLast { it.className == directCallerClass.java.name }
|
||||
if (index == -1) return null
|
||||
return try {
|
||||
Class.forName(stackTrace[index + 1].className)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun getCallerPackage(directCallerClass: KClass<*>): String? {
|
||||
|
||||
return getCallerClass(directCallerClass)?.`package`?.name
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [TestCorDapp] containing resources found in [packageName].
|
||||
*/
|
||||
internal fun testCorDapp(packageName: String): TestCorDapp {
|
||||
|
||||
val uuid = UUID.randomUUID()
|
||||
val name = "$packageName-$uuid"
|
||||
val version = "$uuid"
|
||||
return TestCorDapp.Factory.create(name, version).plusPackage(packageName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Squashes child packages if the parent is present. Example: ["com.foo", "com.foo.bar"] into just ["com.foo"].
|
||||
*/
|
||||
fun simplifyScanPackages(scanPackages: Iterable<String>): List<String> {
|
||||
|
||||
return scanPackages.sorted().fold(emptyList()) { listSoFar, packageName ->
|
||||
when {
|
||||
listSoFar.isEmpty() -> listOf(packageName)
|
||||
packageName.startsWith(listSoFar.last()) -> listSoFar
|
||||
else -> listSoFar + packageName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a class or package name into a path segment.
|
||||
*/
|
||||
internal fun String.packageToPath() = replace(".", File.separator)
|
||||
|
||||
private fun Iterable<JarEntryInfo>.zip(outputStream: ZipOutputStream, willResourceBeAddedBeToCorDapp: (String, URL) -> Boolean): Boolean {
|
||||
|
||||
val entries = filter { (fullyQualifiedName, url) -> willResourceBeAddedBeToCorDapp(fullyQualifiedName, url) }
|
||||
if (entries.isNotEmpty()) {
|
||||
zip(outputStream, entries)
|
||||
}
|
||||
return entries.isNotEmpty()
|
||||
}
|
||||
|
||||
private fun zip(outputStream: ZipOutputStream, allInfo: Iterable<JarEntryInfo>) {
|
||||
|
||||
val time = FileTime.from(Instant.EPOCH)
|
||||
val classLoader = Thread.currentThread().contextClassLoader
|
||||
allInfo.distinctBy { it.url }.sortedBy { it.url.toExternalForm() }.forEach { info ->
|
||||
|
||||
try {
|
||||
val entry = ZipEntry(info.entryName).setCreationTime(time).setLastAccessTime(time).setLastModifiedTime(time)
|
||||
outputStream.putNextEntry(entry)
|
||||
classLoader.getResourceAsStream(info.entryName).use {
|
||||
IOUtils.copy(it, outputStream)
|
||||
}
|
||||
} finally {
|
||||
outputStream.closeEntry()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single resource to be added to a CorDapp JAR.
|
||||
*/
|
||||
internal sealed class JarEntryInfo(val fullyQualifiedName: String, val url: URL) {
|
||||
|
||||
abstract val entryName: String
|
||||
|
||||
/**
|
||||
* Represents a class to be added to a CorDapp JAR.
|
||||
*/
|
||||
class ClassJarEntryInfo(val clazz: Class<*>) : JarEntryInfo(clazz.name, clazz.classFileURL()) {
|
||||
|
||||
override val entryName = "${fullyQualifiedName.packageToPath()}$fileExtensionSeparator$classFileExtension"
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a resource file to be added to a CorDapp JAR.
|
||||
*/
|
||||
class ResourceJarEntryInfo(fullyQualifiedName: String, url: URL) : JarEntryInfo(fullyQualifiedName, url) {
|
||||
|
||||
override val entryName: String
|
||||
get() {
|
||||
val extensionIndex = fullyQualifiedName.lastIndexOf(fileExtensionSeparator)
|
||||
return "${fullyQualifiedName.substring(0 until extensionIndex).packageToPath()}${fullyQualifiedName.substring(extensionIndex)}"
|
||||
}
|
||||
}
|
||||
|
||||
operator fun component1(): String = fullyQualifiedName
|
||||
|
||||
operator fun component2(): URL = url
|
||||
|
||||
private companion object {
|
||||
|
||||
private const val classFileExtension = "class"
|
||||
private const val fileExtensionSeparator = "."
|
||||
private const val whitespace = " "
|
||||
private const val whitespaceReplacement = "%20"
|
||||
|
||||
private fun Class<*>.classFileURL(): URL {
|
||||
|
||||
require(protectionDomain?.codeSource?.location != null) { "Invalid class $name for test CorDapp. Classes without protection domain cannot be referenced. This typically happens for Java / Kotlin types." }
|
||||
return URI.create("${protectionDomain.codeSource.location}/${name.packageToPath()}$fileExtensionSeparator$classFileExtension".escaped()).toURL()
|
||||
}
|
||||
|
||||
private fun String.escaped(): String = this.replace(whitespace, whitespaceReplacement)
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.testing.driver.JmxPolicy
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages
|
||||
import net.corda.testing.node.internal.internalDriver
|
||||
|
||||
/**
|
||||
@ -69,12 +70,12 @@ class CordformNodeRunner(private val cordformDefinition: CordformDefinition) {
|
||||
internalDriver(
|
||||
jmxPolicy = JmxPolicy(true),
|
||||
driverDirectory = cordformDefinition.nodesDirectory,
|
||||
extraCordappPackagesToScan = extraPackagesToScan,
|
||||
// Notaries are manually specified in Cordform so we don't want the driver automatically starting any
|
||||
notarySpecs = emptyList(),
|
||||
// Start from after the largest port used to prevent port clash
|
||||
portAllocation = PortAllocation.Incremental(maxPort + 1),
|
||||
waitForAllNodesToFinish = waitForAllNodesToFinish
|
||||
waitForAllNodesToFinish = waitForAllNodesToFinish,
|
||||
cordappsForAllNodes = cordappsInCurrentAndAdditionalPackages(extraPackagesToScan)
|
||||
) {
|
||||
cordformDefinition.setup(this)
|
||||
startCordformNodes(nodes).getOrThrow() // Only proceed once everything is up and running
|
||||
|
@ -19,7 +19,7 @@ class InternalMockNetworkTests {
|
||||
fun `does not leak serialization env if init fails`() {
|
||||
val e = Exception("didn't work")
|
||||
assertThatThrownBy {
|
||||
object : InternalMockNetwork() {
|
||||
object : InternalMockNetwork(cordappsForAllNodes = emptySet()) {
|
||||
override fun createNotaries() = throw e
|
||||
}
|
||||
}.isSameAs(e)
|
||||
|
@ -17,7 +17,8 @@ import net.corda.core.internal.TEST_UPLOADER
|
||||
import net.corda.core.internal.cordapp.CordappImpl
|
||||
import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.core.node.services.AttachmentStorage
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||
import net.corda.testing.services.MockAttachmentStorage
|
||||
import java.nio.file.Paths
|
||||
@ -52,8 +53,7 @@ class MockCordappProvider(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getContractAttachmentID(contractClassName: ContractClassName): AttachmentId? = cordappRegistry.find { it.first.contractClassNames.contains(contractClassName) }?.second
|
||||
?: super.getContractAttachmentID(contractClassName)
|
||||
override fun getContractAttachmentID(contractClassName: ContractClassName): AttachmentId? = cordappRegistry.find { it.first.contractClassNames.contains(contractClassName) }?.second ?: super.getContractAttachmentID(contractClassName)
|
||||
|
||||
private fun findOrImportAttachment(contractClassNames: List<ContractClassName>, data: ByteArray, attachments: MockAttachmentStorage): AttachmentId {
|
||||
val existingAttachment = attachments.files.filter {
|
||||
|
@ -23,7 +23,7 @@ import net.corda.core.internal.div
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.node.internal.DataSourceFactory.createDatasourceFromDriverJarFolders
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.node.services.config.ConfigHelper
|
||||
import net.corda.node.services.config.configOf
|
||||
import net.corda.node.services.config.parseAsNodeConfiguration
|
||||
@ -133,7 +133,7 @@ private fun runCommand(options: OptionSet, parser: OptionParser) {
|
||||
errorAndExit("Not a valid node folder. Could not find the config file: '$config'.")
|
||||
}
|
||||
val nodeConfig = ConfigHelper.loadConfig(baseDirectory, config).parseAsNodeConfiguration()
|
||||
val cordappLoader = CordappLoader.createDefault(baseDirectory)
|
||||
val cordappLoader = JarScanningCordappLoader.fromDirectories(setOf(baseDirectory))
|
||||
|
||||
val schemaService = NodeSchemaService(extraSchemas = cordappLoader.cordappSchemas, includeNotarySchemas = nodeConfig.notary != null)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user