Merge remote-tracking branch 'remotes/open/master' into merges/os-merge-2018-10-30

# Conflicts:
#	docs/source/corda-configuration-file.rst
#	node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
#	node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt
This commit is contained in:
szymonsztuka 2018-10-30 14:10:01 +00:00
commit fcd822f176
21 changed files with 260 additions and 80 deletions

View File

@ -120,6 +120,7 @@ see changes to this list.
* Lucas Salmen (Itau)
* Lulu Ren (Monad-Labs)
* Maksymilian Pawlak (R3)
* Manila Gauns (Persistent Systems Limited)
* Marek Scocovsky (ABSA)
* marekdapps
* Mark Lauer (Westpac)

View File

@ -24,8 +24,12 @@ class AlwaysInheritFromSandboxedObject : ClassDefinitionProvider, Emitter {
override fun emit(context: EmitterContext, instruction: Instruction) = context.emit {
if (instruction is TypeInstruction &&
instruction.typeName == OBJECT_NAME) {
instruction.typeName == OBJECT_NAME &&
instruction.operation != Opcodes.ANEWARRAY &&
instruction.operation != Opcodes.MULTIANEWARRAY) {
// When creating new objects, make sure the sandboxed type gets used.
// However, an array is always [java.lang.Object] so we must exclude
// arrays from this so that we can still support arrays of arrays.
new(SANDBOX_OBJECT_NAME, instruction.operation)
preventDefault()
}

View File

@ -0,0 +1,29 @@
package net.corda.djvm.rules.implementation
import net.corda.djvm.code.Emitter
import net.corda.djvm.code.EmitterContext
import net.corda.djvm.code.Instruction
import net.corda.djvm.code.instructions.MemberAccessInstruction
import org.objectweb.asm.Opcodes.*
/**
* We cannot wrap Java array objects - and potentially others - and so these would still
* use the non-deterministic [java.lang.Object.hashCode] by default. Therefore we intercept
* these invocations and redirect them to our [sandbox.java.lang.DJVM] object.
*/
class RewriteObjectMethods : Emitter {
override fun emit(context: EmitterContext, instruction: Instruction) = context.emit {
if (instruction is MemberAccessInstruction && instruction.owner == "java/lang/Object") {
when (instruction.operation) {
INVOKEVIRTUAL -> if (instruction.memberName == "hashCode" && instruction.signature == "()I") {
invokeStatic(
owner = "sandbox/java/lang/DJVM",
name = "hashCode",
descriptor = "(Ljava/lang/Object;)I"
)
preventDefault()
}
}
}
}
}

View File

@ -43,14 +43,15 @@ fun Any.sandbox(): Any {
private fun Array<*>.fromDJVMArray(): Array<*> = Object.fromDJVM(this)
/**
* Use the sandbox's classloader explicitly, because this class
* might belong to the shared parent classloader.
* Use [Class.forName] so that we can also fetch classes for arrays of primitive types.
* Also use the sandbox's classloader explicitly here, because this invoking class
* might belong to a shared parent classloader.
*/
@Throws(ClassNotFoundException::class)
internal fun Class<*>.toDJVMType(): Class<*> = SandboxRuntimeContext.instance.classLoader.loadClass(name.toSandboxPackage())
internal fun Class<*>.toDJVMType(): Class<*> = Class.forName(name.toSandboxPackage(), false, SandboxRuntimeContext.instance.classLoader)
@Throws(ClassNotFoundException::class)
internal fun Class<*>.fromDJVMType(): Class<*> = SandboxRuntimeContext.instance.classLoader.loadClass(name.fromSandboxPackage())
internal fun Class<*>.fromDJVMType(): Class<*> = Class.forName(name.fromSandboxPackage(), false, SandboxRuntimeContext.instance.classLoader)
private fun kotlin.String.toSandboxPackage(): kotlin.String {
return if (startsWith(SANDBOX_PREFIX)) {
@ -139,6 +140,21 @@ private fun createEnumDirectory(clazz: Class<out Enum<*>>): sandbox.java.util.Ma
private val allEnums: sandbox.java.util.Map<Class<out Enum<*>>, Array<out Enum<*>>> = sandbox.java.util.LinkedHashMap()
private val allEnumDirectories: sandbox.java.util.Map<Class<out Enum<*>>, sandbox.java.util.Map<String, out Enum<*>>> = sandbox.java.util.LinkedHashMap()
/**
* Replacement function for Object.hashCode(), because some objects
* (i.e. arrays) cannot be replaced by [sandbox.java.lang.Object].
*/
fun hashCode(obj: Any?): Int {
return if (obj is Object) {
obj.hashCode()
} else if (obj != null) {
System.identityHashCode(obj)
} else {
// Throw the same exception that the JVM would throw in this case.
throw NullPointerException().sanitise()
}
}
/**
* Replacement functions for Class<*>.forName(...) which protect
* against users loading classes from outside the sandbox.
@ -161,7 +177,7 @@ fun classForName(className: kotlin.String, initialize: kotlin.Boolean, classLoad
*/
private fun toSandbox(className: kotlin.String): kotlin.String {
if (bannedClasses.any { it.matches(className) }) {
throw ClassNotFoundException(className)
throw ClassNotFoundException(className).sanitise()
}
return SANDBOX_PREFIX + className
}
@ -200,7 +216,7 @@ fun fromDJVM(t: Throwable?): kotlin.Throwable {
.newInstance(t) as kotlin.Throwable
}
} catch (e: Exception) {
RuleViolationError(e.message)
RuleViolationError(e.message).sanitise()
}
}
}
@ -225,10 +241,20 @@ fun catch(t: kotlin.Throwable): Throwable {
try {
return t.toDJVMThrowable()
} catch (e: Exception) {
throw RuleViolationError(e.message)
throw RuleViolationError(e.message).sanitise()
}
}
/**
* Clean up exception stack trace for throwing.
*/
private fun <T: kotlin.Throwable> T.sanitise(): T {
stackTrace = stackTrace.let {
it.sliceArray(1 until findEntryPointIndex(it))
}
return this
}
/**
* Worker functions to convert [java.lang.Throwable] into [sandbox.java.lang.Throwable].
*/
@ -266,12 +292,16 @@ private fun Class<*>.createJavaThrowable(t: Throwable): kotlin.Throwable {
}
}
private fun sanitiseToDJVM(source: Array<java.lang.StackTraceElement>): Array<StackTraceElement> {
private fun findEntryPointIndex(source: Array<java.lang.StackTraceElement>): Int {
var idx = 0
while (idx < source.size && !isEntryPoint(source[idx])) {
++idx
}
return copyToDJVM(source, 0, idx)
return idx
}
private fun sanitiseToDJVM(source: Array<java.lang.StackTraceElement>): Array<StackTraceElement> {
return copyToDJVM(source, 0, findEntryPointIndex(source))
}
internal fun copyToDJVM(source: Array<java.lang.StackTraceElement>, fromIdx: Int, toIdx: Int): Array<StackTraceElement> {

View File

@ -1,6 +1,7 @@
package net.corda.djvm;
import net.corda.djvm.execution.ExecutionSummaryWithResult;
import net.corda.djvm.execution.SandboxException;
import net.corda.djvm.execution.SandboxExecutor;
import net.corda.djvm.source.ClassSource;
@ -13,12 +14,15 @@ public interface WithJava {
try {
return executor.run(ClassSource.fromClassName(task.getName(), null), input);
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
if (e instanceof SandboxException) {
throw asRuntime(e.getCause());
} else {
throw new RuntimeException(e.getMessage(), e);
throw asRuntime(e);
}
}
}
static RuntimeException asRuntime(Throwable t) {
return (t instanceof RuntimeException) ? (RuntimeException) t : new RuntimeException(t.getMessage(), t);
}
}

View File

@ -0,0 +1,91 @@
package net.corda.djvm.execution;
import net.corda.djvm.TestBase;
import net.corda.djvm.WithJava;
import org.junit.Test;
import java.util.function.Function;
import static net.corda.djvm.messages.Severity.WARNING;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
public class SandboxObjectHashCodeJavaTest extends TestBase {
@Test
public void testHashForArray() {
parentedSandbox(WARNING, true, ctx -> {
SandboxExecutor<Object, Integer> executor = new DeterministicSandboxExecutor<>(ctx.getConfiguration());
ExecutionSummaryWithResult<Integer> output = WithJava.run(executor, ArrayHashCode.class, null);
assertThat(output.getResult()).isEqualTo(0xfed_c0de + 1);
return null;
});
}
@Test
public void testHashForObjectInArray() {
parentedSandbox(WARNING, true, ctx -> {
SandboxExecutor<Object, Integer> executor = new DeterministicSandboxExecutor<>(ctx.getConfiguration());
ExecutionSummaryWithResult<Integer> output = WithJava.run(executor, ObjectInArrayHashCode.class, null);
assertThat(output.getResult()).isEqualTo(0xfed_c0de + 1);
return null;
});
}
@Test
public void testHashForNullObject() {
assertThatExceptionOfType(NullPointerException.class)
.isThrownBy(() -> new HashCode().apply(null));
parentedSandbox(WARNING, true, ctx -> {
SandboxExecutor<Object, Integer> executor = new DeterministicSandboxExecutor<>(ctx.getConfiguration());
assertThatExceptionOfType(NullPointerException.class)
.isThrownBy(() -> WithJava.run(executor, HashCode.class, null));
return null;
});
}
@Test
public void testHashForWrappedInteger() {
parentedSandbox(WARNING, true, ctx -> {
SandboxExecutor<Object, Integer> executor = new DeterministicSandboxExecutor<>(ctx.getConfiguration());
ExecutionSummaryWithResult<Integer> output = WithJava.run(executor, HashCode.class, 1234);
assertThat(output.getResult()).isEqualTo(Integer.hashCode(1234));
return null;
});
}
@Test
public void testHashForWrappedString() {
parentedSandbox(WARNING, true, ctx -> {
SandboxExecutor<Object, Integer> executor = new DeterministicSandboxExecutor<>(ctx.getConfiguration());
ExecutionSummaryWithResult<Integer> output = WithJava.run(executor, HashCode.class, "Burble");
assertThat(output.getResult()).isEqualTo("Burble".hashCode());
return null;
});
}
public static class ObjectInArrayHashCode implements Function<Object, Integer> {
@Override
public Integer apply(Object obj) {
Object[] arr = new Object[1];
arr[0] = new Object();
return arr[0].hashCode();
}
}
public static class ArrayHashCode implements Function<Object, Integer> {
@SuppressWarnings("all")
@Override
public Integer apply(Object obj) {
return new Object[0].hashCode();
}
}
public static class HashCode implements Function<Object, Integer> {
@Override
public Integer apply(Object obj) {
return obj.hashCode();
}
}
}

View File

@ -49,6 +49,7 @@ abstract class TestBase {
HandleExceptionUnwrapper(),
ReturnTypeWrapper(),
RewriteClassMethods(),
RewriteObjectMethods(),
StringConstantWrapper(),
ThrowExceptionWrapper()
)

View File

@ -733,4 +733,32 @@ class SandboxExecutorTest : TestBase() {
return cl.find()
}
}
@Test
fun `test creating arrays of arrays`() = parentedSandbox {
val contractExecutor = DeterministicSandboxExecutor<Any, Array<Any>>(configuration)
contractExecutor.run<ArraysOfArrays>("THINGY").apply {
assertThat(result).isEqualTo(arrayOf(arrayOf("THINGY")))
}
}
class ArraysOfArrays : Function<Any, Array<Any>> {
override fun apply(input: Any): Array<Any> {
return arrayOf(arrayOf(input))
}
}
@Test
fun `test creating arrays of int arrays`() = parentedSandbox {
val contractExecutor = DeterministicSandboxExecutor<Int, Array<IntArray>>(configuration)
contractExecutor.run<ArrayOfIntArrays>(0).apply {
assertThat(result).isEqualTo(arrayOf(intArrayOf(0)))
}
}
class ArrayOfIntArrays : Function<Int, Array<IntArray>> {
override fun apply(input: Int): Array<IntArray> {
return arrayOf(intArrayOf(input))
}
}
}

View File

@ -320,6 +320,12 @@ absolute path to the node's base directory.
Valid values for this property are between 4 (that is the number used for the single threaded state machine in
open source) and the number of flow threads.
:cordappSignerKeyFingerprintBlacklist: List of public keys fingerprints (SHA-256 of public key hash) not allowed as Cordapp JARs signers.
Node will not load Cordapps signed by those keys.
The option takes effect only in production mode and defaults to Corda development keys (``["56CA54E803CB87C8472EBD3FBC6A2F1876E814CEEBF74860BD46997F40729367",
"83088052AF16700457AE2C978A7D8AC38DD6A7C713539D00B897CD03A5E5D31D"]``), in development mode any key is allowed to sign Cordpapp JARs.
Examples
--------

View File

@ -2,8 +2,11 @@ package net.corda.nodeapi.internal
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.Crypto.generateKeyPair
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.hash
import net.corda.core.internal.toX500Name
import net.corda.nodeapi.internal.config.CertificateStore
import net.corda.nodeapi.internal.crypto.*
@ -99,7 +102,7 @@ const val DEV_CA_TRUST_STORE_FILE: String = "cordatruststore.jks"
const val DEV_CA_TRUST_STORE_PASS: String = "trustpass"
const val DEV_CA_TRUST_STORE_PRIVATE_KEY_PASS: String = "trustpasskeypass"
val DEV_CERTIFICATES: List<X509Certificate> get() = listOf(DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate)
val DEV_PUB_KEY_HASHES: List<SecureHash.SHA256> get() = listOf(DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate).map { it.publicKey.hash.sha256() }
// We need a class so that we can get hold of the class loader
internal object DevCaHelper {

View File

@ -96,9 +96,6 @@ dependencies {
// For async logging
compile "com.lmax:disruptor:$disruptor_version"
// JOpt: for command line flags.
compile "net.sf.jopt-simple:jopt-simple:$jopt_simple_version"
// Artemis: for reliable p2p message queues.
// TODO: remove the forced update of commons-collections and beanutils when artemis updates them
compile "org.apache.commons:commons-collections4:${commons_collections_version}"

View File

@ -59,6 +59,7 @@ public class CordaCaplet extends Capsule {
@Override
protected ProcessBuilder prelaunch(List<String> jvmArgs, List<String> args) {
checkJavaVersion();
nodeConfig = parseConfigFile(args);
return super.prelaunch(jvmArgs, args);
}
@ -164,6 +165,14 @@ public class CordaCaplet extends Capsule {
}
}
private static void checkJavaVersion() {
String version = System.getProperty("java.version");
if (version == null || !version.startsWith("1.8")) {
System.err.printf("Error: Unsupported Java version %s; currently only version 1.8 is supported.\n", version);
System.exit(1);
}
}
private void requireCordappsDirExists(File dir) {
try {
if (!dir.mkdir() && !dir.exists()) { // It is unlikely to enter this if-branch, but just in case.

View File

@ -9,6 +9,7 @@ import net.corda.confidential.SwapIdentitiesHandler
import net.corda.core.CordaException
import net.corda.core.concurrent.CordaFuture
import net.corda.core.context.InvocationContext
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.isCRLDistributionPointBlacklisted
import net.corda.core.crypto.newSecureRandom
import net.corda.core.crypto.sign
@ -69,7 +70,6 @@ import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.node.services.upgrade.ContractUpgradeServiceImpl
import net.corda.node.services.vault.NodeVaultService
import net.corda.node.utilities.*
import net.corda.nodeapi.internal.DEV_CERTIFICATES
import net.corda.nodeapi.internal.NodeInfoAndSigned
import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.config.CertificateStore
@ -526,12 +526,20 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
// CorDapp will be generated.
generatedCordapps += VirtualCordapp.generateSimpleNotaryCordapp(versionInfo)
}
val blacklistedCerts = if (configuration.devMode) emptyList() else DEV_CERTIFICATES
val blacklistedKeys = if (configuration.devMode) emptyList()
else configuration.cordappSignerKeyFingerprintBlacklist.mapNotNull {
try {
SecureHash.parse(it)
} catch (e: IllegalArgumentException) {
log.error("Error while adding key fingerprint $it to cordappSignerKeyFingerprintBlacklist due to ${e.message}", e)
throw e
}
}
return JarScanningCordappLoader.fromDirectories(
configuration.cordappDirectories,
versionInfo,
extraCordapps = generatedCordapps,
blacklistedCerts = blacklistedCerts
signerKeyFingerprintBlacklist = blacklistedKeys
)
}

View File

@ -28,7 +28,6 @@ import java.lang.reflect.Modifier
import java.net.URL
import java.net.URLClassLoader
import java.nio.file.Path
import java.security.cert.X509Certificate
import java.util.*
import java.util.jar.JarInputStream
import kotlin.reflect.KClass
@ -42,7 +41,7 @@ import kotlin.streams.toList
class JarScanningCordappLoader private constructor(private val cordappJarPaths: List<RestrictedURL>,
private val versionInfo: VersionInfo = VersionInfo.UNKNOWN,
extraCordapps: List<CordappImpl>,
private val blacklistedCordappSigners: List<X509Certificate> = emptyList()) : CordappLoaderTemplate() {
private val signerKeyFingerprintBlacklist: List<SecureHash.SHA256> = emptyList()) : CordappLoaderTemplate() {
override val cordapps: List<CordappImpl> by lazy {
loadCordapps() + extraCordapps
@ -69,10 +68,10 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
fun fromDirectories(cordappDirs: Collection<Path>,
versionInfo: VersionInfo = VersionInfo.UNKNOWN,
extraCordapps: List<CordappImpl> = emptyList(),
blacklistedCerts: List<X509Certificate> = emptyList()): JarScanningCordappLoader {
signerKeyFingerprintBlacklist: List<SecureHash.SHA256> = emptyList()): JarScanningCordappLoader {
logger.info("Looking for CorDapps in ${cordappDirs.distinct().joinToString(", ", "[", "]")}")
val paths = cordappDirs.distinct().flatMap(this::jarUrlsInDirectory).map { it.restricted() }
return JarScanningCordappLoader(paths, versionInfo, extraCordapps, blacklistedCerts)
return JarScanningCordappLoader(paths, versionInfo, extraCordapps, signerKeyFingerprintBlacklist)
}
/**
@ -80,9 +79,10 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
*
* @param scanJars Uses the JAR URLs provided for classpath scanning and Cordapp detection.
*/
fun fromJarUrls(scanJars: List<URL>, versionInfo: VersionInfo = VersionInfo.UNKNOWN, extraCordapps: List<CordappImpl> = emptyList(), blacklistedCerts: List<X509Certificate> = emptyList()): JarScanningCordappLoader {
fun fromJarUrls(scanJars: List<URL>, versionInfo: VersionInfo = VersionInfo.UNKNOWN, extraCordapps: List<CordappImpl> = emptyList(),
cordappsSignerKeyFingerprintBlacklist: List<SecureHash.SHA256> = emptyList()): JarScanningCordappLoader {
val paths = scanJars.map { it.restricted() }
return JarScanningCordappLoader(paths, versionInfo, extraCordapps, blacklistedCerts)
return JarScanningCordappLoader(paths, versionInfo, extraCordapps, cordappsSignerKeyFingerprintBlacklist)
}
private fun URL.restricted(rootPackageName: String? = null) = RestrictedURL(this, rootPackageName)
@ -112,15 +112,16 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
}
}
.filter {
if (blacklistedCordappSigners.isEmpty()) {
if (signerKeyFingerprintBlacklist.isEmpty()) {
true //Nothing blacklisted, no need to check
} else {
val certificates = it.jarPath.openStream().let(::JarInputStream).use(JarSignatureCollector::collectCertificates)
if (certificates.isEmpty() || (certificates - blacklistedCordappSigners).isNotEmpty())
val blockedCertificates = certificates.filter { it.publicKey.hash.sha256() in signerKeyFingerprintBlacklist }
if (certificates.isEmpty() || (certificates - blockedCertificates).isNotEmpty())
true // Cordapp is not signed or it is signed by at least one non-blacklisted certificate
else {
logger.warn("Not loading CorDapp ${it.info.shortName} (${it.info.vendor}) as it is signed by development key(s) only: " +
"${certificates.intersect(blacklistedCordappSigners).map { it.publicKey }}.")
"${blockedCertificates.map { it.publicKey }}.")
false
}
}

View File

@ -14,6 +14,7 @@ import net.corda.nodeapi.BrokerRpcSslOptions
import net.corda.nodeapi.internal.config.*
import net.corda.nodeapi.internal.persistence.CordaPersistence.DataSourceConfigTag
import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.nodeapi.internal.DEV_PUB_KEY_HASHES
import net.corda.tools.shell.SSHDConfiguration
import org.slf4j.Logger
import java.net.URL
@ -84,6 +85,8 @@ interface NodeConfiguration {
val cordappDirectories: List<Path>
val flowOverrides: FlowOverrideConfig?
val cordappSignerKeyFingerprintBlacklist: List<String>
fun validate(): List<String>
companion object {
@ -234,7 +237,8 @@ data class NodeConfigurationImpl(
override val cordappDirectories: List<Path> = listOf(baseDirectory / CORDAPPS_DIR_NAME_DEFAULT),
override val jmxReporterType: JmxReporterType? = JmxReporterType.JOLOKIA,
private val useOpenSsl: Boolean = false,
override val flowOverrides: FlowOverrideConfig?
override val flowOverrides: FlowOverrideConfig?,
override val cordappSignerKeyFingerprintBlacklist: List<String> = DEV_PUB_KEY_HASHES.map { it.toString() }
) : NodeConfiguration {
companion object {
private val logger = loggerFor<NodeConfigurationImpl>()

View File

@ -1,39 +0,0 @@
package net.corda.node.utilities
import joptsimple.OptionException
import joptsimple.OptionParser
import joptsimple.OptionSet
import kotlin.system.exitProcess
abstract class AbstractArgsParser<out T : Any> {
protected val optionParser = OptionParser()
private val helpOption = optionParser.acceptsAll(listOf("h", "help"), "show help").forHelp()
/**
* Parses the given [args] or exits the process if unable to, printing the help output to stderr.
* If the help option is specified then the process is also shutdown after printing the help output to stdout.
*/
fun parseOrExit(vararg args: String): T {
try {
val optionSet = optionParser.parse(*args)
if (optionSet.has(helpOption)) {
optionParser.printHelpOn(System.out)
exitProcess(0)
}
return doParse(optionSet)
} catch (e: Exception) {
when (e) {
is OptionException, is IllegalArgumentException -> {
System.err.println(e.message ?: "Unable to parse arguments.")
optionParser.printHelpOn(System.err)
exitProcess(1)
}
else -> throw e
}
}
}
fun parse(vararg args: String): T = doParse(optionParser.parse(*args))
protected abstract fun doParse(optionSet: OptionSet): T
}

View File

@ -6,7 +6,7 @@ import net.corda.core.internal.packageName
import net.corda.node.VersionInfo
import net.corda.testing.node.internal.TestCordappDirectories
import net.corda.testing.node.internal.cordappForPackages
import net.corda.nodeapi.internal.DEV_CERTIFICATES
import net.corda.nodeapi.internal.DEV_PUB_KEY_HASHES
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import java.nio.file.Paths
@ -147,21 +147,21 @@ class JarScanningCordappLoaderTest {
@Test
fun `cordapp classloader loads app signed by allowed certificate`() {
val jar = JarScanningCordappLoaderTest::class.java.getResource("signed/signed-by-dev-key.jar")!!
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), blacklistedCerts = emptyList())
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), cordappsSignerKeyFingerprintBlacklist = emptyList())
assertThat(loader.cordapps).hasSize(1)
}
@Test
fun `cordapp classloader does not load app signed by blacklisted certificate`() {
val jar = JarScanningCordappLoaderTest::class.java.getResource("signed/signed-by-dev-key.jar")!!
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), blacklistedCerts = DEV_CERTIFICATES)
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), cordappsSignerKeyFingerprintBlacklist = DEV_PUB_KEY_HASHES)
assertThat(loader.cordapps).hasSize(0)
}
@Test
fun `cordapp classloader loads app signed by both allowed and non-blacklisted certificate`() {
val jar = JarScanningCordappLoaderTest::class.java.getResource("signed/signed-by-two-keys.jar")!!
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), blacklistedCerts = DEV_CERTIFICATES)
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), cordappsSignerKeyFingerprintBlacklist = DEV_PUB_KEY_HASHES)
assertThat(loader.cordapps).hasSize(1)
}
}

View File

@ -71,6 +71,10 @@ dependencies {
compile project(":client:jackson")
compile project(":test-utils")
compile project(path: ":samples:irs-demo:cordapp", configuration: "demoArtifacts")
// JOpt: for command line flags.
compile "net.sf.jopt-simple:jopt-simple:$jopt_simple_version"
testCompile('org.springframework.boot:spring-boot-starter-test') {
exclude module: "spring-boot-starter-logging"
exclude module: "logback-classic"

View File

@ -51,6 +51,9 @@ dependencies {
compile 'org.controlsfx:controlsfx:8.40.12'
// This provide com.apple.eawt stub for non-mac system.
compile 'com.yuvimasory:orange-extensions:1.3.0'
// JOpt: for command line flags.
compile "net.sf.jopt-simple:jopt-simple:$jopt_simple_version"
}
tasks.withType(JavaCompile) {

View File

@ -137,10 +137,10 @@ class Main : App(MainView::class) {
}
/**
* This main method will starts 5 nodes (Notary, USA Bank, UK Bank, Bob and Alice) locally for UI testing,
* they will be on localhost ports 20005, 20008, 20011, 20014 and 20017 respectively.
* This main method will start 5 nodes (Notary, USA Bank, UK Bank, Bob and Alice) locally for UI testing,
* which will bind to ports 20005, 20008, 20011, 20014 and 20017 locally.
*
* The simulation start with pre-allocating chunks of cash to each of the party in 2 currencies (USD, GBP), then it enter a loop to generate random events.
* The simulation starts by pre-allocating chunks of cash to each of the parties in 2 currencies (USD, GBP), then it enters a loop which generates random events.
* On each iteration, the issuers will execute a Cash Issue or Cash Exit flow (at a 9:1 ratio) and a random party will execute a move of cash to another random party.
*/
fun main(args: Array<String>) {

View File

@ -39,10 +39,6 @@ dependencies {
// Jackson support: serialisation to/from JSON, YAML, etc.
compile project(':client:jackson')
// JOpt: for command line flags.
compile "net.sf.jopt-simple:jopt-simple:$jopt_simple_version"
// CRaSH: An embeddable monitoring and admin shell with support for adding new commands written in Groovy.
compile("com.github.corda.crash:crash.shell:$crash_version") {
exclude group: "org.slf4j", module: "slf4j-jdk14"