mirror of
https://github.com/corda/corda.git
synced 2025-05-09 12:02:56 +00:00
Merge remote-tracking branch 'remotes/open/master' into merges/july-23-11-26
# Conflicts: # confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt # docs/source/changelog.rst # node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt # node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt # node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt # node/src/integration-test/kotlin/net/corda/node/services/BFTSMaRtTests.kt # node/src/main/kotlin/net/corda/node/internal/Node.kt # node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt # node/src/main/kotlin/net/corda/node/internal/cordapp/ManifestUtils.kt # node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt # node/src/test/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoaderTest.kt # node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt # node/src/test/kotlin/net/corda/node/services/TimedFlowTests.kt # settings.gradle # testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt # testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt # testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt
This commit is contained in:
commit
a4d65dae22
@ -5825,6 +5825,8 @@ public interface net.corda.testing.driver.DriverDSL
|
|||||||
@NotNull
|
@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)
|
||||||
|
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -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! :)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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>(),
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
@ -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``
|
||||||
|
@ -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
|
||||||
---------------------------
|
---------------------------
|
||||||
|
@ -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.
|
||||||
|
@ -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() {
|
||||||
|
@ -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())
|
||||||
|
}
|
||||||
}
|
}
|
@ -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')
|
||||||
|
@ -0,0 +1,117 @@
|
|||||||
|
package net.corda.node
|
||||||
|
|
||||||
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
|
import com.google.common.base.Stopwatch
|
||||||
|
import net.corda.client.rpc.CordaRPCClient
|
||||||
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.flows.StartableByRPC
|
||||||
|
import net.corda.core.internal.concurrent.transpose
|
||||||
|
import net.corda.core.messaging.startFlow
|
||||||
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
|
import net.corda.core.utilities.getOrThrow
|
||||||
|
import net.corda.core.utilities.minutes
|
||||||
|
import net.corda.finance.DOLLARS
|
||||||
|
import net.corda.finance.flows.CashIssueFlow
|
||||||
|
import net.corda.finance.flows.CashPaymentFlow
|
||||||
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
|
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||||
|
import net.corda.testing.driver.DriverParameters
|
||||||
|
import net.corda.testing.driver.InProcess
|
||||||
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.driver.internal.internalServices
|
||||||
|
import net.corda.testing.internal.performance.div
|
||||||
|
import net.corda.testing.node.NotarySpec
|
||||||
|
import net.corda.testing.node.User
|
||||||
|
import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages
|
||||||
|
import net.corda.testing.node.internal.internalDriver
|
||||||
|
import net.corda.testing.node.internal.performance.startPublishingFixedRateInjector
|
||||||
|
import net.corda.testing.node.internal.performance.startReporter
|
||||||
|
import net.corda.testing.node.internal.performance.startTightLoopInjector
|
||||||
|
import org.junit.Ignore
|
||||||
|
import org.junit.Test
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.streams.toList
|
||||||
|
|
||||||
|
@Ignore("Run these locally")
|
||||||
|
class NodePerformanceTests {
|
||||||
|
@StartableByRPC
|
||||||
|
class EmptyFlow : FlowLogic<Unit>() {
|
||||||
|
@Suspendable
|
||||||
|
override fun call() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class FlowMeasurementResult(
|
||||||
|
val flowPerSecond: Double,
|
||||||
|
val averageMs: Double
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `empty flow per second`() {
|
||||||
|
driver(DriverParameters(startNodesInProcess = true)) {
|
||||||
|
val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlow<EmptyFlow>())))).get()
|
||||||
|
|
||||||
|
CordaRPCClient(a.rpcAddress).use("A", "A") { connection ->
|
||||||
|
val timings = Collections.synchronizedList(ArrayList<Long>())
|
||||||
|
val N = 10000
|
||||||
|
val overallTiming = Stopwatch.createStarted().apply {
|
||||||
|
startTightLoopInjector(
|
||||||
|
parallelism = 8,
|
||||||
|
numberOfInjections = N,
|
||||||
|
queueBound = 50
|
||||||
|
) {
|
||||||
|
val timing = Stopwatch.createStarted().apply {
|
||||||
|
connection.proxy.startFlow(::EmptyFlow).returnValue.get()
|
||||||
|
}.stop().elapsed(TimeUnit.MICROSECONDS)
|
||||||
|
timings.add(timing)
|
||||||
|
}
|
||||||
|
}.stop().elapsed(TimeUnit.MICROSECONDS)
|
||||||
|
println(
|
||||||
|
FlowMeasurementResult(
|
||||||
|
flowPerSecond = N / (overallTiming * 0.000001),
|
||||||
|
averageMs = timings.average() * 0.001
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `empty flow rate`() {
|
||||||
|
internalDriver(startNodesInProcess = true) {
|
||||||
|
val a = startNode(rpcUsers = listOf(User("A", "A", setOf(startFlow<EmptyFlow>())))).get()
|
||||||
|
a as InProcess
|
||||||
|
val metricRegistry = startReporter(this.shutdownManager, a.internalServices.monitoringService.metrics)
|
||||||
|
CordaRPCClient(a.rpcAddress).use("A", "A") { connection ->
|
||||||
|
startPublishingFixedRateInjector(metricRegistry, 8, 5.minutes, 2000L / TimeUnit.SECONDS) {
|
||||||
|
connection.proxy.startFlow(::EmptyFlow).returnValue.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `self pay rate`() {
|
||||||
|
val user = User("A", "A", setOf(startFlow<CashIssueFlow>(), startFlow<CashPaymentFlow>()))
|
||||||
|
internalDriver(
|
||||||
|
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, rpcUsers = listOf(user))),
|
||||||
|
startNodesInProcess = true,
|
||||||
|
cordappsForAllNodes = cordappsInCurrentAndAdditionalPackages("net.corda.finance")
|
||||||
|
) {
|
||||||
|
val notary = defaultNotaryNode.getOrThrow() as InProcess
|
||||||
|
val metricRegistry = startReporter(this.shutdownManager, notary.internalServices.monitoringService.metrics)
|
||||||
|
CordaRPCClient(notary.rpcAddress).use("A", "A") { connection ->
|
||||||
|
println("ISSUING")
|
||||||
|
val doneFutures = (1..100).toList().parallelStream().map {
|
||||||
|
connection.proxy.startFlow(::CashIssueFlow, 1.DOLLARS, OpaqueBytes.of(0), defaultNotaryIdentity).returnValue
|
||||||
|
}.toList()
|
||||||
|
doneFutures.transpose().get()
|
||||||
|
println("STARTING PAYMENT")
|
||||||
|
startPublishingFixedRateInjector(metricRegistry, 8, 5.minutes, 100L / TimeUnit.SECONDS) {
|
||||||
|
connection.proxy.startFlow(::CashPaymentFlow, 1.DOLLARS, defaultNotaryIdentity).returnValue.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package net.corda.node.flows
|
||||||
|
|
||||||
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
|
import net.corda.core.flows.*
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.internal.concurrent.transpose
|
||||||
|
import net.corda.core.internal.packageName
|
||||||
|
import net.corda.core.messaging.startFlow
|
||||||
|
import net.corda.core.utilities.getOrThrow
|
||||||
|
import net.corda.core.utilities.unwrap
|
||||||
|
import net.corda.testing.core.singleIdentity
|
||||||
|
import net.corda.testing.driver.DriverParameters
|
||||||
|
import net.corda.testing.driver.TestCorDapp
|
||||||
|
import net.corda.testing.driver.driver
|
||||||
|
import org.junit.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class AsymmetricCorDappsTests {
|
||||||
|
|
||||||
|
@StartableByRPC
|
||||||
|
@InitiatingFlow
|
||||||
|
class Ping(private val pongParty: Party, val times: Int) : FlowLogic<Unit>() {
|
||||||
|
@Suspendable
|
||||||
|
override fun call() {
|
||||||
|
val pongSession = initiateFlow(pongParty)
|
||||||
|
pongSession.sendAndReceive<Unit>(times)
|
||||||
|
for (i in 1..times) {
|
||||||
|
val j = pongSession.sendAndReceive<Int>(i).unwrap { it }
|
||||||
|
assertEquals(i, j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@InitiatedBy(Ping::class)
|
||||||
|
class Pong(private val pingSession: FlowSession) : FlowLogic<Unit>() {
|
||||||
|
@Suspendable
|
||||||
|
override fun call() {
|
||||||
|
val times = pingSession.sendAndReceive<Int>(Unit).unwrap { it }
|
||||||
|
for (i in 1..times) {
|
||||||
|
val j = pingSession.sendAndReceive<Int>(i).unwrap { it }
|
||||||
|
assertEquals(i, j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun noSharedCorDappsWithAsymmetricSpecificClasses() {
|
||||||
|
|
||||||
|
driver(DriverParameters(startNodesInProcess = false, cordappsForAllNodes = emptySet())) {
|
||||||
|
|
||||||
|
val nodeA = startNode(additionalCordapps = setOf(TestCorDapp.Factory.create("Szymon CorDapp", "1.0", classes = setOf(Ping::class.java)))).getOrThrow()
|
||||||
|
val nodeB = startNode(additionalCordapps = setOf(TestCorDapp.Factory.create("Szymon CorDapp", "1.0", classes = setOf(Ping::class.java, Pong::class.java)))).getOrThrow()
|
||||||
|
nodeA.rpc.startFlow(::Ping, nodeB.nodeInfo.singleIdentity(), 1).returnValue.getOrThrow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun sharedCorDappsWithAsymmetricSpecificClasses() {
|
||||||
|
|
||||||
|
val resourceName = "cordapp.properties"
|
||||||
|
val cordappPropertiesResource = this::class.java.getResource(resourceName)
|
||||||
|
val sharedCordapp = TestCorDapp.Factory.create("shared", "1.0", classes = setOf(Ping::class.java)).plusResource("${AsymmetricCorDappsTests::class.java.packageName}.$resourceName", cordappPropertiesResource)
|
||||||
|
val cordappForNodeB = TestCorDapp.Factory.create("nodeB_only", "1.0", classes = setOf(Pong::class.java))
|
||||||
|
driver(DriverParameters(startNodesInProcess = false, cordappsForAllNodes = setOf(sharedCordapp))) {
|
||||||
|
|
||||||
|
val (nodeA, nodeB) = listOf(startNode(), startNode(additionalCordapps = setOf(cordappForNodeB))).transpose().getOrThrow()
|
||||||
|
nodeA.rpc.startFlow(::Ping, nodeB.nodeInfo.singleIdentity(), 1).returnValue.getOrThrow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun sharedCorDappsWithAsymmetricSpecificClassesInProcess() {
|
||||||
|
|
||||||
|
val resourceName = "cordapp.properties"
|
||||||
|
val cordappPropertiesResource = this::class.java.getResource(resourceName)
|
||||||
|
val sharedCordapp = TestCorDapp.Factory.create("shared", "1.0", classes = setOf(Ping::class.java)).plusResource("${AsymmetricCorDappsTests::class.java.packageName}.$resourceName", cordappPropertiesResource)
|
||||||
|
val cordappForNodeB = TestCorDapp.Factory.create("nodeB_only", "1.0", classes = setOf(Pong::class.java))
|
||||||
|
driver(DriverParameters(startNodesInProcess = true, cordappsForAllNodes = setOf(sharedCordapp))) {
|
||||||
|
|
||||||
|
val (nodeA, nodeB) = listOf(startNode(), startNode(additionalCordapps = setOf(cordappForNodeB))).transpose().getOrThrow()
|
||||||
|
nodeA.rpc.startFlow(::Ping, nodeB.nodeInfo.singleIdentity(), 1).returnValue.getOrThrow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,152 @@
|
|||||||
|
package net.corda.node.flows
|
||||||
|
|
||||||
|
import net.corda.client.rpc.CordaRPCClient
|
||||||
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.core.internal.list
|
||||||
|
import net.corda.core.internal.readLines
|
||||||
|
import net.corda.core.messaging.startTrackedFlow
|
||||||
|
import net.corda.core.utilities.getOrThrow
|
||||||
|
import net.corda.node.internal.CheckpointIncompatibleException
|
||||||
|
import net.corda.node.internal.NodeStartup
|
||||||
|
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||||
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
|
import net.corda.testMessage.Message
|
||||||
|
import net.corda.testMessage.MessageState
|
||||||
|
import net.corda.testing.core.ALICE_NAME
|
||||||
|
import net.corda.testing.core.BOB_NAME
|
||||||
|
import net.corda.testing.core.singleIdentity
|
||||||
|
import net.corda.testing.driver.DriverParameters
|
||||||
|
import net.corda.testing.driver.TestCorDapp
|
||||||
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.node.User
|
||||||
|
import net.corda.testing.node.internal.ListenProcessDeathException
|
||||||
|
import net.test.cordapp.v1.SendMessageFlow
|
||||||
|
import org.junit.Test
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
|
class FlowCheckpointVersionNodeStartupCheckTest {
|
||||||
|
companion object {
|
||||||
|
val message = Message("Hello world!")
|
||||||
|
val classes = setOf(net.corda.testMessage.MessageState::class.java,
|
||||||
|
net.corda.testMessage.MessageContract::class.java,
|
||||||
|
net.test.cordapp.v1.SendMessageFlow::class.java,
|
||||||
|
net.test.cordapp.v1.Record::class.java)
|
||||||
|
val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery"), invokeRpc("vaultTrack")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `restart node successfully with suspended flow`() {
|
||||||
|
|
||||||
|
val cordapps = setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes))
|
||||||
|
|
||||||
|
return driver(DriverParameters(isDebug = true, startNodesInProcess = false, inMemoryDB = false, cordappsForAllNodes = cordapps)) {
|
||||||
|
{
|
||||||
|
val alice = startNode(rpcUsers = listOf(user), providedName = ALICE_NAME).getOrThrow()
|
||||||
|
val bob = startNode(rpcUsers = listOf(user), providedName = BOB_NAME).getOrThrow()
|
||||||
|
alice.stop()
|
||||||
|
CordaRPCClient(bob.rpcAddress).start(user.username, user.password).use {
|
||||||
|
val flowTracker = it.proxy.startTrackedFlow(::SendMessageFlow, message, defaultNotaryIdentity, alice.nodeInfo.singleIdentity()).progress
|
||||||
|
//wait until Bob progresses as far as possible because alice node is off
|
||||||
|
flowTracker.takeFirst { it == SendMessageFlow.Companion.FINALISING_TRANSACTION.label }.toBlocking().single()
|
||||||
|
}
|
||||||
|
bob.stop()
|
||||||
|
}()
|
||||||
|
val result = {
|
||||||
|
//Bob will resume the flow
|
||||||
|
val alice = startNode(rpcUsers = listOf(user), providedName = ALICE_NAME, customOverrides = mapOf("devMode" to false)).getOrThrow()
|
||||||
|
startNode(providedName = BOB_NAME, rpcUsers = listOf(user), customOverrides = mapOf("devMode" to false)).getOrThrow()
|
||||||
|
CordaRPCClient(alice.rpcAddress).start(user.username, user.password).use {
|
||||||
|
val page = it.proxy.vaultTrack(MessageState::class.java)
|
||||||
|
if (page.snapshot.states.isNotEmpty()) {
|
||||||
|
page.snapshot.states.first()
|
||||||
|
} else {
|
||||||
|
val r = page.updates.timeout(5, TimeUnit.SECONDS).take(1).toBlocking().single()
|
||||||
|
if (r.consumed.isNotEmpty()) r.consumed.first() else r.produced.first()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
assertNotNull(result)
|
||||||
|
assertEquals(message, result.state.data.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertNodeRestartFailure(
|
||||||
|
cordapps: Set<TestCorDapp>?,
|
||||||
|
cordappsVersionAtStartup: Set<TestCorDapp>,
|
||||||
|
cordappsVersionAtRestart: Set<TestCorDapp>,
|
||||||
|
reuseAdditionalCordappsAtRestart: Boolean,
|
||||||
|
assertNodeLogs: String
|
||||||
|
) {
|
||||||
|
|
||||||
|
return driver(DriverParameters(
|
||||||
|
startNodesInProcess = false, // start nodes in separate processes to ensure CordappLoader is not shared between restarts
|
||||||
|
inMemoryDB = false, // ensure database is persisted between node restarts so we can keep suspended flow in Bob's node
|
||||||
|
cordappsForAllNodes = cordapps)
|
||||||
|
) {
|
||||||
|
val bobLogFolder = {
|
||||||
|
val alice = startNode(rpcUsers = listOf(user), providedName = ALICE_NAME, additionalCordapps = cordappsVersionAtStartup).getOrThrow()
|
||||||
|
val bob = startNode(rpcUsers = listOf(user), providedName = BOB_NAME, additionalCordapps = cordappsVersionAtStartup).getOrThrow()
|
||||||
|
alice.stop()
|
||||||
|
CordaRPCClient(bob.rpcAddress).start(user.username, user.password).use {
|
||||||
|
val flowTracker = it.proxy.startTrackedFlow(::SendMessageFlow, message, defaultNotaryIdentity, alice.nodeInfo.singleIdentity()).progress
|
||||||
|
// wait until Bob progresses as far as possible because Alice node is offline
|
||||||
|
flowTracker.takeFirst { it == SendMessageFlow.Companion.FINALISING_TRANSACTION.label }.toBlocking().single()
|
||||||
|
}
|
||||||
|
val logFolder = bob.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME
|
||||||
|
// SendMessageFlow suspends in Bob node
|
||||||
|
bob.stop()
|
||||||
|
logFolder
|
||||||
|
}()
|
||||||
|
|
||||||
|
startNode(rpcUsers = listOf(user), providedName = ALICE_NAME, customOverrides = mapOf("devMode" to false),
|
||||||
|
additionalCordapps = cordappsVersionAtRestart, regenerateCordappsOnStart = !reuseAdditionalCordappsAtRestart).getOrThrow()
|
||||||
|
|
||||||
|
assertFailsWith(ListenProcessDeathException::class) {
|
||||||
|
startNode(providedName = BOB_NAME, rpcUsers = listOf(user), customOverrides = mapOf("devMode" to false),
|
||||||
|
additionalCordapps = cordappsVersionAtRestart, regenerateCordappsOnStart = !reuseAdditionalCordappsAtRestart).getOrThrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
val logFile = bobLogFolder.list { it.filter { it.fileName.toString().endsWith(".log") }.findAny().get() }
|
||||||
|
val numberOfNodesThatLogged = logFile.readLines { it.filter { assertNodeLogs in it }.count() }
|
||||||
|
assertEquals(1, numberOfNodesThatLogged)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `restart nodes with incompatible version of suspended flow due to different jar name`() {
|
||||||
|
|
||||||
|
assertNodeRestartFailure(
|
||||||
|
emptySet(),
|
||||||
|
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
|
||||||
|
setOf(TestCorDapp.Factory.create("testJar2", "1.0", classes = classes)),
|
||||||
|
false,
|
||||||
|
CheckpointIncompatibleException.FlowNotInstalledException(SendMessageFlow::class.java).message)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `restart nodes with incompatible version of suspended flow`() {
|
||||||
|
|
||||||
|
assertNodeRestartFailure(
|
||||||
|
emptySet(),
|
||||||
|
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
|
||||||
|
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes + net.test.cordapp.v1.SendMessageFlow::class.java)),
|
||||||
|
false,
|
||||||
|
// the part of the log message generated by CheckpointIncompatibleException.FlowVersionIncompatibleException
|
||||||
|
"that is incompatible with the current installed version of")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `restart nodes with incompatible version of suspended flow due to different timestamps only`() {
|
||||||
|
|
||||||
|
assertNodeRestartFailure(
|
||||||
|
emptySet(),
|
||||||
|
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
|
||||||
|
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
|
||||||
|
false,
|
||||||
|
// the part of the log message generated by CheckpointIncompatibleException.FlowVersionIncompatibleException
|
||||||
|
"that is incompatible with the current installed version of")
|
||||||
|
}
|
||||||
|
}
|
@ -58,7 +58,7 @@ class AttachmentLoadingTests : IntegrationTest() {
|
|||||||
@JvmField
|
@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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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(
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
package net.test.cordapp.v1
|
||||||
|
|
||||||
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
|
import net.corda.core.contracts.Command
|
||||||
|
import net.corda.core.contracts.StateAndContract
|
||||||
|
import net.corda.core.flows.*
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.node.StatesToRecord
|
||||||
|
import net.corda.core.transactions.SignedTransaction
|
||||||
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
|
import net.corda.core.utilities.ProgressTracker
|
||||||
|
import net.corda.testMessage.MESSAGE_CONTRACT_PROGRAM_ID
|
||||||
|
import net.corda.testMessage.Message
|
||||||
|
import net.corda.testMessage.MessageContract
|
||||||
|
import net.corda.testMessage.MessageState
|
||||||
|
|
||||||
|
@StartableByRPC
|
||||||
|
@InitiatingFlow
|
||||||
|
class SendMessageFlow(private val message: Message, private val notary: Party, private val reciepent: Party? = null) : FlowLogic<SignedTransaction>() {
|
||||||
|
companion object {
|
||||||
|
object GENERATING_TRANSACTION : ProgressTracker.Step("Generating transaction based on the message.")
|
||||||
|
object VERIFYING_TRANSACTION : ProgressTracker.Step("Verifying contract constraints.")
|
||||||
|
object SIGNING_TRANSACTION : ProgressTracker.Step("Signing transaction with our private key.")
|
||||||
|
object FINALISING_TRANSACTION : ProgressTracker.Step("Obtaining notary signature and recording transaction.") {
|
||||||
|
override fun childProgressTracker() = FinalityFlow.tracker()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tracker() = ProgressTracker(GENERATING_TRANSACTION, VERIFYING_TRANSACTION, SIGNING_TRANSACTION, FINALISING_TRANSACTION)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val progressTracker = tracker()
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
override fun call(): SignedTransaction {
|
||||||
|
progressTracker.currentStep = GENERATING_TRANSACTION
|
||||||
|
|
||||||
|
val messageState = MessageState(message = message, by = ourIdentity)
|
||||||
|
val txCommand = Command(MessageContract.Commands.Send(), messageState.participants.map { it.owningKey })
|
||||||
|
val txBuilder = TransactionBuilder(notary).withItems(StateAndContract(messageState, MESSAGE_CONTRACT_PROGRAM_ID), txCommand)
|
||||||
|
|
||||||
|
progressTracker.currentStep = VERIFYING_TRANSACTION
|
||||||
|
txBuilder.toWireTransaction(serviceHub).toLedgerTransaction(serviceHub).verify()
|
||||||
|
|
||||||
|
progressTracker.currentStep = SIGNING_TRANSACTION
|
||||||
|
val signedTx = serviceHub.signInitialTransaction(txBuilder)
|
||||||
|
|
||||||
|
progressTracker.currentStep = FINALISING_TRANSACTION
|
||||||
|
|
||||||
|
if (reciepent != null) {
|
||||||
|
val session = initiateFlow(reciepent)
|
||||||
|
subFlow(SendTransactionFlow(session, signedTx))
|
||||||
|
return subFlow(FinalityFlow(signedTx, setOf(reciepent), FINALISING_TRANSACTION.childProgressTracker()))
|
||||||
|
} else {
|
||||||
|
return subFlow(FinalityFlow(signedTx, FINALISING_TRANSACTION.childProgressTracker()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@InitiatedBy(SendMessageFlow::class)
|
||||||
|
class Record(private val session: FlowSession) : FlowLogic<Unit>() {
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
override fun call() {
|
||||||
|
val tx = subFlow(ReceiveTransactionFlow(session, statesToRecord = StatesToRecord.ALL_VISIBLE))
|
||||||
|
serviceHub.addSignature(tx)
|
||||||
|
}
|
||||||
|
}
|
32
node/src/main/kotlin/net/corda/node/cordapp/CordappLoader.kt
Normal file
32
node/src/main/kotlin/net/corda/node/cordapp/CordappLoader.kt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package net.corda.node.cordapp
|
||||||
|
|
||||||
|
import net.corda.core.cordapp.Cordapp
|
||||||
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.schemas.MappedSchema
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles loading [Cordapp]s.
|
||||||
|
*/
|
||||||
|
interface CordappLoader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all [Cordapp]s found.
|
||||||
|
*/
|
||||||
|
val cordapps: List<Cordapp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a [ClassLoader] containing all types from all [Cordapp]s.
|
||||||
|
*/
|
||||||
|
val appClassLoader: ClassLoader
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map between flow class and owning [Cordapp].
|
||||||
|
* The mappings are unique, and the node will not start otherwise.
|
||||||
|
*/
|
||||||
|
val flowCordappMap: Map<Class<out FlowLogic<*>>, Cordapp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all [MappedSchema] found inside the [Cordapp]s.
|
||||||
|
*/
|
||||||
|
val cordappSchemas: Set<MappedSchema>
|
||||||
|
}
|
@ -67,10 +67,11 @@ import net.corda.core.utilities.getOrThrow
|
|||||||
import net.corda.core.utilities.minutes
|
import net.corda.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
|
||||||
|
@ -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>() }
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
@ -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>()
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
key=value
|
@ -49,6 +49,7 @@ import net.corda.testing.core.ALICE_NAME
|
|||||||
import net.corda.testing.core.expect
|
import net.corda.testing.core.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())))
|
||||||
|
@ -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() {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
|
}
|
||||||
}
|
}
|
@ -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")
|
||||||
|
@ -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
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
@ -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() {
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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`() {
|
||||||
|
@ -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))
|
||||||
|
@ -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 = {
|
||||||
|
@ -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()
|
||||||
|
@ -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))
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
|
@ -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")
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
*
|
*
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
package net.corda.testing.driver
|
||||||
|
|
||||||
|
import net.corda.core.DoNotImplement
|
||||||
|
import net.corda.testing.node.internal.MutableTestCorDapp
|
||||||
|
import java.net.URL
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents information about a CorDapp. Used to generate CorDapp JARs in tests.
|
||||||
|
*/
|
||||||
|
@DoNotImplement
|
||||||
|
interface TestCorDapp {
|
||||||
|
|
||||||
|
val name: String
|
||||||
|
val title: String
|
||||||
|
val version: String
|
||||||
|
val vendor: String
|
||||||
|
|
||||||
|
val classes: Set<Class<*>>
|
||||||
|
|
||||||
|
val resources: Set<URL>
|
||||||
|
|
||||||
|
fun packageAsJarInDirectory(parentDirectory: Path): Path
|
||||||
|
|
||||||
|
fun packageAsJarWithPath(jarFilePath: Path)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responsible of creating [TestCorDapp]s.
|
||||||
|
*/
|
||||||
|
class Factory {
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a builder-style [TestCorDapp] to easily generate different [TestCorDapp]s that have something in common.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun create(name: String, version: String, vendor: String = "R3", title: String = name, classes: Set<Class<*>> = emptySet(), willResourceBeAddedBeToCorDapp: (fullyQualifiedName: String, url: URL) -> Boolean = MutableTestCorDapp.Companion::filterTestCorDappClass): TestCorDapp.Mutable {
|
||||||
|
|
||||||
|
return MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedBeToCorDapp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@DoNotImplement
|
||||||
|
interface Mutable : TestCorDapp {
|
||||||
|
|
||||||
|
fun withName(name: String): TestCorDapp.Mutable
|
||||||
|
|
||||||
|
fun withTitle(title: String): TestCorDapp.Mutable
|
||||||
|
|
||||||
|
fun withVersion(version: String): TestCorDapp.Mutable
|
||||||
|
|
||||||
|
fun withVendor(vendor: String): TestCorDapp.Mutable
|
||||||
|
|
||||||
|
fun withClasses(classes: Set<Class<*>>): TestCorDapp.Mutable
|
||||||
|
|
||||||
|
fun plusPackages(pckgs: Set<String>): TestCorDapp.Mutable
|
||||||
|
|
||||||
|
fun minusPackages(pckgs: Set<String>): TestCorDapp.Mutable
|
||||||
|
|
||||||
|
fun plusPackage(pckg: String): TestCorDapp.Mutable = plusPackages(setOf(pckg))
|
||||||
|
|
||||||
|
fun minusPackage(pckg: String): TestCorDapp.Mutable = minusPackages(setOf(pckg))
|
||||||
|
|
||||||
|
fun plusPackage(pckg: Package): TestCorDapp.Mutable = plusPackages(pckg.name)
|
||||||
|
|
||||||
|
fun minusPackage(pckg: Package): TestCorDapp.Mutable = minusPackages(pckg.name)
|
||||||
|
|
||||||
|
operator fun plus(clazz: Class<*>): TestCorDapp.Mutable = withClasses(classes + clazz)
|
||||||
|
|
||||||
|
operator fun minus(clazz: Class<*>): TestCorDapp.Mutable = withClasses(classes - clazz)
|
||||||
|
|
||||||
|
fun plusPackages(pckg: String, vararg pckgs: String): TestCorDapp.Mutable = plusPackages(setOf(pckg, *pckgs))
|
||||||
|
|
||||||
|
fun plusPackages(pckg: Package, vararg pckgs: Package): TestCorDapp.Mutable = minusPackages(setOf(pckg, *pckgs).map { it.name }.toSet())
|
||||||
|
|
||||||
|
fun minusPackages(pckg: String, vararg pckgs: String): TestCorDapp.Mutable = minusPackages(setOf(pckg, *pckgs))
|
||||||
|
|
||||||
|
fun minusPackages(pckg: Package, vararg pckgs: Package): TestCorDapp.Mutable = minusPackages(setOf(pckg, *pckgs).map { it.name }.toSet())
|
||||||
|
|
||||||
|
fun plusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable
|
||||||
|
|
||||||
|
fun minusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable
|
||||||
|
}
|
||||||
|
}
|
@ -24,10 +24,8 @@ import net.corda.node.internal.StartedNode
|
|||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.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)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
package net.corda.testing.node.internal
|
||||||
|
|
||||||
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.testing.driver.TestCorDapp
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URL
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
internal class MutableTestCorDapp private constructor(override val name: String, override val version: String, override val vendor: String, override val title: String, private val willResourceBeAddedToCorDapp: (String, URL) -> Boolean, private val jarEntries: Set<JarEntryInfo>) : TestCorDapp.Mutable {
|
||||||
|
|
||||||
|
constructor(name: String, version: String, vendor: String, title: String, classes: Set<Class<*>>, willResourceBeAddedToCorDapp: (String, URL) -> Boolean) : this(name, version, vendor, title, willResourceBeAddedToCorDapp, jarEntriesFromClasses(classes))
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val jarExtension = ".jar"
|
||||||
|
private const val whitespace = " "
|
||||||
|
private const val whitespaceReplacement = "_"
|
||||||
|
private val productionPathSegments = setOf<(String) -> String>({ "out${File.separator}production${File.separator}classes" }, { fullyQualifiedName -> "main${File.separator}${fullyQualifiedName.packageToPath()}" })
|
||||||
|
private val excludedCordaPackages = setOf("net.corda.core", "net.corda.node")
|
||||||
|
|
||||||
|
fun filterTestCorDappClass(fullyQualifiedName: String, url: URL): Boolean {
|
||||||
|
|
||||||
|
return isTestResource(fullyQualifiedName, url) || !isInExcludedCordaPackage(fullyQualifiedName)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isTestResource(fullyQualifiedName: String, url: URL): Boolean {
|
||||||
|
|
||||||
|
return productionPathSegments.map { it.invoke(fullyQualifiedName) }.none { url.toString().contains(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isInExcludedCordaPackage(packageName: String): Boolean {
|
||||||
|
|
||||||
|
return excludedCordaPackages.any { packageName.startsWith(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun jarEntriesFromClasses(classes: Set<Class<*>>): Set<JarEntryInfo> {
|
||||||
|
|
||||||
|
val illegal = classes.filter { it.protectionDomain?.codeSource?.location == null }
|
||||||
|
if (illegal.isNotEmpty()) {
|
||||||
|
throw IllegalArgumentException("Some classes do not have a location, typically because they are part of Java or Kotlin. Offending types were: ${illegal.joinToString(", ", "[", "]") { it.simpleName }}")
|
||||||
|
}
|
||||||
|
return classes.map(Class<*>::jarEntryInfo).toSet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val classes: Set<Class<*>> = jarEntries.filterIsInstance(JarEntryInfo.ClassJarEntryInfo::class.java).map(JarEntryInfo.ClassJarEntryInfo::clazz).toSet()
|
||||||
|
|
||||||
|
override val resources: Set<URL> = jarEntries.map(JarEntryInfo::url).toSet()
|
||||||
|
|
||||||
|
override fun withName(name: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
||||||
|
|
||||||
|
override fun withTitle(title: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
||||||
|
|
||||||
|
override fun withVersion(version: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
||||||
|
|
||||||
|
override fun withVendor(vendor: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
||||||
|
|
||||||
|
override fun withClasses(classes: Set<Class<*>>) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
||||||
|
|
||||||
|
override fun plusPackages(pckgs: Set<String>) = withClasses(pckgs.map { allClassesForPackage(it) }.fold(classes) { all, packageClasses -> all + packageClasses })
|
||||||
|
|
||||||
|
override fun minusPackages(pckgs: Set<String>) = withClasses(pckgs.map { allClassesForPackage(it) }.fold(classes) { all, packageClasses -> all - packageClasses })
|
||||||
|
|
||||||
|
override fun plusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable = MutableTestCorDapp(name, version, vendor, title, willResourceBeAddedToCorDapp, jarEntries + JarEntryInfo.ResourceJarEntryInfo(fullyQualifiedName, url))
|
||||||
|
|
||||||
|
override fun minusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable = MutableTestCorDapp(name, version, vendor, title, willResourceBeAddedToCorDapp, jarEntries - JarEntryInfo.ResourceJarEntryInfo(fullyQualifiedName, url))
|
||||||
|
|
||||||
|
override fun packageAsJarWithPath(jarFilePath: Path) = jarEntries.packageToCorDapp(jarFilePath, name, version, vendor, title, willResourceBeAddedToCorDapp)
|
||||||
|
|
||||||
|
override fun packageAsJarInDirectory(parentDirectory: Path): Path = (parentDirectory / defaultJarName()).also { packageAsJarWithPath(it) }
|
||||||
|
|
||||||
|
private fun defaultJarName(): String = "${name}_$version$jarExtension".replace(whitespace, whitespaceReplacement)
|
||||||
|
}
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
package net.corda.testing.node.internal
|
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 }
|
||||||
}
|
}
|
||||||
|
@ -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 },
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
package net.corda.testing.node.internal
|
||||||
|
|
||||||
|
import net.corda.core.internal.createDirectories
|
||||||
|
import net.corda.core.internal.deleteRecursively
|
||||||
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.core.internal.exists
|
||||||
|
import net.corda.core.utilities.loggerFor
|
||||||
|
import net.corda.testing.driver.TestCorDapp
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.Paths
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.ConcurrentMap
|
||||||
|
|
||||||
|
internal object TestCordappDirectories {
|
||||||
|
|
||||||
|
private val logger = loggerFor<TestCordappDirectories>()
|
||||||
|
|
||||||
|
private const val whitespace = " "
|
||||||
|
private const val whitespaceReplacement = "_"
|
||||||
|
|
||||||
|
private val cordappsCache: ConcurrentMap<List<String>, Path> = ConcurrentHashMap<List<String>, Path>()
|
||||||
|
|
||||||
|
internal fun cached(cordapps: Iterable<TestCorDapp>, replaceExistingOnes: Boolean = false, cordappsDirectory: Path = defaultCordappsDirectory): Iterable<Path> {
|
||||||
|
|
||||||
|
cordappsDirectory.toFile().deleteOnExit()
|
||||||
|
return cordapps.map { cached(it, replaceExistingOnes, cordappsDirectory) }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun cached(cordapp: TestCorDapp, replaceExistingOnes: Boolean = false, cordappsDirectory: Path = defaultCordappsDirectory): Path {
|
||||||
|
|
||||||
|
cordappsDirectory.toFile().deleteOnExit()
|
||||||
|
val cacheKey = cordapp.resources.map { it.toExternalForm() }.sorted()
|
||||||
|
return if (replaceExistingOnes) {
|
||||||
|
cordappsCache.remove(cacheKey)
|
||||||
|
cordappsCache.getOrPut(cacheKey) {
|
||||||
|
|
||||||
|
val cordappDirectory = (cordappsDirectory / "${cordapp.name}_${cordapp.version}".replace(whitespace, whitespaceReplacement)).toAbsolutePath()
|
||||||
|
cordappDirectory.createDirectories()
|
||||||
|
cordapp.packageAsJarInDirectory(cordappDirectory)
|
||||||
|
cordappDirectory
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cordappsCache.getOrPut(cacheKey) {
|
||||||
|
|
||||||
|
val cordappDirectory = (cordappsDirectory / "${cordapp.name}_${cordapp.version}".replace(whitespace, whitespaceReplacement)).toAbsolutePath()
|
||||||
|
cordappDirectory.createDirectories()
|
||||||
|
cordapp.packageAsJarInDirectory(cordappDirectory)
|
||||||
|
cordappDirectory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun forPackages(packages: Iterable<String>, replaceExistingOnes: Boolean = false, cordappsDirectory: Path = defaultCordappsDirectory): Iterable<Path> {
|
||||||
|
|
||||||
|
cordappsDirectory.toFile().deleteOnExit()
|
||||||
|
val cordapps = simplifyScanPackages(packages).distinct().fold(emptySet<TestCorDapp>()) { all, packageName -> all + testCorDapp(packageName) }
|
||||||
|
return cached(cordapps, replaceExistingOnes, cordappsDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val defaultCordappsDirectory: Path by lazy {
|
||||||
|
|
||||||
|
val cordappsDirectory = (Paths.get("build") / "tmp" / getTimestampAsDirectoryName() / "generated-test-cordapps").toAbsolutePath()
|
||||||
|
logger.info("Initialising generated test CorDapps directory in $cordappsDirectory")
|
||||||
|
if (cordappsDirectory.exists()) {
|
||||||
|
cordappsDirectory.deleteRecursively()
|
||||||
|
}
|
||||||
|
cordappsDirectory.createDirectories()
|
||||||
|
cordappsDirectory
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,214 @@
|
|||||||
|
package net.corda.testing.node.internal
|
||||||
|
|
||||||
|
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
|
||||||
|
import net.corda.core.internal.createDirectories
|
||||||
|
import net.corda.core.internal.deleteIfExists
|
||||||
|
import net.corda.core.internal.outputStream
|
||||||
|
import net.corda.node.internal.cordapp.createTestManifest
|
||||||
|
import net.corda.testing.driver.TestCorDapp
|
||||||
|
import org.apache.commons.io.IOUtils
|
||||||
|
import java.io.File
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.net.URI
|
||||||
|
import java.net.URL
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.attribute.FileTime
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.*
|
||||||
|
import java.util.jar.JarOutputStream
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipOutputStream
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packages some [JarEntryInfo] into a CorDapp JAR with specified [path].
|
||||||
|
* @param path The path of the JAR.
|
||||||
|
* @param willResourceBeAddedBeToCorDapp A filter for the inclusion of [JarEntryInfo] in the JAR.
|
||||||
|
*/
|
||||||
|
internal fun Iterable<JarEntryInfo>.packageToCorDapp(path: Path, name: String, version: String, vendor: String, title: String = name, willResourceBeAddedBeToCorDapp: (String, URL) -> Boolean = { _, _ -> true }) {
|
||||||
|
|
||||||
|
var hasContent = false
|
||||||
|
try {
|
||||||
|
hasContent = packageToCorDapp(path.outputStream(), name, version, vendor, title, willResourceBeAddedBeToCorDapp)
|
||||||
|
} finally {
|
||||||
|
if (!hasContent) {
|
||||||
|
path.deleteIfExists()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packages some [JarEntryInfo] into a CorDapp JAR using specified [outputStream].
|
||||||
|
* @param outputStream The [OutputStream] for the JAR.
|
||||||
|
* @param willResourceBeAddedBeToCorDapp A filter for the inclusion of [JarEntryInfo] in the JAR.
|
||||||
|
*/
|
||||||
|
internal fun Iterable<JarEntryInfo>.packageToCorDapp(outputStream: OutputStream, name: String, version: String, vendor: String, title: String = name, willResourceBeAddedBeToCorDapp: (String, URL) -> Boolean = { _, _ -> true }): Boolean {
|
||||||
|
|
||||||
|
val manifest = createTestManifest(name, title, version, vendor)
|
||||||
|
return JarOutputStream(outputStream, manifest).use { jos -> zip(jos, willResourceBeAddedBeToCorDapp) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a [Class] into a [JarEntryInfo].
|
||||||
|
*/
|
||||||
|
internal fun Class<*>.jarEntryInfo(): JarEntryInfo {
|
||||||
|
|
||||||
|
return JarEntryInfo.ClassJarEntryInfo(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packages some [TestCorDapp]s under a root [directory], each with it's own JAR.
|
||||||
|
* @param directory The parent directory in which CorDapp JAR will be created.
|
||||||
|
*/
|
||||||
|
fun Iterable<TestCorDapp>.packageInDirectory(directory: Path) {
|
||||||
|
|
||||||
|
directory.createDirectories()
|
||||||
|
forEach { cordapp -> cordapp.packageAsJarInDirectory(directory) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all classes within the [targetPackage].
|
||||||
|
*/
|
||||||
|
fun allClassesForPackage(targetPackage: String): Set<Class<*>> {
|
||||||
|
|
||||||
|
val scanResult = FastClasspathScanner(targetPackage).strictWhitelist().scan()
|
||||||
|
return scanResult.namesOfAllClasses.filter { className -> className.startsWith(targetPackage) }.map(scanResult::classNameToClassRef).toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps each package to a [TestCorDapp] with resources found in that package.
|
||||||
|
*/
|
||||||
|
fun cordappsForPackages(packages: Iterable<String>): Set<TestCorDapp> {
|
||||||
|
|
||||||
|
return simplifyScanPackages(packages).toSet().fold(emptySet()) { all, packageName -> all + testCorDapp(packageName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps each package to a [TestCorDapp] with resources found in that package.
|
||||||
|
*/
|
||||||
|
fun cordappsForPackages(firstPackage: String, vararg otherPackages: String): Set<TestCorDapp> {
|
||||||
|
|
||||||
|
return cordappsForPackages(setOf(*otherPackages) + firstPackage)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCallerClass(directCallerClass: KClass<*>): Class<*>? {
|
||||||
|
|
||||||
|
val stackTrace = Throwable().stackTrace
|
||||||
|
val index = stackTrace.indexOfLast { it.className == directCallerClass.java.name }
|
||||||
|
if (index == -1) return null
|
||||||
|
return try {
|
||||||
|
Class.forName(stackTrace[index + 1].className)
|
||||||
|
} catch (e: ClassNotFoundException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCallerPackage(directCallerClass: KClass<*>): String? {
|
||||||
|
|
||||||
|
return getCallerClass(directCallerClass)?.`package`?.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a [TestCorDapp] containing resources found in [packageName].
|
||||||
|
*/
|
||||||
|
internal fun testCorDapp(packageName: String): TestCorDapp {
|
||||||
|
|
||||||
|
val uuid = UUID.randomUUID()
|
||||||
|
val name = "$packageName-$uuid"
|
||||||
|
val version = "$uuid"
|
||||||
|
return TestCorDapp.Factory.create(name, version).plusPackage(packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Squashes child packages if the parent is present. Example: ["com.foo", "com.foo.bar"] into just ["com.foo"].
|
||||||
|
*/
|
||||||
|
fun simplifyScanPackages(scanPackages: Iterable<String>): List<String> {
|
||||||
|
|
||||||
|
return scanPackages.sorted().fold(emptyList()) { listSoFar, packageName ->
|
||||||
|
when {
|
||||||
|
listSoFar.isEmpty() -> listOf(packageName)
|
||||||
|
packageName.startsWith(listSoFar.last()) -> listSoFar
|
||||||
|
else -> listSoFar + packageName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a class or package name into a path segment.
|
||||||
|
*/
|
||||||
|
internal fun String.packageToPath() = replace(".", File.separator)
|
||||||
|
|
||||||
|
private fun Iterable<JarEntryInfo>.zip(outputStream: ZipOutputStream, willResourceBeAddedBeToCorDapp: (String, URL) -> Boolean): Boolean {
|
||||||
|
|
||||||
|
val entries = filter { (fullyQualifiedName, url) -> willResourceBeAddedBeToCorDapp(fullyQualifiedName, url) }
|
||||||
|
if (entries.isNotEmpty()) {
|
||||||
|
zip(outputStream, entries)
|
||||||
|
}
|
||||||
|
return entries.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun zip(outputStream: ZipOutputStream, allInfo: Iterable<JarEntryInfo>) {
|
||||||
|
|
||||||
|
val time = FileTime.from(Instant.EPOCH)
|
||||||
|
val classLoader = Thread.currentThread().contextClassLoader
|
||||||
|
allInfo.distinctBy { it.url }.sortedBy { it.url.toExternalForm() }.forEach { info ->
|
||||||
|
|
||||||
|
try {
|
||||||
|
val entry = ZipEntry(info.entryName).setCreationTime(time).setLastAccessTime(time).setLastModifiedTime(time)
|
||||||
|
outputStream.putNextEntry(entry)
|
||||||
|
classLoader.getResourceAsStream(info.entryName).use {
|
||||||
|
IOUtils.copy(it, outputStream)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
outputStream.closeEntry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single resource to be added to a CorDapp JAR.
|
||||||
|
*/
|
||||||
|
internal sealed class JarEntryInfo(val fullyQualifiedName: String, val url: URL) {
|
||||||
|
|
||||||
|
abstract val entryName: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a class to be added to a CorDapp JAR.
|
||||||
|
*/
|
||||||
|
class ClassJarEntryInfo(val clazz: Class<*>) : JarEntryInfo(clazz.name, clazz.classFileURL()) {
|
||||||
|
|
||||||
|
override val entryName = "${fullyQualifiedName.packageToPath()}$fileExtensionSeparator$classFileExtension"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a resource file to be added to a CorDapp JAR.
|
||||||
|
*/
|
||||||
|
class ResourceJarEntryInfo(fullyQualifiedName: String, url: URL) : JarEntryInfo(fullyQualifiedName, url) {
|
||||||
|
|
||||||
|
override val entryName: String
|
||||||
|
get() {
|
||||||
|
val extensionIndex = fullyQualifiedName.lastIndexOf(fileExtensionSeparator)
|
||||||
|
return "${fullyQualifiedName.substring(0 until extensionIndex).packageToPath()}${fullyQualifiedName.substring(extensionIndex)}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun component1(): String = fullyQualifiedName
|
||||||
|
|
||||||
|
operator fun component2(): URL = url
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
|
||||||
|
private const val classFileExtension = "class"
|
||||||
|
private const val fileExtensionSeparator = "."
|
||||||
|
private const val whitespace = " "
|
||||||
|
private const val whitespaceReplacement = "%20"
|
||||||
|
|
||||||
|
private fun Class<*>.classFileURL(): URL {
|
||||||
|
|
||||||
|
require(protectionDomain?.codeSource?.location != null) { "Invalid class $name for test CorDapp. Classes without protection domain cannot be referenced. This typically happens for Java / Kotlin types." }
|
||||||
|
return URI.create("${protectionDomain.codeSource.location}/${name.packageToPath()}$fileExtensionSeparator$classFileExtension".escaped()).toURL()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.escaped(): String = this.replace(whitespace, whitespaceReplacement)
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,7 @@ import net.corda.core.utilities.NetworkHostAndPort
|
|||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.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
|
||||||
|
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user