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:
Michele Sollecito 2018-07-23 15:22:39 +01:00
commit a4d65dae22
73 changed files with 1839 additions and 518 deletions

View File

@ -5825,6 +5825,8 @@ public interface net.corda.testing.driver.DriverDSL
@NotNull @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) 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 @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) public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.WebserverHandle> startWebserver(net.corda.testing.driver.NodeHandle)
@NotNull @NotNull
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.WebserverHandle> startWebserver(net.corda.testing.driver.NodeHandle, String) 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 final class net.corda.testing.driver.DriverParameters extends java.lang.Object
public <init>() 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)
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() public final boolean component1()
@NotNull @NotNull
public final java.util.List<String> component10() 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 final java.util.concurrent.atomic.AtomicInteger getPortCounter()
public int nextPort() 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 public final class net.corda.testing.driver.VerifierType extends java.lang.Enum
protected <init>() protected <init>()
public static net.corda.testing.driver.VerifierType valueOf(String) 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 @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>) 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 @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) public final net.corda.testing.node.StartedMockNode createNode(net.corda.testing.node.MockNodeParameters)
@NotNull @NotNull
public final net.corda.testing.node.StartedMockNode createPartyNode(net.corda.core.identity.CordaX500Name) 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 @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>) 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 @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) public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.testing.node.MockNodeParameters)
@NotNull @NotNull
public final java.util.List<String> getCordappPackages() 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>()
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, ?>)
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.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 @Nullable
public final Integer component1() public final Integer component1()
@Nullable @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, ?>) 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 @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>) 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) public boolean equals(Object)
@NotNull @NotNull
public final kotlin.jvm.functions.Function1<net.corda.node.services.config.NodeConfiguration, Object> getConfigOverrides() 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 class net.corda.testing.node.MockServices extends java.lang.Object implements net.corda.core.node.ServiceHub
public <init>() public <init>()
public <init>(java.util.List<String>) public <init>(Iterable<String>)
public <init>(java.util.List<String>, net.corda.core.identity.CordaX500Name) public <init>(Iterable<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>(Iterable<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>(Iterable<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>(Iterable<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>(Iterable<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>(Iterable<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>, net.corda.testing.core.TestIdentity, java.security.KeyPair...)
public <init>(net.corda.core.identity.CordaX500Name) 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, java.security.KeyPair, java.security.KeyPair...)
public <init>(net.corda.core.identity.CordaX500Name, net.corda.core.node.services.IdentityService) public <init>(net.corda.core.identity.CordaX500Name, net.corda.core.node.services.IdentityService)

View File

@ -3,9 +3,9 @@
# PR Checklist: # PR Checklist:
- [ ] Have you run the unit, integration and smoke tests as described here? https://docs.corda.net/head/testing.html - [ ] 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 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, and potentially release notes? - [ ] 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 now and add to this Pull Request that you agree to it. - [ ] 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! :) Thanks for your code, it's appreciated! :)

View File

@ -185,7 +185,7 @@ allprojects {
jvmTarget = "1.8" jvmTarget = "1.8"
javaParameters = true // Useful for reflection. javaParameters = true // Useful for reflection.
freeCompilerArgs = ['-Xjvm-default=compatibility'] 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) { 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. // Prevent the project from creating temporary files outside of the build directory.
systemProperty 'java.io.tmpdir', buildDir.absolutePath systemProperty 'java.io.tmpdir', buildDir.absolutePath

View File

@ -29,6 +29,7 @@ import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.CHARLIE_NAME import net.corda.testing.core.CHARLIE_NAME
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.cordappsForPackages
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -44,7 +45,7 @@ class IdentitySyncFlowTests {
fun before() { fun before() {
// We run this in parallel threads to help catch any race conditions that may exist. // We run this in parallel threads to help catch any race conditions that may exist.
mockNet = InternalMockNetwork( 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, networkSendManuallyPumped = false,
threadPerNode = true threadPerNode = true
) )

View File

@ -137,7 +137,7 @@ class AttachmentTests : WithMockNet {
// Makes a node that doesn't do sanity checking at load time. // Makes a node that doesn't do sanity checking at load time.
private fun makeBadNode(name: CordaX500Name) = mockNet.createNode( private fun makeBadNode(name: CordaX500Name) = mockNet.createNode(
InternalMockNodeParameters(legalName = randomise(name)), InternalMockNodeParameters(legalName = randomise(name)),
nodeFactory = { args -> nodeFactory = { args, _ ->
object : InternalMockNetwork.MockNode(args) { object : InternalMockNetwork.MockNode(args) {
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false } override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false }
} }

View File

@ -30,6 +30,7 @@ import net.corda.testing.core.*
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.cordappsForPackages
import org.junit.AfterClass import org.junit.AfterClass
import org.junit.Test import org.junit.Test
@ -37,7 +38,7 @@ class CollectSignaturesFlowTests : WithContracts {
companion object { companion object {
private val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) private val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB"))
private val miniCorpServices = MockServices(listOf("net.corda.testing.contracts"), miniCorp, rigorousMock()) 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 private const val MAGIC_NUMBER = 1337

View File

@ -30,10 +30,7 @@ import org.junit.Test
class ContractUpgradeFlowRPCTest : WithContracts, WithFinality { class ContractUpgradeFlowRPCTest : WithContracts, WithFinality {
companion object { companion object {
private val classMockNet = InternalMockNetwork(cordappPackages = listOf( private val classMockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows"))
"net.corda.testing.contracts",
"net.corda.finance.contracts.asset",
"net.corda.core.flows"))
@JvmStatic @JvmStatic
@AfterClass @AfterClass
@ -99,7 +96,7 @@ class ContractUpgradeFlowRPCTest : WithContracts, WithFinality {
).get() ).get()
} }
private fun RPCDriverDSL.createTestUser() = rpcTestUser.copy(permissions = setOf( private fun createTestUser() = rpcTestUser.copy(permissions = setOf(
startFlow<WithFinality.FinalityInvoker>(), startFlow<WithFinality.FinalityInvoker>(),
startFlow<ContractUpgradeFlow.Initiate<*, *>>(), startFlow<ContractUpgradeFlow.Initiate<*, *>>(),
startFlow<ContractUpgradeFlow.Authorise>(), startFlow<ContractUpgradeFlow.Authorise>(),

View File

@ -35,6 +35,7 @@ import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.cordappsForPackages
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
import org.junit.AfterClass import org.junit.AfterClass
import org.junit.Test import org.junit.Test
@ -42,10 +43,7 @@ import java.util.*
class ContractUpgradeFlowTest : WithContracts, WithFinality { class ContractUpgradeFlowTest : WithContracts, WithFinality {
companion object { companion object {
private val classMockNet = InternalMockNetwork(cordappPackages = listOf( private val classMockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows"))
"net.corda.testing.contracts",
"net.corda.finance.contracts.asset",
"net.corda.core.flows"))
@JvmStatic @JvmStatic
@AfterClass @AfterClass

View File

@ -24,13 +24,14 @@ import net.corda.finance.issuedBy
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.testing.core.* import net.corda.testing.core.*
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.cordappsForPackages
import org.junit.AfterClass import org.junit.AfterClass
import org.junit.Test import org.junit.Test
class FinalityFlowTests : WithFinality { class FinalityFlowTests : WithFinality {
companion object { companion object {
private val CHARLIE = TestIdentity(CHARLIE_NAME, 90).party 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 @JvmStatic
@AfterClass @AfterClass

View File

@ -171,7 +171,7 @@ class AttachmentSerializationTest {
} }
private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String { private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String {
client = mockNet.restartNode(client) { args -> client = mockNet.restartNode(client) { args, _ ->
object : InternalMockNetwork.MockNode(args) { object : InternalMockNetwork.MockNode(args) {
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad } override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
} }

View File

@ -6,6 +6,7 @@ release, see :doc:`upgrade-notes`.
Unreleased 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 * ``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`` don't provide any guarantee the returned port will be available which can result in flaky tests. Use ``PortAllocation.Incremental``

View File

@ -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 .. note:: Before installing a CorDapp, you must create one or more nodes to install it on. For instructions, please see
:doc:`generating-a-node`. :doc:`generating-a-node`.
At runtime, nodes will load any CorDapps present in their ``cordapps`` folder. Therefore in order to install a CorDapp on 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 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. the node's JAR and configuration files are stored) and the node restarted.
CorDapp configuration files CorDapp configuration files
--------------------------- ---------------------------

View File

@ -317,7 +317,7 @@ Assuming all went well, you can view the newly-created IOU by accessing the vaul
*Via web/example:* *Via web/example:*
* PartyA: Navigate to http://localhost:10009/web/example and hit the "refresh" button * 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 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. not involved in this transaction.

View File

@ -12,6 +12,7 @@ import net.corda.finance.contracts.asset.cash.selection.AbstractCashSelection
import net.corda.finance.contracts.getCashBalance import net.corda.finance.contracts.getCashBalance
import net.corda.finance.issuedBy import net.corda.finance.issuedBy
import net.corda.testing.core.singleIdentity 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
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
@ -19,7 +20,7 @@ import org.junit.After
import org.junit.Test import org.junit.Test
class CashSelectionTest { 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 @After
fun cleanUp() { fun cleanUp() {

View File

@ -21,18 +21,25 @@ import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.cordapp.CordappLoader
import net.corda.node.internal.cordapp.CordappProviderImpl import net.corda.node.internal.cordapp.CordappProviderImpl
import net.corda.node.internal.cordapp.JarScanningCordappLoader
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity import net.corda.testing.core.TestIdentity
import net.corda.testing.internal.rigorousMock
import net.corda.testing.internal.MockCordappConfigProvider 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 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.Rule
import org.junit.Test import org.junit.Test
import java.nio.file.Path
import java.nio.file.Paths
class AttachmentsClassLoaderStaticContractTests { class AttachmentsClassLoaderStaticContractTests {
private companion object { private companion object {
@ -70,7 +77,7 @@ class AttachmentsClassLoaderStaticContractTests {
} }
private val serviceHub = rigorousMock<ServicesForResolution>().also { 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 doReturn(testNetworkParameters()).whenever(it).networkParameters
} }
@ -92,4 +99,18 @@ class AttachmentsClassLoaderStaticContractTests {
assertNotNull(contract) 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())
}
} }

View File

@ -144,6 +144,7 @@ dependencies {
testCompile project(':test-utils') testCompile project(':test-utils')
testCompile project(':client:jfx') testCompile project(':client:jfx')
testCompile project(':finance') testCompile project(':finance')
testCompile project(':finance:isolated')
// sample test schemas // sample test schemas
testCompile project(path: ':finance', configuration: 'testArtifacts') testCompile project(path: ':finance', configuration: 'testArtifacts')

View File

@ -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()
}
}
}
}
}

View File

@ -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()
}
}
}

View File

@ -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")
}
}

View File

@ -58,7 +58,7 @@ class AttachmentLoadingTests : IntegrationTest() {
@JvmField @JvmField
val testSerialization = SerializationEnvironmentRule() val testSerialization = SerializationEnvironmentRule()
private val attachments = MockAttachmentStorage() 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 cordapp get() = provider.cordapps.first()
private val attachmentId get() = provider.getCordappAttachmentId(cordapp)!! private val attachmentId get() = provider.getCordappAttachmentId(cordapp)!!
private val appContext get() = provider.getAppContext(cordapp) private val appContext get() = provider.getAppContext(cordapp)
@ -86,13 +86,6 @@ class AttachmentLoadingTests : IntegrationTest() {
startNode(providedName = bankBName) startNode(providedName = bankBName)
).transpose().getOrThrow() ).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 { private val services = object : ServicesForResolution {
@ -125,9 +118,9 @@ class AttachmentLoadingTests : IntegrationTest() {
@Test @Test
fun `test that attachments retrieved over the network are not used for code`() { fun `test that attachments retrieved over the network are not used for code`() {
withoutTestSerialization { withoutTestSerialization {
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) { driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList(), cordappsForAllNodes = emptySet())) {
installIsolatedCordappTo(bankAName) val bankA = startNode(providedName = bankAName, additionalCordapps = cordappsForPackages("net.corda.finance.contracts.isolated")).getOrThrow()
val (bankA, bankB) = createTwoNodes() 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") { 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() bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
} }
@ -135,16 +128,4 @@ class AttachmentLoadingTests : IntegrationTest() {
Unit 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()
}
}
}
}

View File

@ -16,11 +16,7 @@ import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TimeWindow import net.corda.core.contracts.TimeWindow
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.*
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.flows.NotaryError import net.corda.core.flows.NotaryError
import net.corda.core.flows.NotaryException import net.corda.core.flows.NotaryException
import net.corda.core.flows.NotaryFlow 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.node.TestClock
import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas 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
import net.corda.testing.node.internal.InternalMockNetwork.MockNode import net.corda.testing.node.internal.InternalMockNetwork.MockNode
import net.corda.testing.node.internal.InternalMockNodeParameters import net.corda.testing.node.internal.InternalMockNodeParameters
@ -63,18 +60,8 @@ import java.nio.file.Paths
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
import kotlin.collections.List
import kotlin.collections.component1 import kotlin.collections.component1
import kotlin.collections.component2 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.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -94,7 +81,7 @@ class BFTNotaryServiceTests {
@JvmStatic @JvmStatic
fun before() { fun before() {
IntegrationTest.globalSetUp() //Enterprise only - remote db setup 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 clusterSize = minClusterSize(1)
val started = startBftClusterAndNode(clusterSize, mockNet) val started = startBftClusterAndNode(clusterSize, mockNet)
notary = started.first notary = started.first

View File

@ -14,6 +14,7 @@ import net.corda.testing.core.dummyCommand
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas 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
import net.corda.testing.node.internal.InternalMockNetwork.MockNode import net.corda.testing.node.internal.InternalMockNetwork.MockNode
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
@ -34,7 +35,7 @@ class BFTSMaRtTests : IntegrationTest() {
@Before @Before
fun before() { fun before() {
mockNet = InternalMockNetwork(listOf("net.corda.testing.contracts")) mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts"))
} }
@After @After

View File

@ -32,6 +32,7 @@ import net.corda.testing.internal.DEV_ROOT_CA
import net.corda.testing.internal.IntegrationTest import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.node.NotarySpec 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.SharedCompatibilityZoneParams
import net.corda.testing.node.internal.internalDriver import net.corda.testing.node.internal.internalDriver
import net.corda.testing.node.internal.network.NetworkMapServer import net.corda.testing.node.internal.network.NetworkMapServer
@ -100,7 +101,7 @@ class NodeRegistrationTest : IntegrationTest() {
compatibilityZone = compatibilityZone, compatibilityZone = compatibilityZone,
initialiseSerialization = false, initialiseSerialization = false,
notarySpecs = listOf(NotarySpec(notaryName)), notarySpecs = listOf(NotarySpec(notaryName)),
extraCordappPackagesToScan = listOf("net.corda.finance"), cordappsForAllNodes = cordappsInCurrentAndAdditionalPackages("net.corda.finance"),
notaryCustomOverrides = mapOf("devMode" to false) notaryCustomOverrides = mapOf("devMode" to false)
) { ) {
val (alice, genevieve) = listOf( val (alice, genevieve) = listOf(

View File

@ -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)
}
}

View 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>
}

View File

@ -67,10 +67,11 @@ import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.minutes import net.corda.core.utilities.minutes
import net.corda.node.CordaClock import net.corda.node.CordaClock
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.cordapp.CordappLoader
import net.corda.node.internal.CheckpointVerifier.verifyCheckpointsCompatible import net.corda.node.internal.CheckpointVerifier.verifyCheckpointsCompatible
import net.corda.node.internal.classloading.requireAnnotation import net.corda.node.internal.classloading.requireAnnotation
import net.corda.node.internal.cordapp.CordappConfigFileProvider 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.CordappProviderImpl
import net.corda.node.internal.cordapp.CordappProviderInternal import net.corda.node.internal.cordapp.CordappProviderInternal
import net.corda.node.internal.rpc.proxies.AuthenticatedRpcOpsProxy import net.corda.node.internal.rpc.proxies.AuthenticatedRpcOpsProxy

View File

@ -22,7 +22,6 @@ import net.corda.core.internal.Emoji
import net.corda.core.internal.concurrent.thenMatch import net.corda.core.internal.concurrent.thenMatch
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.VersionInfo 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.NodeConfiguration
import net.corda.node.services.config.RelayConfiguration import net.corda.node.services.config.RelayConfiguration
import net.corda.node.services.statemachine.MultiThreadedStateMachineExecutor import net.corda.node.services.statemachine.MultiThreadedStateMachineExecutor
@ -38,9 +37,8 @@ import java.util.concurrent.TimeUnit
open class EnterpriseNode(configuration: NodeConfiguration, open class EnterpriseNode(configuration: NodeConfiguration,
versionInfo: VersionInfo, versionInfo: VersionInfo,
initialiseSerialization: Boolean = true, initialiseSerialization: Boolean = true
cordappLoader: CordappLoader = makeCordappLoader(configuration, versionInfo) ) : Node(configuration, versionInfo, initialiseSerialization) {
) : Node(configuration, versionInfo, initialiseSerialization, cordappLoader) {
companion object { companion object {
private val logger by lazy { loggerFor<EnterpriseNode>() } private val logger by lazy { loggerFor<EnterpriseNode>() }

View File

@ -35,8 +35,9 @@ import net.corda.node.SimpleClock
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.internal.artemis.ArtemisBroker import net.corda.node.internal.artemis.ArtemisBroker
import net.corda.node.internal.artemis.BrokerAddresses 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.core.internal.errors.AddressBindingException
import net.corda.node.cordapp.CordappLoader
import net.corda.node.internal.security.RPCSecurityManagerImpl import net.corda.node.internal.security.RPCSecurityManagerImpl
import net.corda.node.internal.security.RPCSecurityManagerWithAdditionalUser import net.corda.node.internal.security.RPCSecurityManagerWithAdditionalUser
import net.corda.node.serialization.amqp.AMQPServerSerializationScheme import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
@ -124,15 +125,11 @@ open class Node(configuration: NodeConfiguration,
} }
private val sameVmNodeCounter = AtomicInteger() private val sameVmNodeCounter = AtomicInteger()
const val scanPackagesSystemProperty = "net.corda.node.cordapp.scan.packages"
const val scanPackagesSeparator = ","
@JvmStatic @JvmStatic
protected fun makeCordappLoader(configuration: NodeConfiguration, versionInfo: VersionInfo): CordappLoader { protected fun makeCordappLoader(configuration: NodeConfiguration, versionInfo: VersionInfo): CordappLoader {
return System.getProperty(scanPackagesSystemProperty)?.let { scanPackages -> return JarScanningCordappLoader.fromDirectories(configuration.cordappDirectories, versionInfo)
CordappLoader.createDefaultWithTestPackages(configuration, scanPackages.split(scanPackagesSeparator), versionInfo)
} ?: CordappLoader.createDefault(configuration.baseDirectory, versionInfo)
} }
// TODO: make this configurable. // TODO: make this configurable.
const val MAX_RPC_MESSAGE_SIZE = 10485760 const val MAX_RPC_MESSAGE_SIZE = 10485760

View File

@ -23,6 +23,7 @@ import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.cordapp.CordappLoader
import java.net.URL import java.net.URL
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap

View File

@ -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 package net.corda.node.internal.cordapp
import com.github.benmanes.caffeine.cache.Caffeine
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult
import net.corda.core.cordapp.Cordapp 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.crypto.sha256
import net.corda.core.flows.* import net.corda.core.flows.*
import net.corda.core.internal.* 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.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.core.node.services.CordaService
import net.corda.node.VersionInfo
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.serialization.SerializationCustomSerializer import net.corda.core.serialization.SerializationCustomSerializer
import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializationWhitelist
import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SerializeAsToken
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.node.VersionInfo import net.corda.node.cordapp.CordappLoader
import net.corda.node.internal.classloading.requireAnnotation import net.corda.node.internal.classloading.requireAnnotation
import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.internal.coreContractClasses import net.corda.nodeapi.internal.coreContractClasses
import net.corda.serialization.internal.DefaultWhitelist import net.corda.serialization.internal.DefaultWhitelist
import org.apache.commons.collections4.map.LRUMap import org.apache.commons.collections4.map.LRUMap
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
import java.net.JarURLConnection
import java.net.URL import java.net.URL
import java.net.URLClassLoader import java.net.URLClassLoader
import java.nio.file.Path 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.*
import java.util.concurrent.ConcurrentHashMap
import java.util.jar.JarInputStream import java.util.jar.JarInputStream
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.streams.toList import kotlin.streams.toList
@ -70,20 +34,11 @@ import kotlin.streams.toList
* *
* @property cordappJarPaths The classpath of cordapp JARs * @property cordappJarPaths The classpath of cordapp JARs
*/ */
class CordappLoader private constructor(private val cordappJarPaths: List<RestrictedURL>, versionInfo: VersionInfo) { class JarScanningCordappLoader private constructor(private val cordappJarPaths: List<RestrictedURL>, versionInfo: VersionInfo = VersionInfo.UNKNOWN) : CordappLoaderTemplate() {
val cordapps: List<Cordapp> by lazy { loadCordapps() + coreCordapp }
val appClassLoader: ClassLoader = URLClassLoader(cordappJarPaths.stream().map { it.url }.toTypedArray(), javaClass.classLoader)
// Create a map of the CorDapps that provide a Flow. If a flow is not in this map it is a Core flow. override val cordapps: List<Cordapp> by lazy { loadCordapps() + coreCordapp }
// It also checks that there is only one CorDapp containing that flow class
val flowCordappMap: Map<Class<out FlowLogic<*>>, Cordapp> by lazy { override val appClassLoader: ClassLoader = URLClassLoader(cordappJarPaths.stream().map { it.url }.toTypedArray(), javaClass.classLoader)
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
}
}
init { init {
if (cordappJarPaths.isEmpty()) { 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. */ /** A Cordapp representing the core package which is not scanned automatically. */
@VisibleForTesting @VisibleForTesting
@ -107,165 +102,36 @@ class CordappLoader private constructor(private val cordappJarPaths: List<Restri
serializationWhitelists = listOf(), serializationWhitelists = listOf(),
serializationCustomSerializers = listOf(), serializationCustomSerializers = listOf(),
customSchemas = setOf(), customSchemas = setOf(),
jarPath = ContractUpgradeFlow.javaClass.protectionDomain.codeSource.location, // Core JAR location
info = CordappImpl.Info("corda-core", versionInfo.vendor, versionInfo.releaseVersion), info = CordappImpl.Info("corda-core", versionInfo.vendor, versionInfo.releaseVersion),
allFlows = listOf(), allFlows = listOf(),
jarPath = ContractUpgradeFlow.javaClass.location, // Core JAR location
jarHash = SecureHash.allOnesHash jarHash = SecureHash.allOnesHash
) )
companion object { private fun loadCordapps(): List<Cordapp> {
private val logger = contextLogger() return cordappJarPaths.map { scanCordapp(it).toCordapp(it) }
/**
* 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> { private fun RestrictedScanResult.toCordapp(url: RestrictedURL): Cordapp {
return cordappJarPaths.map {
val url = it.url val name = url.url.toPath().fileName.toString().removeSuffix(".jar")
val name = url.toPath().fileName.toString().removeSuffix(".jar") val info = url.url.openStream().let(::JarInputStream).use { it.manifest }.toCordappInfo(name)
val info = url.openStream().let(::JarInputStream).use { it.manifest }.toCordappInfo(name) return CordappImpl(
val scanResult = scanCordapp(it) findContractClassNames(this),
CordappImpl(contractClassNames = findContractClassNames(scanResult), findInitiatedFlows(this),
initiatedFlows = findInitiatedFlows(scanResult), findRPCFlows(this),
rpcFlows = findRPCFlows(scanResult), findServiceFlows(this),
serviceFlows = findServiceFlows(scanResult), findSchedulableFlows(this),
schedulableFlows = findSchedulableFlows(scanResult), findServices(this),
services = findServices(scanResult), findPlugins(url),
serializationWhitelists = findPlugins(it), findSerializers(this),
serializationCustomSerializers = findSerializers(scanResult), findCustomSchemas(this),
customSchemas = findCustomSchemas(scanResult), findAllFlows(this),
allFlows = findAllFlows(scanResult), url.url,
jarPath = it.url, info,
info = info, getJarHash(url.url),
jarHash = getJarHash(it.url), name
name = name )
)
}
} }
private fun getJarHash(url: URL): SecureHash.SHA256 = url.openStream().readFully().sha256() 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 val cachedScanResult = LRUMap<RestrictedURL, RestrictedScanResult>(1000)
private fun scanCordapp(cordappJarPath: RestrictedURL): RestrictedScanResult { private fun scanCordapp(cordappJarPath: RestrictedURL): RestrictedScanResult {
logger.info("Scanning CorDapp in ${cordappJarPath.url}") 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) RestrictedScanResult(FastClasspathScanner().addClassLoader(appClassLoader).overrideClasspath(cordappJarPath.url).scan(), cordappJarPath.qualifiedNamePrefix)
}) }
} }
private class FlowTypeHierarchyComparator(val initiatingFlow: Class<out FlowLogic<*>>) : Comparator<Class<out FlowLogic<*>>> { 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. * Thrown when scanning CorDapps.
*/ */
class MultipleCordappsForFlowException(message: String) : Exception(message) 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)
}
}

View File

@ -16,10 +16,9 @@ import java.util.*
import java.util.jar.Attributes import java.util.jar.Attributes
import java.util.jar.Manifest 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 manifest = Manifest()
val version = "test-$jarUUID"
val vendor = "R3"
// Mandatory manifest attribute. If not present, all other entries are silently skipped. // Mandatory manifest attribute. If not present, all other entries are silently skipped.
manifest.mainAttributes[Attributes.Name.MANIFEST_VERSION] = "1.0" manifest.mainAttributes[Attributes.Name.MANIFEST_VERSION] = "1.0"
@ -37,11 +36,18 @@ internal fun createTestManifest(name: String, title: String, jarUUID: UUID): Man
return manifest 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) mainAttributes.putValue(key, value)
} }
internal fun Manifest?.toCordappInfo(defaultShortName: String): Cordapp.Info { internal fun Manifest?.toCordappInfo(defaultShortName: String): Cordapp.Info {
var unknown = CordappImpl.Info.UNKNOWN var unknown = CordappImpl.Info.UNKNOWN
(this?.mainAttributes?.getValue("Name") ?: defaultShortName).let { shortName -> (this?.mainAttributes?.getValue("Name") ?: defaultShortName).let { shortName ->
unknown = unknown.copy(shortName = shortName) unknown = unknown.copy(shortName = shortName)

View File

@ -15,6 +15,7 @@ import com.typesafe.config.ConfigException
import net.corda.core.context.AuthServiceId import net.corda.core.context.AuthServiceId
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.TimedFlow import net.corda.core.internal.TimedFlow
import net.corda.core.internal.div
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.seconds 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_PERIOD_MILLIS: Duration = Duration.ofMinutes(1)
private val DEFAULT_FLOW_MONITOR_SUSPENSION_LOGGING_THRESHOLD_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 { interface NodeConfiguration : NodeSSLConfiguration {
val myLegalName: CordaX500Name val myLegalName: CordaX500Name
@ -83,6 +85,7 @@ interface NodeConfiguration : NodeSSLConfiguration {
val effectiveH2Settings: NodeH2Settings? val effectiveH2Settings: NodeH2Settings?
val flowMonitorPeriodMillis: Duration get() = DEFAULT_FLOW_MONITOR_PERIOD_MILLIS val flowMonitorPeriodMillis: Duration get() = DEFAULT_FLOW_MONITOR_PERIOD_MILLIS
val flowMonitorSuspensionLoggingThresholdMillis: Duration get() = DEFAULT_FLOW_MONITOR_SUSPENSION_LOGGING_THRESHOLD_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> fun validate(): List<String>
@ -97,6 +100,8 @@ interface NodeConfiguration : NodeSSLConfiguration {
val defaultAttachmentContentCacheSize: Long = 10.MB val defaultAttachmentContentCacheSize: Long = 10.MB
const val defaultAttachmentCacheBound = 1024L const val defaultAttachmentCacheBound = 1024L
const val cordappDirectoriesKey = "cordappDirectories"
} }
} }
@ -262,7 +267,8 @@ data class NodeConfigurationImpl(
// do not use or remove (used by Capsule) // do not use or remove (used by Capsule)
private val jarDirs: List<String> = emptyList(), private val jarDirs: List<String> = emptyList(),
override val flowMonitorPeriodMillis: Duration = DEFAULT_FLOW_MONITOR_PERIOD_MILLIS, 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 { ) : NodeConfiguration {
companion object { companion object {
private val logger = loggerFor<NodeConfigurationImpl>() private val logger = loggerFor<NodeConfigurationImpl>()

View File

@ -0,0 +1 @@
key=value

View File

@ -49,6 +49,7 @@ import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.expect import net.corda.testing.core.expect
import net.corda.testing.core.expectEvents import net.corda.testing.core.expectEvents
import net.corda.testing.core.sequence 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
import net.corda.testing.node.internal.InternalMockNetwork.MockNode import net.corda.testing.node.internal.InternalMockNetwork.MockNode
import net.corda.testing.node.internal.InternalMockNodeParameters import net.corda.testing.node.internal.InternalMockNodeParameters
@ -91,7 +92,7 @@ class CordaRPCOpsImplTest {
@Before @Before
fun setup() { 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)) aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
rpc = aliceNode.rpcOps rpc = aliceNode.rpcOps
CURRENT_RPC_CONTEXT.set(RpcAuthContext(InvocationContext.rpc(testActor()), buildSubject("TEST_USER", emptySet()))) CURRENT_RPC_CONTEXT.set(RpcAuthContext(InvocationContext.rpc(testActor()), buildSubject("TEST_USER", emptySet())))

View File

@ -15,6 +15,7 @@ import net.corda.core.node.services.CordaService
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.cordappsForPackages
import org.junit.After import org.junit.After
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
@ -27,7 +28,7 @@ class NodeUnloadHandlerTests {
val shutdownLatch = CountDownLatch(1) 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 @After
fun cleanUp() { fun cleanUp() {

View File

@ -85,7 +85,7 @@ class CordappProviderImplTests {
fun `test cordapp configuration`() { fun `test cordapp configuration`() {
val configProvider = MockCordappConfigProvider() val configProvider = MockCordappConfigProvider()
configProvider.cordappConfigs[isolatedCordappName] = validConfig configProvider.cordappConfigs[isolatedCordappName] = validConfig
val loader = CordappLoader.createDevMode(listOf(isolatedJAR)) val loader = JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR))
val provider = CordappProviderImpl(loader, configProvider, attachmentStore, whitelistedContractImplementations) val provider = CordappProviderImpl(loader, configProvider, attachmentStore, whitelistedContractImplementations)
val expected = provider.getAppContext(provider.cordapps.first()).config val expected = provider.getAppContext(provider.cordapps.first()).config
@ -94,7 +94,7 @@ class CordappProviderImplTests {
} }
private fun newCordappProvider(vararg urls: URL): CordappProviderImpl { 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) return CordappProviderImpl(loader, stubConfigProvider, attachmentStore, whitelistedContractImplementations)
} }
} }

View File

@ -12,8 +12,13 @@ package net.corda.node.internal.cordapp
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.* 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.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
@InitiatingFlow @InitiatingFlow
@ -40,7 +45,7 @@ class DummyRPCFlow : FlowLogic<Unit>() {
override fun call() = Unit override fun call() = Unit
} }
class CordappLoaderTest { class JarScanningCordappLoaderTest {
private companion object { private companion object {
const val testScanPackage = "net.corda.node.internal.cordapp" const val testScanPackage = "net.corda.node.internal.cordapp"
const val isolatedContractId = "net.corda.finance.contracts.isolated.AnotherDummyContract" const val isolatedContractId = "net.corda.finance.contracts.isolated.AnotherDummyContract"
@ -50,14 +55,14 @@ class CordappLoaderTest {
@Test @Test
fun `test that classes that aren't in cordapps aren't loaded`() { 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 // 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) assertThat(loader.cordapps).containsOnly(loader.coreCordapp)
} }
@Test @Test
fun `isolated JAR contains a CorDapp with a contract and plugin`() { fun `isolated JAR contains a CorDapp with a contract and plugin`() {
val isolatedJAR = CordappLoaderTest::class.java.getResource("isolated.jar")!! val isolatedJAR = JarScanningCordappLoaderTest::class.java.getResource("isolated.jar")!!
val loader = CordappLoader.createDevMode(listOf(isolatedJAR)) val loader = JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR))
val actual = loader.cordapps.toTypedArray() val actual = loader.cordapps.toTypedArray()
assertThat(actual).hasSize(2) assertThat(actual).hasSize(2)
@ -75,13 +80,11 @@ class CordappLoaderTest {
@Test @Test
fun `flows are loaded by loader`() { fun `flows are loaded by loader`() {
val loader = CordappLoader.createWithTestPackages(listOf(testScanPackage)) val loader = cordappLoaderForPackages(listOf(testScanPackage))
val actual = loader.cordapps.toTypedArray() val actual = loader.cordapps.toTypedArray()
// One core cordapp, one cordapp from this source tree, and two others due to identically named locations // One core cordapp, one cordapp from this source tree. In gradle it will also pick up the node jar.
// in resources and the non-test part of node. This is okay due to this being test code. In production this assertThat(actual.size == 2 || actual.size == 3).isTrue()
// cannot happen. In gradle it will also pick up the node jar.
assertThat(actual.size == 4 || actual.size == 5).isTrue()
val actualCordapp = actual.single { !it.initiatedFlows.isEmpty() } val actualCordapp = actual.single { !it.initiatedFlows.isEmpty() }
assertThat(actualCordapp.initiatedFlows).first().hasSameClassAs(DummyFlow::class.java) assertThat(actualCordapp.initiatedFlows).first().hasSameClassAs(DummyFlow::class.java)
@ -91,14 +94,14 @@ class CordappLoaderTest {
@Test @Test
fun `duplicate packages are ignored`() { 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 } val cordapps = loader.cordapps.filter { LoaderTestFlow::class.java in it.initiatedFlows }
assertThat(cordapps).hasSize(1) assertThat(cordapps).hasSize(1)
} }
@Test @Test
fun `sub-packages are ignored`() { 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 } val cordapps = loader.cordapps.filter { LoaderTestFlow::class.java in it.initiatedFlows }
assertThat(cordapps).hasSize(1) 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. // being used internally. Later iterations will use a classloader per cordapp and this test can be retired.
@Test @Test
fun `cordapp classloader can load cordapp classes`() { fun `cordapp classloader can load cordapp classes`() {
val isolatedJAR = CordappLoaderTest::class.java.getResource("isolated.jar")!! val isolatedJAR = JarScanningCordappLoaderTest::class.java.getResource("isolated.jar")!!
val loader = CordappLoader.createDevMode(listOf(isolatedJAR)) val loader = JarScanningCordappLoader.fromJarUrls(listOf(isolatedJAR))
loader.appClassLoader.loadClass(isolatedContractId) loader.appClassLoader.loadClass(isolatedContractId)
loader.appClassLoader.loadClass(isolatedFlowName) 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())
}
} }

View File

@ -60,6 +60,7 @@ import net.corda.testing.internal.rigorousMock
import net.corda.testing.internal.vault.VaultFiller import net.corda.testing.internal.vault.VaultFiller
import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.MockServices 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.InternalMockNetwork
import net.corda.testing.node.internal.InternalMockNodeParameters import net.corda.testing.node.internal.InternalMockNodeParameters
import net.corda.testing.node.internal.pumpReceive import net.corda.testing.node.internal.pumpReceive
@ -89,7 +90,7 @@ import kotlin.test.assertTrue
@RunWith(Parameterized::class) @RunWith(Parameterized::class)
class TwoPartyTradeFlowTests(private val anonymous: Boolean) { class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
companion object { companion object {
private val cordappPackages = listOf("net.corda.finance.contracts", "net.corda.finance.schemas") private val cordappPackages = setOf("net.corda.finance.contracts")
@JvmStatic @JvmStatic
@Parameterized.Parameters(name = "Anonymous = {0}") @Parameterized.Parameters(name = "Anonymous = {0}")
fun data(): Collection<Boolean> = listOf(true, false) 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 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 // we run in the unit test thread exclusively to speed things up, ensure deterministic results and
// allow interruption half way through. // allow interruption half way through.
mockNet = InternalMockNetwork(cordappPackages = cordappPackages, threadPerNode = true) mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(cordappPackages), threadPerNode = true)
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>() val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) { MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
val notaryNode = mockNet.defaultNotaryNode val notaryNode = mockNet.defaultNotaryNode
@ -169,7 +170,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
@Test(expected = InsufficientBalanceException::class) @Test(expected = InsufficientBalanceException::class)
fun `trade cash for commercial paper fails using soft locking`() { 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>() val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) { MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
val notaryNode = mockNet.defaultNotaryNode val notaryNode = mockNet.defaultNotaryNode
@ -227,7 +228,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
@Test @Test
fun `shutdown and restore`() { fun `shutdown and restore`() {
mockNet = InternalMockNetwork(cordappPackages = cordappPackages) mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(cordappPackages))
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>() val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) { MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
val notaryNode = mockNet.defaultNotaryNode val notaryNode = mockNet.defaultNotaryNode
@ -326,11 +327,20 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
// of gets and puts. // of gets and puts.
private fun makeNodeWithTracking(name: CordaX500Name): StartedNode<InternalMockNetwork.MockNode> { private fun makeNodeWithTracking(name: CordaX500Name): StartedNode<InternalMockNetwork.MockNode> {
// Create a node in the mock network ... // Create a node in the mock network ...
return mockNet.createNode(InternalMockNodeParameters(legalName = name), nodeFactory = { args -> return mockNet.createNode(InternalMockNodeParameters(legalName = name), nodeFactory = { args, cordappLoader ->
object : InternalMockNetwork.MockNode(args) { if (cordappLoader != null) {
// That constructs a recording tx storage object : InternalMockNetwork.MockNode(args, cordappLoader) {
override fun makeTransactionStorage(database: CordaPersistence, transactionCacheSizeBytes: Long): WritableTransactionStorage { // That constructs a recording tx storage
return RecordingTransactionStorage(database, super.makeTransactionStorage(database, transactionCacheSizeBytes)) 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 @Test
fun `check dependencies of sale asset are resolved`() { fun `check dependencies of sale asset are resolved`() {
mockNet = InternalMockNetwork(cordappPackages = cordappPackages) mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(cordappPackages))
val notaryNode = mockNet.defaultNotaryNode val notaryNode = mockNet.defaultNotaryNode
val aliceNode = makeNodeWithTracking(ALICE_NAME) val aliceNode = makeNodeWithTracking(ALICE_NAME)
val bobNode = makeNodeWithTracking(BOB_NAME) val bobNode = makeNodeWithTracking(BOB_NAME)
@ -442,7 +452,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
@Test @Test
fun `track works`() { fun `track works`() {
mockNet = InternalMockNetwork(cordappPackages = cordappPackages) mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(cordappPackages))
val notaryNode = mockNet.defaultNotaryNode val notaryNode = mockNet.defaultNotaryNode
val aliceNode = makeNodeWithTracking(ALICE_NAME) val aliceNode = makeNodeWithTracking(ALICE_NAME)
val bobNode = makeNodeWithTracking(BOB_NAME) val bobNode = makeNodeWithTracking(BOB_NAME)
@ -520,7 +530,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
@Test @Test
fun `dependency with error on buyer side`() { fun `dependency with error on buyer side`() {
mockNet = InternalMockNetwork(cordappPackages = cordappPackages) mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(cordappPackages))
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>() val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) { MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
runWithError(ledgerIdentityService, true, false, "at least one cash input") runWithError(ledgerIdentityService, true, false, "at least one cash input")
@ -529,7 +539,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
@Test @Test
fun `dependency with error on seller side`() { fun `dependency with error on seller side`() {
mockNet = InternalMockNetwork(cordappPackages = cordappPackages) mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(cordappPackages))
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>() val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) { MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
runWithError(ledgerIdentityService, false, true, "Issuances have a time-window") runWithError(ledgerIdentityService, false, true, "Issuances have a time-window")

View File

@ -11,11 +11,7 @@
package net.corda.node.modes.draining package net.corda.node.modes.draining
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.LinearState import net.corda.core.contracts.*
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.flows.FinalityFlow import net.corda.core.flows.FinalityFlow
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowLogicRefFactory 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.BOB_NAME
import net.corda.testing.core.dummyCommand import net.corda.testing.core.dummyCommand
import net.corda.testing.core.singleIdentity 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
import net.corda.testing.node.internal.InternalMockNodeParameters import net.corda.testing.node.internal.InternalMockNodeParameters
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
@ -61,7 +58,7 @@ class ScheduledFlowsDrainingModeTest {
@Before @Before
fun setup() { 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)) aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
bobNode = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME)) bobNode = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME))
notary = mockNet.defaultNotaryIdentity notary = mockNet.defaultNotaryIdentity

View File

@ -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.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.testing.driver.TestCorDapp
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.InternalMockNodeParameters import net.corda.testing.node.internal.InternalMockNodeParameters
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
@ -37,10 +38,8 @@ class FinalityHandlerTest {
// CorDapp. Bob's FinalityHandler will error when validating the tx. // CorDapp. Bob's FinalityHandler will error when validating the tx.
mockNet = InternalMockNetwork() mockNet = InternalMockNetwork()
val alice = mockNet.createNode(InternalMockNodeParameters( val assertCordapp = TestCorDapp.Factory.create("net.corda.finance.contracts.asset", "1.0").plusPackage("net.corda.finance.contracts.asset")
legalName = ALICE_NAME, val alice = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME, additionalCordapps = setOf(assertCordapp)))
extraCordappPackages = listOf("net.corda.finance.contracts.asset")
))
var bob = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME)) var bob = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME))

View File

@ -14,6 +14,7 @@ import net.corda.core.utilities.getOrThrow
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.issuedBy 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.InternalMockNetwork
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
@ -24,7 +25,7 @@ import java.util.concurrent.CountDownLatch
class ServiceHubConcurrentUsageTest { class ServiceHubConcurrentUsageTest {
private val mockNet = InternalMockNetwork(listOf(Cash::class.packageName)) private val mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(Cash::class.packageName))
@After @After
fun stopNodes() { fun stopNodes() {

View File

@ -48,6 +48,7 @@ import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.MockNetworkParameters import net.corda.testing.node.MockNetworkParameters
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.InternalMockNodeParameters import net.corda.testing.node.internal.InternalMockNodeParameters
import net.corda.testing.node.internal.cordappsForPackages
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
import org.junit.Before import org.junit.Before
import org.junit.ClassRule import org.junit.ClassRule
@ -108,8 +109,8 @@ class TimedFlowTestRule(val clusterSize: Int) : ExternalResource() {
override fun before() { override fun before() {
mockNet = InternalMockNetwork( mockNet = InternalMockNetwork(
listOf("net.corda.testing.contracts", "net.corda.node.services"), cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts", "net.corda.node.services"),
MockNetworkParameters().withServicePeerAllocationStrategy(InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin()), defaultParameters = MockNetworkParameters().withServicePeerAllocationStrategy(InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin()),
threadPerNode = true threadPerNode = true
) )
val started = startClusterAndNode(mockNet) val started = startClusterAndNode(mockNet)

View File

@ -33,6 +33,7 @@ import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.dummyCommand import net.corda.testing.core.dummyCommand
import net.corda.testing.core.singleIdentity 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
import net.corda.testing.node.internal.InternalMockNetwork.MockNode import net.corda.testing.node.internal.InternalMockNetwork.MockNode
import net.corda.testing.node.internal.InternalMockNodeParameters import net.corda.testing.node.internal.InternalMockNodeParameters
@ -111,7 +112,7 @@ class ScheduledFlowTests {
@Before @Before
fun setup() { 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)) aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
bobNode = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME)) bobNode = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME))
notary = mockNet.defaultNotaryIdentity notary = mockNet.defaultNotaryIdentity

View File

@ -25,6 +25,7 @@ import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.driver.internal.InProcessImpl import net.corda.testing.driver.internal.InProcessImpl
import net.corda.testing.internal.vault.DummyLinearStateSchemaV1 import net.corda.testing.internal.vault.DummyLinearStateSchemaV1
import net.corda.testing.node.internal.cordappsForPackages
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNetwork
import org.hibernate.annotations.Cascade import org.hibernate.annotations.Cascade
import org.hibernate.annotations.CascadeType import org.hibernate.annotations.CascadeType
@ -41,7 +42,7 @@ class NodeSchemaServiceTest {
*/ */
@Test @Test
fun `registering custom schemas for testing with MockNode`() { 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 mockNode = mockNet.createNode()
val schemaService = mockNode.services.schemaService val schemaService = mockNode.services.schemaService
assertTrue(schemaService.schemaOptions.containsKey(DummyLinearStateSchemaV1)) assertTrue(schemaService.schemaOptions.containsKey(DummyLinearStateSchemaV1))
@ -74,8 +75,6 @@ class NodeSchemaServiceTest {
/** /**
* Note: this test verifies auto-scanning to register identified [MappedSchema] schemas. * 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 @Test
fun `auto scanning of custom schemas for testing with Driver`() { fun `auto scanning of custom schemas for testing with Driver`() {

View File

@ -78,7 +78,7 @@ class FlowFrameworkTests {
@JvmStatic @JvmStatic
fun beforeClass() { fun beforeClass() {
mockNet = InternalMockNetwork( mockNet = InternalMockNetwork(
cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts"), cordappsForAllNodes = cordappsForPackages("net.corda.finance.contracts", "net.corda.testing.contracts"),
servicePeerAllocationStrategy = RoundRobin() servicePeerAllocationStrategy = RoundRobin()
) )
@ -491,7 +491,7 @@ class FlowFrameworkTripartyTests {
@JvmStatic @JvmStatic
fun beforeClass() { fun beforeClass() {
mockNet = InternalMockNetwork( mockNet = InternalMockNetwork(
cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts"), cordappsForAllNodes = cordappsForPackages("net.corda.finance.contracts", "net.corda.testing.contracts"),
servicePeerAllocationStrategy = RoundRobin() servicePeerAllocationStrategy = RoundRobin()
) )
@ -655,7 +655,7 @@ class FlowFrameworkPersistenceTests {
@Before @Before
fun start() { fun start() {
mockNet = InternalMockNetwork( mockNet = InternalMockNetwork(
cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts"), cordappsForAllNodes = cordappsForPackages("net.corda.finance.contracts", "net.corda.testing.contracts"),
servicePeerAllocationStrategy = RoundRobin() servicePeerAllocationStrategy = RoundRobin()
) )
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME)) aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))

View File

@ -14,6 +14,7 @@ import net.corda.core.utilities.seconds
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.node.services.config.FlowTimeoutConfiguration import net.corda.node.services.config.FlowTimeoutConfiguration
import net.corda.node.services.config.NodeConfiguration 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.InternalMockNetwork
import net.corda.testing.node.internal.InternalMockNodeParameters import net.corda.testing.node.internal.InternalMockNodeParameters
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
@ -37,7 +38,7 @@ class IdempotentFlowTests {
@Before @Before
fun start() { fun start() {
mockNet = InternalMockNetwork(threadPerNode = true, cordappPackages = listOf(this.javaClass.packageName)) mockNet = InternalMockNetwork(threadPerNode = true, cordappsForAllNodes = cordappsForPackages(this.javaClass.packageName))
nodeA = mockNet.createNode(InternalMockNodeParameters( nodeA = mockNet.createNode(InternalMockNodeParameters(
legalName = CordaX500Name("Alice", "AliceCorp", "GB"), legalName = CordaX500Name("Alice", "AliceCorp", "GB"),
configOverrides = { configOverrides = {

View File

@ -16,6 +16,7 @@ import net.corda.node.services.FinalityHandler
import net.corda.node.services.messaging.Message import net.corda.node.services.messaging.Message
import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.persistence.DBTransactionStorage
import net.corda.nodeapi.internal.persistence.contextTransaction 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
import net.corda.testing.node.internal.InternalMockNetwork.MockNode import net.corda.testing.node.internal.InternalMockNetwork.MockNode
import net.corda.testing.node.internal.MessagingServiceSpy import net.corda.testing.node.internal.MessagingServiceSpy
@ -42,7 +43,7 @@ class RetryFlowMockTest {
@Before @Before
fun start() { fun start() {
mockNet = InternalMockNetwork(threadPerNode = true, cordappPackages = listOf(this.javaClass.packageName)) mockNet = InternalMockNetwork(threadPerNode = true, cordappsForAllNodes = cordappsForPackages(this.javaClass.packageName))
nodeA = mockNet.createNode() nodeA = mockNet.createNode()
nodeB = mockNet.createNode() nodeB = mockNet.createNode()
mockNet.startNodes() mockNet.startNodes()

View File

@ -25,6 +25,7 @@ import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.testing.node.MockNetworkNotarySpec 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.InternalMockNetwork
import net.corda.testing.node.internal.InternalMockNodeParameters import net.corda.testing.node.internal.InternalMockNodeParameters
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
@ -43,7 +44,7 @@ class NotaryServiceTests {
@Before @Before
fun setup() { fun setup() {
mockNet = InternalMockNetwork( mockNet = InternalMockNetwork(
cordappPackages = listOf("net.corda.testing.contracts"), cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts"),
notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME, validating = false)) notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME, validating = false))
) )
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME)) aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))

View File

@ -46,6 +46,7 @@ import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.dummyCommand import net.corda.testing.core.dummyCommand
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.testing.node.TestClock 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.InMemoryMessage
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.InternalMockNodeParameters import net.corda.testing.node.internal.InternalMockNodeParameters
@ -72,7 +73,7 @@ class ValidatingNotaryServiceTests {
@Before @Before
fun setup() { 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)) aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
notaryNode = mockNet.defaultNotaryNode notaryNode = mockNet.defaultNotaryNode
notary = mockNet.defaultNotaryIdentity notary = mockNet.defaultNotaryIdentity

View File

@ -39,6 +39,7 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.HibernateConfiguration import net.corda.nodeapi.internal.persistence.HibernateConfiguration
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.testing.internal.rigorousMock 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.InternalMockNetwork
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
import org.junit.After import org.junit.After
@ -90,7 +91,7 @@ class VaultSoftLockManagerTest {
private val mockVault = rigorousMock<VaultServiceInternal>().also { private val mockVault = rigorousMock<VaultServiceInternal>().also {
doNothing().whenever(it).softLockRelease(any(), anyOrNull()) 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) { object : InternalMockNetwork.MockNode(args) {
override fun makeVaultService(keyManagementService: KeyManagementService, services: ServicesForResolution, hibernateConfig: HibernateConfiguration, database: CordaPersistence): VaultServiceInternal { override fun makeVaultService(keyManagementService: KeyManagementService, services: ServicesForResolution, hibernateConfig: HibernateConfiguration, database: CordaPersistence): VaultServiceInternal {
val node = this val node = this

View File

@ -21,6 +21,7 @@ import net.corda.core.identity.Party
import net.corda.core.internal.concurrent.fork import net.corda.core.internal.concurrent.fork
import net.corda.core.internal.concurrent.transpose import net.corda.core.internal.concurrent.transpose
import net.corda.core.internal.packageName 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.Vault
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.node.services.queryBy import net.corda.core.node.services.queryBy

View File

@ -23,6 +23,7 @@ import net.corda.node.internal.StartedNode
import net.corda.testing.core.* import net.corda.testing.core.*
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.cordappsForPackages
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -40,7 +41,7 @@ class CashIssueAndPaymentFlowTests {
@Before @Before
fun start() { fun start() {
mockNet = InternalMockNetwork(servicePeerAllocationStrategy = RoundRobin(), 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) bankOfCordaNode = mockNet.createPartyNode(BOC_NAME)
aliceNode = mockNet.createPartyNode(ALICE_NAME) aliceNode = mockNet.createPartyNode(ALICE_NAME)
bankOfCorda = bankOfCordaNode.info.singleIdentity() bankOfCorda = bankOfCordaNode.info.singleIdentity()

View File

@ -22,9 +22,8 @@ import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.testing.core.* import net.corda.testing.core.*
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin 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.InternalMockNetwork
import net.corda.testing.node.internal.cordappsForPackages
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -51,7 +50,7 @@ class CashIssueAndPayNoSelectionTests(private val anonymous: Boolean) {
@Before @Before
fun start() { fun start() {
mockNet = InternalMockNetwork(servicePeerAllocationStrategy = RoundRobin(), 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) bankOfCordaNode = mockNet.createPartyNode(BOC_NAME)
aliceNode = mockNet.createPartyNode(ALICE_NAME) aliceNode = mockNet.createPartyNode(ALICE_NAME)
bankOfCorda = bankOfCordaNode.info.singleIdentity() bankOfCorda = bankOfCordaNode.info.singleIdentity()

View File

@ -23,6 +23,7 @@ import net.corda.node.internal.StartedNode
import net.corda.testing.core.* import net.corda.testing.core.*
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.cordappsForPackages
import net.corda.testing.node.internal.startFlow import net.corda.testing.node.internal.startFlow
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -42,7 +43,7 @@ class CashPaymentFlowTests {
fun start() { fun start() {
mockNet = InternalMockNetwork( mockNet = InternalMockNetwork(
servicePeerAllocationStrategy = RoundRobin(), 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) bankOfCordaNode = mockNet.createPartyNode(BOC_NAME)
aliceNode = mockNet.createPartyNode(ALICE_NAME) aliceNode = mockNet.createPartyNode(ALICE_NAME)
bankOfCorda = bankOfCordaNode.info.singleIdentity() bankOfCorda = bankOfCordaNode.info.singleIdentity()

View File

@ -64,10 +64,7 @@ import net.corda.testing.internal.LogHelper
import net.corda.testing.internal.TEST_TX_TIME import net.corda.testing.internal.TEST_TX_TIME
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.* import net.corda.testing.node.*
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.*
import net.corda.testing.node.internal.InternalMockNodeParameters
import net.corda.testing.node.internal.pumpReceive
import net.corda.testing.node.internal.startFlow
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -99,8 +96,8 @@ internal fun CheckpointStorage.checkpoints(): List<SerializedBytes<Checkpoint>>
@RunWith(Parameterized::class) @RunWith(Parameterized::class)
class TwoPartyTradeFlowTests(private val anonymous: Boolean) { class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
companion object { companion object {
private val cordappPackages = listOf( private val cordappPackages = listOf("com.r3.corda.enterprise.perftestcordapp.contracts", "com.r3.corda.enterprise.perftestcordapp.schemas")
"com.r3.corda.enterprise.perftestcordapp.contracts", "com.r3.corda.enterprise.perftestcordapp.schemas") private val cordappsForAllNodes = cordappsForPackages(cordappPackages)
@JvmStatic @JvmStatic
@Parameterized.Parameters(name = "Anonymous = {0}") @Parameterized.Parameters(name = "Anonymous = {0}")
fun data(): Collection<Boolean> = listOf(true, false) 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 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 // we run in the unit test thread exclusively to speed things up, ensure deterministic results and
// allow interruption half way through. // allow interruption half way through.
mockNet = InternalMockNetwork(threadPerNode = true, cordappPackages = cordappPackages) mockNet = InternalMockNetwork(threadPerNode = true, cordappsForAllNodes = cordappsForAllNodes)
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>() val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) { MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
val notaryNode = mockNet.defaultNotaryNode val notaryNode = mockNet.defaultNotaryNode
@ -181,7 +178,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
@Test(expected = InsufficientBalanceException::class) @Test(expected = InsufficientBalanceException::class)
fun `trade cash for commercial paper fails using soft locking`() { 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>() val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) { MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
val notaryNode = mockNet.defaultNotaryNode val notaryNode = mockNet.defaultNotaryNode
@ -239,7 +236,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
@Test @Test
fun `shutdown and restore`() { fun `shutdown and restore`() {
mockNet = InternalMockNetwork(cordappPackages = cordappPackages) mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForAllNodes)
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>() val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) { MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
val notaryNode = mockNet.defaultNotaryNode val notaryNode = mockNet.defaultNotaryNode
@ -340,7 +337,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
private fun makeNodeWithTracking( private fun makeNodeWithTracking(
name: CordaX500Name): StartedNode<InternalMockNetwork.MockNode> { name: CordaX500Name): StartedNode<InternalMockNetwork.MockNode> {
// Create a node in the mock network ... // 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) { object : InternalMockNetwork.MockNode(args) {
// That constructs a recording tx storage // That constructs a recording tx storage
override fun makeTransactionStorage(database: CordaPersistence, transactionCacheSizeBytes: Long): WritableTransactionStorage { override fun makeTransactionStorage(database: CordaPersistence, transactionCacheSizeBytes: Long): WritableTransactionStorage {
@ -352,7 +349,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
@Test @Test
fun `check dependencies of sale asset are resolved`() { fun `check dependencies of sale asset are resolved`() {
mockNet = InternalMockNetwork(cordappPackages = cordappPackages) mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForAllNodes)
val notaryNode = mockNet.defaultNotaryNode val notaryNode = mockNet.defaultNotaryNode
val aliceNode = makeNodeWithTracking(ALICE_NAME) val aliceNode = makeNodeWithTracking(ALICE_NAME)
val bobNode = makeNodeWithTracking(BOB_NAME) val bobNode = makeNodeWithTracking(BOB_NAME)
@ -456,7 +453,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
@Test @Test
fun `track works`() { fun `track works`() {
mockNet = InternalMockNetwork(cordappPackages = cordappPackages) mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForAllNodes)
val notaryNode = mockNet.defaultNotaryNode val notaryNode = mockNet.defaultNotaryNode
val aliceNode = makeNodeWithTracking(ALICE_NAME) val aliceNode = makeNodeWithTracking(ALICE_NAME)
val bobNode = makeNodeWithTracking(BOB_NAME) val bobNode = makeNodeWithTracking(BOB_NAME)
@ -534,7 +531,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
@Test @Test
fun `dependency with error on buyer side`() { fun `dependency with error on buyer side`() {
mockNet = InternalMockNetwork(cordappPackages = cordappPackages) mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForAllNodes)
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>() val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) { MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
runWithError(ledgerIdentityService, true, false, "at least one cash input") runWithError(ledgerIdentityService, true, false, "at least one cash input")
@ -543,7 +540,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
@Test @Test
fun `dependency with error on seller side`() { fun `dependency with error on seller side`() {
mockNet = InternalMockNetwork(cordappPackages = cordappPackages) mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForAllNodes)
val ledgerIdentityService = rigorousMock<IdentityServiceInternal>() val ledgerIdentityService = rigorousMock<IdentityServiceInternal>()
MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) { MockServices(cordappPackages, MEGA_CORP.name, ledgerIdentityService).ledger(DUMMY_NOTARY) {
runWithError(ledgerIdentityService, false, true, "Issuances have a time-window") runWithError(ledgerIdentityService, false, true, "Issuances have a time-window")

View File

@ -23,7 +23,7 @@ import net.corda.core.node.services.AttachmentStorage
import net.corda.core.serialization.* import net.corda.core.serialization.*
import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.ByteSequence
import net.corda.core.utilities.OpaqueBytes 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.node.internal.cordapp.CordappProviderImpl
import net.corda.nodeapi.DummyContractBackdoor import net.corda.nodeapi.DummyContractBackdoor
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
@ -67,7 +67,7 @@ class AttachmentsClassLoaderTests {
val testSerialization = SerializationEnvironmentRule() val testSerialization = SerializationEnvironmentRule()
private val attachments = MockAttachmentStorage() private val attachments = MockAttachmentStorage()
private val networkParameters = testNetworkParameters() 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 cordapp get() = cordappProvider.cordapps.first()
private val attachmentId get() = cordappProvider.getCordappAttachmentId(cordapp)!! private val attachmentId get() = cordappProvider.getCordappAttachmentId(cordapp)!!
private val appContext get() = cordappProvider.getAppContext(cordapp) private val appContext get() = cordappProvider.getAppContext(cordapp)

View File

@ -86,3 +86,15 @@ include 'cordform-common'
include 'hsm-tool' include 'hsm-tool'
include 'launcher' include 'launcher'
include 'node:dist' 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
}
}

View File

@ -30,10 +30,7 @@ import net.corda.testing.driver.PortAllocation.Incremental
import net.corda.testing.driver.internal.internalServices import net.corda.testing.driver.internal.internalServices
import net.corda.testing.node.NotarySpec import net.corda.testing.node.NotarySpec
import net.corda.testing.node.User import net.corda.testing.node.User
import net.corda.testing.node.internal.DriverDSLImpl import net.corda.testing.node.internal.*
import net.corda.testing.node.internal.genericDriver
import net.corda.testing.node.internal.getTimestampAsDirectoryName
import net.corda.testing.node.internal.newContext
import rx.Observable import rx.Observable
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths 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 * @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. * in. If null the Driver-level value will be used.
* @property maximumHeapSize The maximum JVM heap size to use for the node. * @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") @Suppress("unused")
data class NodeParameters( data class NodeParameters(
@ -155,8 +155,57 @@ data class NodeParameters(
val customOverrides: Map<String, Any?> = emptyMap(), val customOverrides: Map<String, Any?> = emptyMap(),
val startInSameProcess: Boolean? = null, val startInSameProcess: Boolean? = null,
val maximumHeapSize: String = "512m", 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( constructor(
providedName: CordaX500Name?, providedName: CordaX500Name?,
rpcUsers: List<User>, rpcUsers: List<User>,
@ -171,7 +220,44 @@ data class NodeParameters(
customOverrides, customOverrides,
startInSameProcess, startInSameProcess,
maximumHeapSize, 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( fun copy(
providedName: CordaX500Name?, providedName: CordaX500Name?,
@ -189,6 +275,25 @@ data class NodeParameters(
maximumHeapSize, maximumHeapSize,
null) 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 withProvidedName(providedName: CordaX500Name?): NodeParameters = copy(providedName = providedName)
fun withRpcUsers(rpcUsers: List<User>): NodeParameters = copy(rpcUsers = rpcUsers) fun withRpcUsers(rpcUsers: List<User>): NodeParameters = copy(rpcUsers = rpcUsers)
fun withVerifierType(verifierType: VerifierType): NodeParameters = copy(verifierType = verifierType) fun withVerifierType(verifierType: VerifierType): NodeParameters = copy(verifierType = verifierType)
@ -196,6 +301,8 @@ data class NodeParameters(
fun withStartInSameProcess(startInSameProcess: Boolean?): NodeParameters = copy(startInSameProcess = startInSameProcess) fun withStartInSameProcess(startInSameProcess: Boolean?): NodeParameters = copy(startInSameProcess = startInSameProcess)
fun withMaximumHeapSize(maximumHeapSize: String): NodeParameters = copy(maximumHeapSize = maximumHeapSize) fun withMaximumHeapSize(maximumHeapSize: String): NodeParameters = copy(maximumHeapSize = maximumHeapSize)
fun withLogLevel(logLevel: String?): NodeParameters = copy(logLevel = logLevel) 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, startNodesInProcess = defaultParameters.startNodesInProcess,
waitForAllNodesToFinish = defaultParameters.waitForAllNodesToFinish, waitForAllNodesToFinish = defaultParameters.waitForAllNodesToFinish,
notarySpecs = defaultParameters.notarySpecs, notarySpecs = defaultParameters.notarySpecs,
extraCordappPackagesToScan = defaultParameters.extraCordappPackagesToScan,
jmxPolicy = defaultParameters.jmxPolicy, jmxPolicy = defaultParameters.jmxPolicy,
compatibilityZone = null, compatibilityZone = null,
networkParameters = defaultParameters.networkParameters, networkParameters = defaultParameters.networkParameters,
notaryCustomOverrides = defaultParameters.notaryCustomOverrides, notaryCustomOverrides = defaultParameters.notaryCustomOverrides,
inMemoryDB = defaultParameters.inMemoryDB inMemoryDB = defaultParameters.inMemoryDB,
cordappsForAllNodes = defaultParameters.cordappsForAllNodes()
), ),
coerce = { it }, coerce = { it },
dsl = dsl, 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 * @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 * the data is not persisted between node restarts). Has no effect if node is configured
* in any way to use database other than H2. * 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") @Suppress("unused")
data class DriverParameters( data class DriverParameters(
@ -299,8 +407,44 @@ data class DriverParameters(
val networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()), val networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()),
val notaryCustomOverrides: Map<String, Any?> = emptyMap(), val notaryCustomOverrides: Map<String, Any?> = emptyMap(),
val initialiseSerialization: Boolean = true, 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( constructor(
isDebug: Boolean, isDebug: Boolean,
driverDirectory: Path, driverDirectory: Path,
@ -329,7 +473,41 @@ data class DriverParameters(
networkParameters, networkParameters,
emptyMap(), emptyMap(),
true, 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( constructor(
@ -362,7 +540,43 @@ data class DriverParameters(
networkParameters, networkParameters,
emptyMap(), emptyMap(),
initialiseSerialization, 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) fun withIsDebug(isDebug: Boolean): DriverParameters = copy(isDebug = isDebug)
@ -380,6 +594,7 @@ data class DriverParameters(
fun withNetworkParameters(networkParameters: NetworkParameters): DriverParameters = copy(networkParameters = networkParameters) fun withNetworkParameters(networkParameters: NetworkParameters): DriverParameters = copy(networkParameters = networkParameters)
fun withNotaryCustomOverrides(notaryCustomOverrides: Map<String, Any?>): DriverParameters = copy(notaryCustomOverrides = notaryCustomOverrides) fun withNotaryCustomOverrides(notaryCustomOverrides: Map<String, Any?>): DriverParameters = copy(notaryCustomOverrides = notaryCustomOverrides)
fun withInMemoryDB(inMemoryDB: Boolean): DriverParameters = copy(inMemoryDB = inMemoryDB) fun withInMemoryDB(inMemoryDB: Boolean): DriverParameters = copy(inMemoryDB = inMemoryDB)
fun withCordappsForAllNodes(cordappsForAllNodes: Set<TestCorDapp>?): DriverParameters = copy(cordappsForAllNodes = cordappsForAllNodes)
fun copy( fun copy(
isDebug: Boolean, isDebug: Boolean,
@ -441,4 +656,69 @@ data class DriverParameters(
notaryCustomOverrides = emptyMap(), notaryCustomOverrides = emptyMap(),
initialiseSerialization = initialiseSerialization 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
)
} }

View File

@ -90,6 +90,38 @@ interface DriverDSL {
maximumHeapSize: String = defaultParameters.maximumHeapSize maximumHeapSize: String = defaultParameters.maximumHeapSize
): CordaFuture<NodeHandle> ): 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. * Helper function for starting a [Node] with custom parameters from Java.
* *

View File

@ -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
}
}

View File

@ -24,10 +24,8 @@ import net.corda.node.internal.StartedNode
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.node.internal.InternalMockMessagingService import net.corda.testing.driver.TestCorDapp
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.*
import net.corda.testing.node.internal.InternalMockNodeParameters
import net.corda.testing.node.internal.newContext
import rx.Observable import rx.Observable
import java.math.BigInteger import java.math.BigInteger
import java.nio.file.Path 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, * @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. * 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 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") @Suppress("unused")
data class MockNodeParameters @JvmOverloads constructor( data class MockNodeParameters constructor(
val forcedID: Int? = null, val forcedID: Int? = null,
val legalName: CordaX500Name? = null, val legalName: CordaX500Name? = null,
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
val configOverrides: (NodeConfiguration) -> Any? = {}, 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 withForcedID(forcedID: Int?): MockNodeParameters = copy(forcedID = forcedID)
fun withLegalName(legalName: CordaX500Name?): MockNodeParameters = copy(legalName = legalName) fun withLegalName(legalName: CordaX500Name?): MockNodeParameters = copy(legalName = legalName)
fun withEntropyRoot(entropyRoot: BigInteger): MockNodeParameters = copy(entropyRoot = entropyRoot) fun withEntropyRoot(entropyRoot: BigInteger): MockNodeParameters = copy(entropyRoot = entropyRoot)
fun withConfigOverrides(configOverrides: (NodeConfiguration) -> Any?): MockNodeParameters = copy(configOverrides = configOverrides) 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 { 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 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 * @property networkParameters The network parameters to be used by all the nodes. [NetworkParameters.notaries] must be
* empty as notaries are defined by [notarySpecs]. * 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") @Suppress("MemberVisibilityCanBePrivate", "CanBeParameter")
open class MockNetwork( open class MockNetwork(
@ -224,11 +237,23 @@ open class MockNetwork(
val threadPerNode: Boolean = defaultParameters.threadPerNode, val threadPerNode: Boolean = defaultParameters.threadPerNode,
val servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy, val servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy,
val notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs, val notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs,
val networkParameters: NetworkParameters = defaultParameters.networkParameters) { val networkParameters: NetworkParameters = defaultParameters.networkParameters,
val cordappsForAllNodes: Set<TestCorDapp> = cordappsForPackages(cordappPackages)) {
@JvmOverloads @JvmOverloads
constructor(cordappPackages: List<String>, parameters: MockNetworkParameters = MockNetworkParameters()) : this(cordappPackages, defaultParameters = parameters) 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. */ /** 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 val nextNodeId get(): Int = internalMockNetwork.nextNodeId
@ -272,7 +297,26 @@ open class MockNetwork(
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
configOverrides: (NodeConfiguration) -> Any? = {}, configOverrides: (NodeConfiguration) -> Any? = {},
extraCordappPackages: List<String> = emptyList()): StartedMockNode { 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))) return StartedMockNode.create(internalMockNetwork.createNode(InternalMockNodeParameters(parameters)))
} }
@ -295,7 +339,26 @@ open class MockNetwork(
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
configOverrides: (NodeConfiguration) -> Any? = {}, configOverrides: (NodeConfiguration) -> Any? = {},
extraCordappPackages: List<String> = emptyList()): UnstartedMockNode { 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))) return UnstartedMockNode.create(internalMockNetwork.createUnstartedNode(InternalMockNodeParameters(parameters)))
} }

View File

@ -28,9 +28,10 @@ import net.corda.core.node.services.*
import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SerializeAsToken
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.cordapp.CordappLoader
import net.corda.node.internal.ServicesForResolutionImpl import net.corda.node.internal.ServicesForResolutionImpl
import net.corda.node.internal.configureDatabase 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.api.*
import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.node.services.schema.HibernateObserver import net.corda.node.services.schema.HibernateObserver
@ -72,7 +73,15 @@ open class MockServices private constructor(
private val initialIdentity: TestIdentity, private val initialIdentity: TestIdentity,
private val moreKeys: Array<out KeyPair> private val moreKeys: Array<out KeyPair>
) : ServiceHub { ) : ServiceHub {
companion object { 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. * 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 * 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, initialIdentity: TestIdentity,
networkParameters: NetworkParameters = testNetworkParameters(), networkParameters: NetworkParameters = testNetworkParameters(),
vararg moreKeys: KeyPair): Pair<CordaPersistence, MockServices> { 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 dataSourceProps = makeInternalTestDataSourceProperties(initialIdentity.name.organisation, SecureHash.randomSHA256().toString())
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas) val schemaService = NodeSchemaService(cordappLoader.cordappSchemas)
val database = configureDatabase(dataSourceProps, makeTestDatabaseProperties(initialIdentity.name.organisation), identityService::wellKnownPartyFromX500Name, identityService::wellKnownPartyFromAnonymous, schemaService) 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) 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. // Because Kotlin is dumb and makes not publicly visible objects public, thus changing the public API.
private val mockStateMachineRecordedTransactionMappingStorage = MockStateMachineRecordedTransactionMappingStorage() private val mockStateMachineRecordedTransactionMappingStorage = MockStateMachineRecordedTransactionMappingStorage()
} }
@ -155,39 +156,38 @@ open class MockServices private constructor(
* (you can get one from [makeTestIdentityService]) and represents the given identity. * (you can get one from [makeTestIdentityService]) and represents the given identity.
*/ */
@JvmOverloads @JvmOverloads
constructor(cordappPackages: List<String>, constructor(cordappPackages: Iterable<String>,
initialIdentity: TestIdentity, initialIdentity: TestIdentity,
identityService: IdentityService = makeTestIdentityService(), identityService: IdentityService = makeTestIdentityService(),
vararg moreKeys: KeyPair) : 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, initialIdentity: TestIdentity,
identityService: IdentityService, identityService: IdentityService,
networkParameters: NetworkParameters, networkParameters: NetworkParameters,
vararg moreKeys: KeyPair) : 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 * 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. * (you can get one from [makeTestIdentityService]) and represents the given identity.
*/ */
@JvmOverloads @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) this(cordappPackages, TestIdentity(initialIdentityName, key), identityService, *moreKeys)
/** /**
* Create a mock [ServiceHub] that can't load CorDapp code, which uses the provided identity service * 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. * (you can get one from [makeTestIdentityService]) and which represents the given identity.
*/ */
@JvmOverloads @JvmOverloads
constructor(cordappPackages: List<String>, initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService()) : constructor(cordappPackages: Iterable<String>, initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService()) :
this(cordappPackages, TestIdentity(initialIdentityName), identityService) this(cordappPackages, TestIdentity(initialIdentityName), identityService)
/** /**
* Create a mock [ServiceHub] that can't load CorDapp code, and which uses a default service identity. * 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 * 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 @JvmOverloads
constructor(initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService(), key: KeyPair, vararg moreKeys: KeyPair) 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 * 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 @JvmOverloads
constructor(initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService()) 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 * 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. * it is aware of.
*/ */
constructor(firstIdentity: TestIdentity, vararg moreIdentities: TestIdentity) : this( constructor(firstIdentity: TestIdentity, vararg moreIdentities: TestIdentity) : this(
listOf(getCallerPackage()), listOf(getCallerPackage(MockServices::class)!!),
firstIdentity, firstIdentity,
makeTestIdentityService(*listOf(firstIdentity, *moreIdentities).map { it.identity }.toTypedArray()), makeTestIdentityService(*listOf(firstIdentity, *moreIdentities).map { it.identity }.toTypedArray()),
firstIdentity.keyPair 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 * Create a mock [ServiceHub] which uses the package of the caller to find CorDapp code. It uses a default service
* identity. * 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>) { override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable<SignedTransaction>) {
txs.forEach { 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) return NodeInfo(listOf(NetworkHostAndPort("mock.node.services", 10000)), listOf(initialIdentity.identity), 1, serial = 1L)
} }
override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2) override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2)
private val mockCordappProvider: MockCordappProvider = MockCordappProvider(cordappLoader, attachments, networkParameters.whitelistedContractImplementations) private val mockCordappProvider: MockCordappProvider = MockCordappProvider(cordappLoader, attachments, networkParameters.whitelistedContractImplementations)
override val cordappProvider: CordappProvider get() = mockCordappProvider override val cordappProvider: CordappProvider get() = mockCordappProvider
protected val servicesForResolution: ServicesForResolution get() = ServicesForResolutionImpl(identityService, attachments, cordappProvider, networkParameters, validatedTransactions) protected val servicesForResolution: ServicesForResolution get() = ServicesForResolutionImpl(identityService, attachments, cordappProvider, networkParameters, validatedTransactions)

View File

@ -62,6 +62,7 @@ import net.corda.testing.node.NotarySpec
import net.corda.testing.node.User 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.NON_VALIDATING_RAFT
import net.corda.testing.node.internal.DriverDSLImpl.ClusterType.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.OkHttpClient
import okhttp3.Request import okhttp3.Request
import rx.Subscription import rx.Subscription
@ -95,20 +96,19 @@ class DriverDSLImpl(
val isDebug: Boolean, val isDebug: Boolean,
val startNodesInProcess: Boolean, val startNodesInProcess: Boolean,
val waitForAllNodesToFinish: Boolean, val waitForAllNodesToFinish: Boolean,
extraCordappPackagesToScan: List<String>,
val jmxPolicy: JmxPolicy, val jmxPolicy: JmxPolicy,
val notarySpecs: List<NotarySpec>, val notarySpecs: List<NotarySpec>,
val compatibilityZone: CompatibilityZoneParams?, val compatibilityZone: CompatibilityZoneParams?,
val networkParameters: NetworkParameters, val networkParameters: NetworkParameters,
val notaryCustomOverrides: Map<String, Any?>, val notaryCustomOverrides: Map<String, Any?>,
val inMemoryDB: Boolean val inMemoryDB: Boolean,
val cordappsForAllNodes: Set<TestCorDapp>
) : InternalDriverDSL { ) : InternalDriverDSL {
private var _executorService: ScheduledExecutorService? = null private var _executorService: ScheduledExecutorService? = null
val executorService get() = _executorService!! val executorService get() = _executorService!!
private var _shutdownManager: ShutdownManager? = null private var _shutdownManager: ShutdownManager? = null
override val shutdownManager get() = _shutdownManager!! 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. // Map from a nodes legal name to an observable emitting the number of nodes in its network map.
private val networkVisibilityController = NetworkVisibilityController() private val networkVisibilityController = NetworkVisibilityController()
/** /**
@ -206,26 +206,7 @@ class DriverDSLImpl(
} }
} }
override fun startNode( 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)
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( override fun startNode(
defaultParameters: NodeParameters, defaultParameters: NodeParameters,
@ -235,6 +216,20 @@ class DriverDSLImpl(
customOverrides: Map<String, Any?>, customOverrides: Map<String, Any?>,
startInSameProcess: Boolean?, startInSameProcess: Boolean?,
maximumHeapSize: String, 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? bytemanPort: Int?
): CordaFuture<NodeHandle> { ): CordaFuture<NodeHandle> {
val p2pAddress = portAllocation.nextHostAndPort() val p2pAddress = portAllocation.nextHostAndPort()
@ -251,7 +246,7 @@ class DriverDSLImpl(
return registrationFuture.flatMap { return registrationFuture.flatMap {
networkMapAvailability.flatMap { networkMapAvailability.flatMap {
// But starting the node proper does require the network map // 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, startInSameProcess: Boolean? = null,
maximumHeapSize: String = "512m", maximumHeapSize: String = "512m",
p2pAddress: NetworkHostAndPort = portAllocation.nextHostAndPort(), p2pAddress: NetworkHostAndPort = portAllocation.nextHostAndPort(),
additionalCordapps: Set<TestCorDapp> = emptySet(),
regenerateCordappsOnStart: Boolean = false,
bytemanPort: Int? = null): CordaFuture<NodeHandle> { bytemanPort: Int? = null): CordaFuture<NodeHandle> {
val rpcAddress = portAllocation.nextHostAndPort() val rpcAddress = portAllocation.nextHostAndPort()
val rpcAdminAddress = portAllocation.nextHostAndPort() val rpcAdminAddress = portAllocation.nextHostAndPort()
@ -293,7 +290,7 @@ class DriverDSLImpl(
allowMissingConfig = true, allowMissingConfig = true,
configOverrides = if (overrides.hasPath("devMode")) overrides else overrides + mapOf("devMode" to true) configOverrides = if (overrides.hasPath("devMode")) overrides else overrides + mapOf("devMode" to true)
)).checkAndOverrideForInMemoryDB() )).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> { 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")) NON_VALIDATING_BFT(false, CordaX500Name("BFT", "Zurich", "CH"))
} }
// TODO remove this
internal fun startCordformNodes(cordforms: List<CordformNode>): CordaFuture<*> { internal fun startCordformNodes(cordforms: List<CordformNode>): CordaFuture<*> {
check(notarySpecs.isEmpty()) { "Specify notaries in the CordformDefinition" } check(notarySpecs.isEmpty()) { "Specify notaries in the CordformDefinition" }
check(compatibilityZone == null) { "Cordform nodes cannot be run with compatibilityZoneURL" } check(compatibilityZone == null) { "Cordform nodes cannot be run with compatibilityZoneURL" }
@ -404,6 +402,7 @@ class DriverDSLImpl(
}.transpose() }.transpose()
} }
// TODO remove this
private fun startCordformNode(cordform: CordformNode, localNetworkMap: LocalNetworkMap): CordaFuture<NodeHandle> { private fun startCordformNode(cordform: CordformNode, localNetworkMap: LocalNetworkMap): CordaFuture<NodeHandle> {
val name = CordaX500Name.parse(cordform.name) 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 // 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, allowMissingConfig = true,
configOverrides = rawConfig.toNodeOnly() configOverrides = rawConfig.toNodeOnly()
) )
val cordaConfig = typesafe.parseAsNodeConfiguration() val config = NodeConfig(typesafe).checkAndOverrideForInMemoryDB()
val config = NodeConfig(rawConfig, cordaConfig).checkAndOverrideForInMemoryDB() return startNodeInternal(config, webAddress, null, "512m", localNetworkMap, emptySet())
return startNodeInternal(config, webAddress, null, "512m", localNetworkMap, null)
} }
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
@ -656,7 +654,6 @@ class DriverDSLImpl(
bytemanJarPath, bytemanJarPath,
null, null,
systemProperties, systemProperties,
cordappPackages,
"512m", "512m",
*extraCmdLineFlag *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, webAddress: NetworkHostAndPort,
startInProcess: Boolean?, startInProcess: Boolean?,
maximumHeapSize: String, maximumHeapSize: String,
localNetworkMap: LocalNetworkMap?, localNetworkMap: LocalNetworkMap?,
bytemanPort: Int?): CordaFuture<NodeHandle> { additionalCordapps: Set<TestCorDapp>,
val visibilityHandle = networkVisibilityController.register(config.corda.myLegalName) regenerateCordappsOnStart: Boolean = false,
val baseDirectory = config.corda.baseDirectory.createDirectories() bytemanPort: Int? = null): CordaFuture<NodeHandle> {
val visibilityHandle = networkVisibilityController.register(specifiedConfig.corda.myLegalName)
val baseDirectory = specifiedConfig.corda.baseDirectory.createDirectories()
localNetworkMap?.networkParametersCopier?.install(baseDirectory) localNetworkMap?.networkParametersCopier?.install(baseDirectory)
localNetworkMap?.nodeInfosCopier?.addConfig(baseDirectory) localNetworkMap?.nodeInfosCopier?.addConfig(baseDirectory)
@ -682,10 +685,16 @@ class DriverDSLImpl(
visibilityHandle.close() 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) { if (startInProcess ?: startNodesInProcess) {
val nodeAndThreadFuture = startInProcessNode(executorService, config, cordappPackages) val nodeAndThreadFuture = startInProcessNode(executorService, config)
shutdownManager.registerShutdown( shutdownManager.registerShutdown(
nodeAndThreadFuture.map { (node, thread) -> nodeAndThreadFuture.map { (node, thread) ->
{ {
@ -712,7 +721,7 @@ class DriverDSLImpl(
} else { } else {
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
val monitorPort = if (jmxPolicy.startJmxHttpServer) jmxPolicy.jmxHttpServerPortAllocation?.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 // 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 // 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)] 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( private fun startInProcessNode(
executorService: ScheduledExecutorService, executorService: ScheduledExecutorService,
config: NodeConfig, config: NodeConfig
cordappPackages: List<String>
): CordaFuture<Pair<StartedNode<Node>, Thread>> { ): CordaFuture<Pair<StartedNode<Node>, Thread>> {
return executorService.fork { return executorService.fork {
log.info("Starting in-process Node ${config.corda.myLegalName.organisation}") log.info("Starting in-process Node ${config.corda.myLegalName.organisation}")
@ -819,7 +830,7 @@ class DriverDSLImpl(
// Write node.conf // Write node.conf
writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe.toNodeOnly()) writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe.toNodeOnly())
// TODO pass the version in? // 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) { val nodeThread = thread(name = config.corda.myLegalName.organisation) {
node.internals.run() node.internals.run()
} }
@ -838,10 +849,10 @@ class DriverDSLImpl(
bytemanJarPath: String?, bytemanJarPath: String?,
bytemanPort: Int?, bytemanPort: Int?,
overriddenSystemProperties: Map<String, String>, overriddenSystemProperties: Map<String, String>,
cordappPackages: List<String>,
maximumHeapSize: String, maximumHeapSize: String,
vararg extraCmdLineFlag: String vararg extraCmdLineFlag: String
): Process { ): Process {
log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, " + log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, " +
"debug port is " + (debugPort ?: "not enabled") + ", " + "debug port is " + (debugPort ?: "not enabled") + ", " +
"jolokia monitoring port is " + (monitorPort ?: "not enabled") + ", " + "jolokia monitoring port is " + (monitorPort ?: "not enabled") + ", " +
@ -856,11 +867,6 @@ class DriverDSLImpl(
) )
systemProperties += inheritFromParentProcess() systemProperties += inheritFromParentProcess()
if (cordappPackages.isNotEmpty()) {
systemProperties += Node.scanPackagesSystemProperty to cordappPackages.joinToString(Node.scanPackagesSeparator)
}
systemProperties += overriddenSystemProperties systemProperties += overriddenSystemProperties
// See experimental/quasar-hook/README.md for how to generate. // See experimental/quasar-hook/README.md for how to generate.
@ -1075,6 +1081,8 @@ interface InternalDriverDSL : DriverDSL, CordformContext {
customOverrides: Map<String, Any?> = defaultParameters.customOverrides, customOverrides: Map<String, Any?> = defaultParameters.customOverrides,
startInSameProcess: Boolean? = defaultParameters.startInSameProcess, startInSameProcess: Boolean? = defaultParameters.startInSameProcess,
maximumHeapSize: String = defaultParameters.maximumHeapSize, maximumHeapSize: String = defaultParameters.maximumHeapSize,
additionalCordapps: Set<TestCorDapp> = defaultParameters.additionalCordapps,
regenerateCordappsOnStart: Boolean = defaultParameters.regenerateCordappsOnStart,
bytemanPort: Int? = null bytemanPort: Int? = null
): CordaFuture<NodeHandle> ): CordaFuture<NodeHandle>
} }
@ -1132,13 +1140,13 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
isDebug = defaultParameters.isDebug, isDebug = defaultParameters.isDebug,
startNodesInProcess = defaultParameters.startNodesInProcess, startNodesInProcess = defaultParameters.startNodesInProcess,
waitForAllNodesToFinish = defaultParameters.waitForAllNodesToFinish, waitForAllNodesToFinish = defaultParameters.waitForAllNodesToFinish,
extraCordappPackagesToScan = defaultParameters.extraCordappPackagesToScan,
jmxPolicy = defaultParameters.jmxPolicy, jmxPolicy = defaultParameters.jmxPolicy,
notarySpecs = defaultParameters.notarySpecs, notarySpecs = defaultParameters.notarySpecs,
compatibilityZone = null, compatibilityZone = null,
networkParameters = defaultParameters.networkParameters, networkParameters = defaultParameters.networkParameters,
notaryCustomOverrides = defaultParameters.notaryCustomOverrides, notaryCustomOverrides = defaultParameters.notaryCustomOverrides,
inMemoryDB = defaultParameters.inMemoryDB inMemoryDB = defaultParameters.inMemoryDB,
cordappsForAllNodes = defaultParameters.cordappsForAllNodes()
) )
) )
val shutdownHook = addShutdownHook(driverDsl::shutdown) val shutdownHook = addShutdownHook(driverDsl::shutdown)
@ -1212,12 +1220,12 @@ fun <A> internalDriver(
startNodesInProcess: Boolean = DriverParameters().startNodesInProcess, startNodesInProcess: Boolean = DriverParameters().startNodesInProcess,
waitForAllNodesToFinish: Boolean = DriverParameters().waitForAllNodesToFinish, waitForAllNodesToFinish: Boolean = DriverParameters().waitForAllNodesToFinish,
notarySpecs: List<NotarySpec> = DriverParameters().notarySpecs, notarySpecs: List<NotarySpec> = DriverParameters().notarySpecs,
extraCordappPackagesToScan: List<String> = DriverParameters().extraCordappPackagesToScan,
jmxPolicy: JmxPolicy = DriverParameters().jmxPolicy, jmxPolicy: JmxPolicy = DriverParameters().jmxPolicy,
networkParameters: NetworkParameters = DriverParameters().networkParameters, networkParameters: NetworkParameters = DriverParameters().networkParameters,
compatibilityZone: CompatibilityZoneParams? = null, compatibilityZone: CompatibilityZoneParams? = null,
notaryCustomOverrides: Map<String, Any?> = DriverParameters().notaryCustomOverrides, notaryCustomOverrides: Map<String, Any?> = DriverParameters().notaryCustomOverrides,
inMemoryDB: Boolean = DriverParameters().inMemoryDB, inMemoryDB: Boolean = DriverParameters().inMemoryDB,
cordappsForAllNodes: Set<TestCorDapp> = DriverParameters().cordappsForAllNodes(),
dsl: DriverDSLImpl.() -> A dsl: DriverDSLImpl.() -> A
): A { ): A {
return genericDriver( return genericDriver(
@ -1231,12 +1239,12 @@ fun <A> internalDriver(
startNodesInProcess = startNodesInProcess, startNodesInProcess = startNodesInProcess,
waitForAllNodesToFinish = waitForAllNodesToFinish, waitForAllNodesToFinish = waitForAllNodesToFinish,
notarySpecs = notarySpecs, notarySpecs = notarySpecs,
extraCordappPackagesToScan = extraCordappPackagesToScan,
jmxPolicy = jmxPolicy, jmxPolicy = jmxPolicy,
compatibilityZone = compatibilityZone, compatibilityZone = compatibilityZone,
networkParameters = networkParameters, networkParameters = networkParameters,
notaryCustomOverrides = notaryCustomOverrides, notaryCustomOverrides = notaryCustomOverrides,
inMemoryDB = inMemoryDB inMemoryDB = inMemoryDB,
cordappsForAllNodes = cordappsForAllNodes
), ),
coerce = { it }, coerce = { it },
dsl = dsl, dsl = dsl,
@ -1257,6 +1265,8 @@ private fun Config.toNodeOnly(): Config {
return if (hasPath("webAddress")) withoutPath("webAddress").withoutPath("useHTTPS") else this 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> { fun DriverDSL.startNode(providedName: CordaX500Name, devMode: Boolean, parameters: NodeParameters = NodeParameters()): CordaFuture<NodeHandle> {
var customOverrides = emptyMap<String, String>() var customOverrides = emptyMap<String, String>()
if (!devMode) { if (!devMode) {

View File

@ -22,10 +22,7 @@ import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.*
import net.corda.core.internal.createDirectories
import net.corda.core.internal.createDirectory
import net.corda.core.internal.uncheckedCast
import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.core.messaging.SingleMessageRecipient 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.IdentityService
import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.KeyManagementService
import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializationWhitelist
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.*
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.hours
import net.corda.core.utilities.seconds
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.cordapp.CordappLoader
import net.corda.node.internal.AbstractNode import net.corda.node.internal.AbstractNode
import net.corda.node.internal.StartedNode 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.NodePropertiesStore
import net.corda.node.services.api.SchemaService import net.corda.node.services.api.SchemaService
import net.corda.node.services.config.* 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.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.driver.TestCorDapp
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import net.corda.testing.internal.setGlobalSerialization import net.corda.testing.internal.setGlobalSerialization
import net.corda.testing.internal.testThreadFactory import net.corda.testing.internal.testThreadFactory
import net.corda.testing.node.* import net.corda.testing.node.*
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.activemq.artemis.utils.ReusableLatch
import org.apache.sshd.common.util.security.SecurityUtils import org.apache.sshd.common.util.security.SecurityUtils
import rx.internal.schedulers.CachedThreadScheduler import rx.internal.schedulers.CachedThreadScheduler
import java.math.BigInteger import java.math.BigInteger
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
import java.time.Clock import java.time.Clock
@ -85,8 +81,7 @@ data class MockNodeArgs(
val network: InternalMockNetwork, val network: InternalMockNetwork,
val id: Int, val id: Int,
val entropyRoot: BigInteger, val entropyRoot: BigInteger,
val version: VersionInfo = MOCK_VERSION_INFO, val version: VersionInfo = MOCK_VERSION_INFO
val extraCordappPackages: List<String> = emptyList()
) )
data class InternalMockNodeParameters( data class InternalMockNodeParameters(
@ -95,25 +90,26 @@ data class InternalMockNodeParameters(
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
val configOverrides: (NodeConfiguration) -> Any? = {}, val configOverrides: (NodeConfiguration) -> Any? = {},
val version: VersionInfo = MOCK_VERSION_INFO, val version: VersionInfo = MOCK_VERSION_INFO,
val extraCordappPackages: List<String> = emptyList()) { val additionalCordapps: Set<TestCorDapp>? = null) {
constructor(mockNodeParameters: MockNodeParameters) : this( constructor(mockNodeParameters: MockNodeParameters) : this(
mockNodeParameters.forcedID, mockNodeParameters.forcedID,
mockNodeParameters.legalName, mockNodeParameters.legalName,
mockNodeParameters.entropyRoot, mockNodeParameters.entropyRoot,
mockNodeParameters.configOverrides, mockNodeParameters.configOverrides,
MOCK_VERSION_INFO, MOCK_VERSION_INFO,
mockNodeParameters.extraCordappPackages mockNodeParameters.additionalCordapps
) )
} }
open class InternalMockNetwork(private val cordappPackages: List<String> = emptyList(), open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParameters(),
defaultParameters: MockNetworkParameters = MockNetworkParameters(),
val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped, val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped,
val threadPerNode: Boolean = defaultParameters.threadPerNode, val threadPerNode: Boolean = defaultParameters.threadPerNode,
servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy, servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy,
val notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs, val notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs,
val testDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()),
networkParameters: NetworkParameters = testNetworkParameters(), 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 { init {
// Apache SSHD for whatever reason registers a SFTP FileSystemProvider - which gets loaded by JimFS. // Apache SSHD for whatever reason registers a SFTP FileSystemProvider - which gets loaded by JimFS.
// This SFTP support loads BouncyCastle, which we want to avoid. // 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" } require(networkParameters.notaries.isEmpty()) { "Define notaries using notarySpecs" }
} }
private companion object {
private val logger = loggerFor<InternalMockNetwork>()
}
var nextNodeId = 0 var nextNodeId = 0
private set private set
private val filesystem = Jimfs.newFileSystem(unix()) 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 sharedUserCount = AtomicInteger(0)
private val sharedCorDappsDirectories: Iterable<Path> by lazy {
TestCordappDirectories.cached(cordappsForAllNodes)
}
/** A read only view of the current set of nodes. */ /** A read only view of the current set of nodes. */
val nodes: List<MockNode> get() = _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, args.config,
TestClock(Clock.systemUTC()), TestClock(Clock.systemUTC()),
args.version, args.version,
// Add the specified additional CorDapps. cordappLoader,
CordappLoader.createDefaultWithTestPackages(args.config, args.network.cordappPackages + args.extraCordappPackages),
args.network.busyLatch args.network.busyLatch
) { ) {
companion object { companion object {
@ -365,7 +368,7 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
return createUnstartedNode(parameters, defaultFactory) 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) 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. */ /** 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)!! 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 id = parameters.forcedID ?: nextNodeId++
val config = mockNodeConfiguration().also { val config = mockNodeConfiguration().also {
doReturn(baseDirectory(id).createDirectories()).whenever(it).baseDirectory 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 doReturn(emptyList<SecureHash>()).whenever(it).extraNetworkMapKeys
parameters.configOverrides(it) 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 _nodes += node
if (start) { if (start) {
node.start() node.start()
@ -396,7 +404,7 @@ open class InternalMockNetwork(private val cordappPackages: List<String> = empty
return node 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.internals.disableDBCloseOnStop()
node.dispose() node.dispose()
return createNode( 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 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 * 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() { fun waitQuiescent() {
busyLatch.await() busyLatch.await()
} }
} }
open class MessagingServiceSpy(val messagingService: MessagingService) : MessagingService by messagingService open class MessagingServiceSpy(val messagingService: MessagingService) : MessagingService by messagingService

View File

@ -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)
}

View File

@ -10,6 +10,7 @@
package net.corda.testing.node.internal package net.corda.testing.node.internal
import com.typesafe.config.ConfigValueFactory
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.concurrent.fork import net.corda.core.internal.concurrent.fork
import net.corda.core.internal.concurrent.transpose 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.internal.div
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.loggerFor
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.internal.EnterpriseNode import net.corda.node.internal.EnterpriseNode
import net.corda.node.internal.Node import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.services.config.* import net.corda.node.services.config.*
import net.corda.nodeapi.internal.config.toConfig import net.corda.nodeapi.internal.config.toConfig
import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NetworkParametersCopier
@ -41,10 +42,12 @@ import java.nio.file.Path
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.concurrent.thread 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() { abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyList()) : IntegrationTest() {
companion object { companion object {
private val WHITESPACE = "\\s++".toRegex() private val WHITESPACE = "\\s++".toRegex()
private val logger = loggerFor<NodeBasedTest>()
} }
@Rule @Rule
@ -112,15 +115,24 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
) + configOverrides ) + 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() val errors = nodeConfiguration.validate()
if (errors.isNotEmpty()) { if (errors.isNotEmpty()) {
throw IllegalStateException("Invalid node configuration. Errors where:${System.lineSeparator()}${errors.joinToString(System.lineSeparator())}") throw IllegalStateException("Invalid node configuration. Errors where:${System.lineSeparator()}${errors.joinToString(System.lineSeparator())}")
} }
} }
defaultNetworkParameters.install(baseDirectory) defaultNetworkParameters.install(baseDirectory)
return InProcessNode(parsedConfig, MOCK_VERSION_INFO.copy(platformVersion = platformVersion), cordappPackages) return InProcessNode(parsedConfig, MOCK_VERSION_INFO.copy(platformVersion = platformVersion))
} }
@JvmOverloads @JvmOverloads
@ -153,8 +165,7 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
} }
} }
class InProcessNode( class InProcessNode(configuration: NodeConfiguration, versionInfo: VersionInfo) : EnterpriseNode(configuration, versionInfo, false) {
configuration: NodeConfiguration, versionInfo: VersionInfo, cordappPackages: List<String>) : EnterpriseNode(
configuration, versionInfo, false, CordappLoader.createDefaultWithTestPackages(configuration, cordappPackages)) {
override fun getRxIoScheduler() = CachedThreadScheduler(testThreadFactory()).also { runOnStop += it::shutdown } override fun getRxIoScheduler() = CachedThreadScheduler(testThreadFactory()).also { runOnStop += it::shutdown }
} }

View File

@ -37,8 +37,10 @@ import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.MAX_MESSAGE_SIZE import net.corda.testing.core.MAX_MESSAGE_SIZE
import net.corda.testing.driver.JmxPolicy import net.corda.testing.driver.JmxPolicy
import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.PortAllocation
import net.corda.testing.driver.TestCorDapp
import net.corda.testing.node.NotarySpec import net.corda.testing.node.NotarySpec
import net.corda.testing.node.User 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.SimpleString
import org.apache.activemq.artemis.api.core.TransportConfiguration import org.apache.activemq.artemis.api.core.TransportConfiguration
import org.apache.activemq.artemis.api.core.client.ActiveMQClient import org.apache.activemq.artemis.api.core.client.ActiveMQClient
@ -119,13 +121,13 @@ fun <A> rpcDriver(
useTestClock: Boolean = false, useTestClock: Boolean = false,
startNodesInProcess: Boolean = false, startNodesInProcess: Boolean = false,
waitForNodesToFinish: Boolean = false, waitForNodesToFinish: Boolean = false,
extraCordappPackagesToScan: List<String> = emptyList(),
notarySpecs: List<NotarySpec> = emptyList(), notarySpecs: List<NotarySpec> = emptyList(),
externalTrace: Trace? = null, externalTrace: Trace? = null,
jmxPolicy: JmxPolicy = JmxPolicy(), jmxPolicy: JmxPolicy = JmxPolicy(),
networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()), networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()),
notaryCustomOverrides: Map<String, Any?> = emptyMap(), notaryCustomOverrides: Map<String, Any?> = emptyMap(),
inMemoryDB: Boolean = true, inMemoryDB: Boolean = true,
cordappsForAllNodes: Set<TestCorDapp> = cordappsInCurrentAndAdditionalPackages(),
dsl: RPCDriverDSL.() -> A dsl: RPCDriverDSL.() -> A
): A { ): A {
return genericDriver( return genericDriver(
@ -139,13 +141,13 @@ fun <A> rpcDriver(
isDebug = isDebug, isDebug = isDebug,
startNodesInProcess = startNodesInProcess, startNodesInProcess = startNodesInProcess,
waitForAllNodesToFinish = waitForNodesToFinish, waitForAllNodesToFinish = waitForNodesToFinish,
extraCordappPackagesToScan = extraCordappPackagesToScan,
notarySpecs = notarySpecs, notarySpecs = notarySpecs,
jmxPolicy = jmxPolicy, jmxPolicy = jmxPolicy,
compatibilityZone = null, compatibilityZone = null,
networkParameters = networkParameters, networkParameters = networkParameters,
notaryCustomOverrides = notaryCustomOverrides, notaryCustomOverrides = notaryCustomOverrides,
inMemoryDB = inMemoryDB inMemoryDB = inMemoryDB,
cordappsForAllNodes = cordappsForAllNodes
), externalTrace ), externalTrace
), ),
coerce = { it }, coerce = { it },

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -17,6 +17,7 @@ import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.testing.driver.JmxPolicy import net.corda.testing.driver.JmxPolicy
import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.PortAllocation
import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages
import net.corda.testing.node.internal.internalDriver import net.corda.testing.node.internal.internalDriver
/** /**
@ -69,12 +70,12 @@ class CordformNodeRunner(private val cordformDefinition: CordformDefinition) {
internalDriver( internalDriver(
jmxPolicy = JmxPolicy(true), jmxPolicy = JmxPolicy(true),
driverDirectory = cordformDefinition.nodesDirectory, driverDirectory = cordformDefinition.nodesDirectory,
extraCordappPackagesToScan = extraPackagesToScan,
// Notaries are manually specified in Cordform so we don't want the driver automatically starting any // Notaries are manually specified in Cordform so we don't want the driver automatically starting any
notarySpecs = emptyList(), notarySpecs = emptyList(),
// Start from after the largest port used to prevent port clash // Start from after the largest port used to prevent port clash
portAllocation = PortAllocation.Incremental(maxPort + 1), portAllocation = PortAllocation.Incremental(maxPort + 1),
waitForAllNodesToFinish = waitForAllNodesToFinish waitForAllNodesToFinish = waitForAllNodesToFinish,
cordappsForAllNodes = cordappsInCurrentAndAdditionalPackages(extraPackagesToScan)
) { ) {
cordformDefinition.setup(this) cordformDefinition.setup(this)
startCordformNodes(nodes).getOrThrow() // Only proceed once everything is up and running startCordformNodes(nodes).getOrThrow() // Only proceed once everything is up and running

View File

@ -19,7 +19,7 @@ class InternalMockNetworkTests {
fun `does not leak serialization env if init fails`() { fun `does not leak serialization env if init fails`() {
val e = Exception("didn't work") val e = Exception("didn't work")
assertThatThrownBy { assertThatThrownBy {
object : InternalMockNetwork() { object : InternalMockNetwork(cordappsForAllNodes = emptySet()) {
override fun createNotaries() = throw e override fun createNotaries() = throw e
} }
}.isSameAs(e) }.isSameAs(e)

View File

@ -17,7 +17,8 @@ import net.corda.core.internal.TEST_UPLOADER
import net.corda.core.internal.cordapp.CordappImpl import net.corda.core.internal.cordapp.CordappImpl
import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.node.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.node.internal.cordapp.CordappProviderImpl
import net.corda.testing.services.MockAttachmentStorage import net.corda.testing.services.MockAttachmentStorage
import java.nio.file.Paths 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 override fun getContractAttachmentID(contractClassName: ContractClassName): AttachmentId? = cordappRegistry.find { it.first.contractClassNames.contains(contractClassName) }?.second ?: super.getContractAttachmentID(contractClassName)
?: super.getContractAttachmentID(contractClassName)
private fun findOrImportAttachment(contractClassNames: List<ContractClassName>, data: ByteArray, attachments: MockAttachmentStorage): AttachmentId { private fun findOrImportAttachment(contractClassNames: List<ContractClassName>, data: ByteArray, attachments: MockAttachmentStorage): AttachmentId {
val existingAttachment = attachments.files.filter { val existingAttachment = attachments.files.filter {

View File

@ -23,7 +23,7 @@ import net.corda.core.internal.div
import net.corda.core.internal.exists import net.corda.core.internal.exists
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.node.internal.DataSourceFactory.createDatasourceFromDriverJarFolders 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.ConfigHelper
import net.corda.node.services.config.configOf import net.corda.node.services.config.configOf
import net.corda.node.services.config.parseAsNodeConfiguration 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'.") errorAndExit("Not a valid node folder. Could not find the config file: '$config'.")
} }
val nodeConfig = ConfigHelper.loadConfig(baseDirectory, config).parseAsNodeConfiguration() 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) val schemaService = NodeSchemaService(extraSchemas = cordappLoader.cordappSchemas, includeNotarySchemas = nodeConfig.notary != null)