Merge pull request #1275 from corda/bogdan-merge-160618

OS-ENT merge
This commit is contained in:
bpaunescu 2018-07-17 12:37:45 +01:00 committed by GitHub
commit cb4e428ec4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 101 additions and 211 deletions

0
CONTRIBUTORS.md Normal file
View File

View File

@ -48,10 +48,14 @@ fun Iterable<ContractState>.sumCashOrZero(currency: Issued<Currency>): Amount<Is
} }
/** Sums the asset states in the list, returning null if there are none. */ /** Sums the asset states in the list, returning null if there are none. */
fun <T : Any> Iterable<ContractState>.sumFungibleOrNull() = filterIsInstance<FungibleAsset<T>>().map { it.amount }.sumOrNull() fun <T : Any> Iterable<ContractState>.sumFungibleOrNull(): Amount<Issued<T>>? {
return filterIsInstance<FungibleAsset<T>>().map { it.amount }.sumOrNull()
}
/** Sums the asset states in the list, returning zero of the given token if there are none. */ /** Sums the asset states in the list, returning zero of the given token if there are none. */
fun <T : Any> Iterable<ContractState>.sumFungibleOrZero(token: Issued<T>) = filterIsInstance<FungibleAsset<T>>().map { it.amount }.sumOrZero(token) fun <T : Any> Iterable<ContractState>.sumFungibleOrZero(token: Issued<T>): Amount<Issued<T>> {
return filterIsInstance<FungibleAsset<T>>().map { it.amount }.sumOrZero(token)
}
/** /**
* Sums the obligation states in the list, throwing an exception if there are none. All state objects in the * Sums the obligation states in the list, throwing an exception if there are none. All state objects in the

View File

@ -32,6 +32,7 @@ import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.time.temporal.Temporal import java.time.temporal.Temporal
import java.util.* import java.util.*
import javax.security.auth.x500.X500Principal
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.reflect.KType import kotlin.reflect.KType
@ -138,6 +139,7 @@ private fun Config.getSingleValue(path: String, type: KType, onUnknownKeys: (Set
Path::class -> Paths.get(getString(path)) Path::class -> Paths.get(getString(path))
URL::class -> URL(getString(path)) URL::class -> URL(getString(path))
UUID::class -> UUID.fromString(getString(path)) UUID::class -> UUID.fromString(getString(path))
X500Principal::class -> X500Principal(getString(path))
CordaX500Name::class -> { CordaX500Name::class -> {
when (getValue(path).valueType()) { when (getValue(path).valueType()) {
ConfigValueType.OBJECT -> getConfig(path).parseAs(onUnknownKeys) ConfigValueType.OBJECT -> getConfig(path).parseAs(onUnknownKeys)
@ -183,6 +185,7 @@ private fun Config.getCollectionValue(path: String, type: KType, onUnknownKeys:
NetworkHostAndPort::class -> getStringList(path).map(NetworkHostAndPort.Companion::parse) NetworkHostAndPort::class -> getStringList(path).map(NetworkHostAndPort.Companion::parse)
Path::class -> getStringList(path).map { Paths.get(it) } Path::class -> getStringList(path).map { Paths.get(it) }
URL::class -> getStringList(path).map(::URL) URL::class -> getStringList(path).map(::URL)
X500Principal::class -> getStringList(path).map(::X500Principal)
UUID::class -> getStringList(path).map { UUID.fromString(it) } UUID::class -> getStringList(path).map { UUID.fromString(it) }
CordaX500Name::class -> getStringList(path).map(CordaX500Name.Companion::parse) CordaX500Name::class -> getStringList(path).map(CordaX500Name.Companion::parse)
Properties::class -> getConfigList(path).map(Config::toProperties) Properties::class -> getConfigList(path).map(Config::toProperties)
@ -236,7 +239,7 @@ private fun Any.toConfigMap(): Map<String, Any> {
val configValue = if (value is String || value is Boolean || value is Number) { val configValue = if (value is String || value is Boolean || value is Number) {
// These types are supported by Config as use as is // These types are supported by Config as use as is
value value
} else if (value is Temporal || value is NetworkHostAndPort || value is CordaX500Name || value is Path || value is URL || value is UUID) { } else if (value is Temporal || value is NetworkHostAndPort || value is CordaX500Name || value is Path || value is URL || value is UUID || value is X500Principal) {
// These types make sense to be represented as Strings and the exact inverse parsing function for use in parseAs // These types make sense to be represented as Strings and the exact inverse parsing function for use in parseAs
value.toString() value.toString()
} else if (value is Enum<*>) { } else if (value is Enum<*>) {
@ -271,6 +274,7 @@ private fun Iterable<*>.toConfigIterable(field: Field): Iterable<Any?> {
NetworkHostAndPort::class.java -> map(Any?::toString) NetworkHostAndPort::class.java -> map(Any?::toString)
Path::class.java -> map(Any?::toString) Path::class.java -> map(Any?::toString)
URL::class.java -> map(Any?::toString) URL::class.java -> map(Any?::toString)
X500Principal::class.java -> map(Any?::toString)
UUID::class.java -> map(Any?::toString) UUID::class.java -> map(Any?::toString)
CordaX500Name::class.java -> map(Any?::toString) CordaX500Name::class.java -> map(Any?::toString)
Properties::class.java -> map { ConfigFactory.parseMap(uncheckedCast(it)).root() } Properties::class.java -> map { ConfigFactory.parseMap(uncheckedCast(it)).root() }

View File

@ -40,7 +40,7 @@ fun loadOrCreateKeyStore(keyStoreFilePath: Path, storePassword: String): KeyStor
keyStoreFilePath.read { keyStore.load(it, pass) } keyStoreFilePath.read { keyStore.load(it, pass) }
} else { } else {
keyStore.load(null, pass) keyStore.load(null, pass)
keyStoreFilePath.parent.createDirectories() keyStoreFilePath.toAbsolutePath().parent?.createDirectories()
keyStoreFilePath.write { keyStore.store(it, pass) } keyStoreFilePath.write { keyStore.store(it, pass) }
} }
return keyStore return keyStore

View File

@ -11,6 +11,7 @@
package net.corda.nodeapi.internal.protonwrapper.netty package net.corda.nodeapi.internal.protonwrapper.netty
import io.netty.handler.ssl.SslHandler import io.netty.handler.ssl.SslHandler
import net.corda.core.crypto.newSecureRandom
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.toHex import net.corda.core.utilities.toHex
@ -117,7 +118,7 @@ internal fun createClientSslHelper(target: NetworkHostAndPort,
val sslContext = SSLContext.getInstance("TLS") val sslContext = SSLContext.getInstance("TLS")
val keyManagers = keyManagerFactory.keyManagers val keyManagers = keyManagerFactory.keyManagers
val trustManagers = trustManagerFactory.trustManagers.filterIsInstance(X509ExtendedTrustManager::class.java).map { LoggingTrustManagerWrapper(it) }.toTypedArray() val trustManagers = trustManagerFactory.trustManagers.filterIsInstance(X509ExtendedTrustManager::class.java).map { LoggingTrustManagerWrapper(it) }.toTypedArray()
sslContext.init(keyManagers, trustManagers, SecureRandom()) sslContext.init(keyManagers, trustManagers, newSecureRandom())
val sslEngine = sslContext.createSSLEngine(target.host, target.port) val sslEngine = sslContext.createSSLEngine(target.host, target.port)
sslEngine.useClientMode = true sslEngine.useClientMode = true
sslEngine.enabledProtocols = ArtemisTcpTransport.TLS_VERSIONS.toTypedArray() sslEngine.enabledProtocols = ArtemisTcpTransport.TLS_VERSIONS.toTypedArray()
@ -131,7 +132,7 @@ internal fun createServerSslHelper(keyManagerFactory: KeyManagerFactory,
val sslContext = SSLContext.getInstance("TLS") val sslContext = SSLContext.getInstance("TLS")
val keyManagers = keyManagerFactory.keyManagers val keyManagers = keyManagerFactory.keyManagers
val trustManagers = trustManagerFactory.trustManagers.filterIsInstance(X509ExtendedTrustManager::class.java).map { LoggingTrustManagerWrapper(it) }.toTypedArray() val trustManagers = trustManagerFactory.trustManagers.filterIsInstance(X509ExtendedTrustManager::class.java).map { LoggingTrustManagerWrapper(it) }.toTypedArray()
sslContext.init(keyManagers, trustManagers, SecureRandom()) sslContext.init(keyManagers, trustManagers, newSecureRandom())
val sslEngine = sslContext.createSSLEngine() val sslEngine = sslContext.createSSLEngine()
sslEngine.useClientMode = false sslEngine.useClientMode = false
sslEngine.needClientAuth = true sslEngine.needClientAuth = true

View File

@ -24,6 +24,7 @@ import java.nio.file.Path
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.util.* import java.util.*
import javax.security.auth.x500.X500Principal
import kotlin.reflect.full.primaryConstructor import kotlin.reflect.full.primaryConstructor
class ConfigParsingTest { class ConfigParsingTest {
@ -94,6 +95,11 @@ class ConfigParsingTest {
testPropertyType<URLData, URLListData, URL>(URL("http://localhost:1234"), URL("http://localhost:1235"), valuesToString = true) testPropertyType<URLData, URLListData, URL>(URL("http://localhost:1234"), URL("http://localhost:1235"), valuesToString = true)
} }
@Test
fun X500Principal() {
testPropertyType<X500PrincipalData, X500PrincipalListData, X500Principal>(X500Principal("C=US, L=New York, CN=Corda Root CA, OU=Corda, O=R3 HoldCo LLC"), X500Principal("O=Bank A,L=London,C=GB"), valuesToString = true)
}
@Test @Test
fun UUID() { fun UUID() {
testPropertyType<UUIDData, UUIDListData, UUID>(UUID.randomUUID(), UUID.randomUUID(), valuesToString = true) testPropertyType<UUIDData, UUIDListData, UUID>(UUID.randomUUID(), UUID.randomUUID(), valuesToString = true)
@ -334,6 +340,8 @@ class ConfigParsingTest {
data class PathListData(override val values: List<Path>) : ListData<Path> data class PathListData(override val values: List<Path>) : ListData<Path>
data class URLData(override val value: URL) : SingleData<URL> data class URLData(override val value: URL) : SingleData<URL>
data class URLListData(override val values: List<URL>) : ListData<URL> data class URLListData(override val values: List<URL>) : ListData<URL>
data class X500PrincipalData(override val value: X500Principal) : SingleData<X500Principal>
data class X500PrincipalListData(override val values: List<X500Principal>) : ListData<X500Principal>
data class UUIDData(override val value: UUID) : SingleData<UUID> data class UUIDData(override val value: UUID) : SingleData<UUID>
data class UUIDListData(override val values: List<UUID>) : ListData<UUID> data class UUIDListData(override val values: List<UUID>) : ListData<UUID>
data class CordaX500NameData(override val value: CordaX500Name) : SingleData<CordaX500Name> data class CordaX500NameData(override val value: CordaX500Name) : SingleData<CordaX500Name>

View File

@ -13,6 +13,7 @@ package net.corda.nodeapi.internal.crypto
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512 import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
import net.corda.core.crypto.Crypto.generateKeyPair import net.corda.core.crypto.Crypto.generateKeyPair
import net.corda.core.crypto.newSecureRandom
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext
@ -248,7 +249,7 @@ class X509UtilitiesTest {
val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustMgrFactory.init(trustStore) trustMgrFactory.init(trustStore)
val trustManagers = trustMgrFactory.trustManagers val trustManagers = trustMgrFactory.trustManagers
context.init(keyManagers, trustManagers, SecureRandom()) context.init(keyManagers, trustManagers, newSecureRandom())
val serverSocketFactory = context.serverSocketFactory val serverSocketFactory = context.serverSocketFactory
val clientSocketFactory = context.socketFactory val clientSocketFactory = context.socketFactory

View File

@ -14,6 +14,7 @@ import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever import com.nhaarman.mockito_kotlin.whenever
import io.netty.channel.EventLoopGroup import io.netty.channel.EventLoopGroup
import io.netty.channel.nio.NioEventLoopGroup import io.netty.channel.nio.NioEventLoopGroup
import net.corda.core.crypto.newSecureRandom
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.toFuture import net.corda.core.toFuture
@ -157,7 +158,7 @@ class ProtonWrapperTests {
val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustMgrFactory.init(trustStore) trustMgrFactory.init(trustStore)
val trustManagers = trustMgrFactory.trustManagers val trustManagers = trustMgrFactory.trustManagers
context.init(keyManagers, trustManagers, SecureRandom()) context.init(keyManagers, trustManagers, newSecureRandom())
val serverSocketFactory = context.serverSocketFactory val serverSocketFactory = context.serverSocketFactory

View File

@ -148,12 +148,7 @@ import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.NodeInfoAndSigned
import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.*
import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException
import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
import net.corda.nodeapi.internal.persistence.SchemaMigration
import net.corda.nodeapi.internal.persistence.isH2Database
import net.corda.nodeapi.internal.storeLegalIdentity import net.corda.nodeapi.internal.storeLegalIdentity
import net.corda.tools.shell.InteractiveShell import net.corda.tools.shell.InteractiveShell
import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.activemq.artemis.utils.ReusableLatch
@ -1187,7 +1182,7 @@ fun configureDatabase(hikariProperties: Properties,
when { when {
ex is HikariPool.PoolInitializationException -> throw CouldNotCreateDataSourceException("Could not connect to the database. Please check your JDBC connection URL, or the connectivity to the database.", ex) ex is HikariPool.PoolInitializationException -> throw CouldNotCreateDataSourceException("Could not connect to the database. Please check your JDBC connection URL, or the connectivity to the database.", ex)
ex.cause is ClassNotFoundException -> throw CouldNotCreateDataSourceException("Could not find the database driver class. Please add it to the 'drivers' folder. See: https://docs.corda.net/corda-configuration-file.html") ex.cause is ClassNotFoundException -> throw CouldNotCreateDataSourceException("Could not find the database driver class. Please add it to the 'drivers' folder. See: https://docs.corda.net/corda-configuration-file.html")
else -> throw ex else -> throw CouldNotCreateDataSourceException("Could not create the DataSource: ${ex.message}", ex)
} }
} }
} }

View File

@ -203,7 +203,7 @@ open class Node(configuration: NodeConfiguration,
if (!configuration.messagingServerExternal) { if (!configuration.messagingServerExternal) {
val brokerBindAddress = configuration.messagingServerAddress ?: NetworkHostAndPort("0.0.0.0", configuration.p2pAddress.port) val brokerBindAddress = configuration.messagingServerAddress ?: NetworkHostAndPort("0.0.0.0", configuration.p2pAddress.port)
messageBroker = ArtemisMessagingServer(configuration, brokerBindAddress, networkParameters.maxMessageSize, info.legalIdentities.map { it.owningKey }) messageBroker = ArtemisMessagingServer(configuration, brokerBindAddress, networkParameters.maxMessageSize)
} }
val serverAddress = configuration.messagingServerAddress val serverAddress = configuration.messagingServerAddress

View File

@ -33,6 +33,7 @@ import java.net.URL
import java.nio.file.Path import java.nio.file.Path
import java.time.Duration import java.time.Duration
import java.util.* import java.util.*
import javax.security.auth.x500.X500Principal
val Int.MB: Long get() = this * 1024L * 1024L val Int.MB: Long get() = this * 1024L * 1024L
@ -78,7 +79,7 @@ interface NodeConfiguration : NodeSSLConfiguration {
val drainingModePollPeriod: Duration get() = Duration.ofSeconds(5) val drainingModePollPeriod: Duration get() = Duration.ofSeconds(5)
val extraNetworkMapKeys: List<UUID> val extraNetworkMapKeys: List<UUID>
val tlsCertCrlDistPoint: URL? val tlsCertCrlDistPoint: URL?
val tlsCertCrlIssuer: String? val tlsCertCrlIssuer: X500Principal?
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
@ -222,7 +223,7 @@ data class NodeConfigurationImpl(
override val compatibilityZoneURL: URL? = null, override val compatibilityZoneURL: URL? = null,
override var networkServices: NetworkServicesConfig? = null, override var networkServices: NetworkServicesConfig? = null,
override val tlsCertCrlDistPoint: URL? = null, override val tlsCertCrlDistPoint: URL? = null,
override val tlsCertCrlIssuer: String? = null, override val tlsCertCrlIssuer: X500Principal? = null,
override val rpcUsers: List<User>, override val rpcUsers: List<User>,
override val security: SecurityConfiguration? = null, override val security: SecurityConfiguration? = null,
override val verifierType: VerifierType, override val verifierType: VerifierType,
@ -296,11 +297,6 @@ data class NodeConfigurationImpl(
if (tlsCertCrlDistPoint == null) { if (tlsCertCrlDistPoint == null) {
errors += "tlsCertCrlDistPoint needs to be specified when tlsCertCrlIssuer is not NULL" errors += "tlsCertCrlDistPoint needs to be specified when tlsCertCrlIssuer is not NULL"
} }
try {
X500Name(tlsCertCrlIssuer)
} catch (e: Exception) {
errors += "Error when parsing tlsCertCrlIssuer: ${e.message}"
}
} }
if (!crlCheckSoftFail && tlsCertCrlDistPoint == null) { if (!crlCheckSoftFail && tlsCertCrlDistPoint == null) {
errors += "tlsCertCrlDistPoint needs to be specified when crlCheckSoftFail is FALSE" errors += "tlsCertCrlDistPoint needs to be specified when crlCheckSoftFail is FALSE"

View File

@ -25,18 +25,14 @@ import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.ArtemisTcpTransport.Companion.p2pAcceptorTcpTransport import net.corda.nodeapi.ArtemisTcpTransport.Companion.p2pAcceptorTcpTransport
import net.corda.nodeapi.internal.AmqpMessageSizeChecksInterceptor import net.corda.nodeapi.internal.AmqpMessageSizeChecksInterceptor
import net.corda.nodeapi.internal.ArtemisMessageSizeChecksInterceptor import net.corda.nodeapi.internal.ArtemisMessageSizeChecksInterceptor
import net.corda.nodeapi.internal.ArtemisMessagingComponent
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.JOURNAL_HEADER_SIZE import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.JOURNAL_HEADER_SIZE
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATIONS_ADDRESS import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATIONS_ADDRESS
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX
import net.corda.nodeapi.internal.requireOnDefaultFileSystem import net.corda.nodeapi.internal.requireOnDefaultFileSystem
import org.apache.activemq.artemis.api.core.RoutingType
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
import org.apache.activemq.artemis.core.config.Configuration import org.apache.activemq.artemis.core.config.Configuration
import org.apache.activemq.artemis.core.config.CoreAddressConfiguration
import org.apache.activemq.artemis.core.config.CoreQueueConfiguration
import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl
import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration
import org.apache.activemq.artemis.core.security.Role import org.apache.activemq.artemis.core.security.Role
@ -45,7 +41,6 @@ import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl
import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager
import java.io.IOException import java.io.IOException
import java.security.KeyStoreException import java.security.KeyStoreException
import java.security.PublicKey
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
import javax.security.auth.login.AppConfigurationEntry import javax.security.auth.login.AppConfigurationEntry
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag.REQUIRED import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag.REQUIRED
@ -66,8 +61,7 @@ import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag.RE
@ThreadSafe @ThreadSafe
class ArtemisMessagingServer(private val config: NodeConfiguration, class ArtemisMessagingServer(private val config: NodeConfiguration,
private val messagingServerAddress: NetworkHostAndPort, private val messagingServerAddress: NetworkHostAndPort,
private val maxMessageSize: Int, private val maxMessageSize: Int) : ArtemisBroker, SingletonSerializeAsToken() {
private val identities: List<PublicKey> = emptyList()) : ArtemisBroker, SingletonSerializeAsToken() {
companion object { companion object {
private val log = contextLogger() private val log = contextLogger()
} }
@ -131,48 +125,30 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
log.info("P2P messaging server listening on $messagingServerAddress") log.info("P2P messaging server listening on $messagingServerAddress")
} }
private fun createArtemisConfig(): Configuration { private fun createArtemisConfig() = SecureArtemisConfiguration().apply {
val addressConfigs = identities.map { val artemisDir = config.baseDirectory / "artemis"
val queueName = ArtemisMessagingComponent.RemoteInboxAddress(it).queueName bindingsDirectory = (artemisDir / "bindings").toString()
log.info("Configuring address $queueName") journalDirectory = (artemisDir / "journal").toString()
val queueConfig = CoreQueueConfiguration().apply { largeMessagesDirectory = (artemisDir / "large-messages").toString()
address = queueName acceptorConfigurations = mutableSetOf(p2pAcceptorTcpTransport(NetworkHostAndPort(messagingServerAddress.host, messagingServerAddress.port), config))
name = queueName // Enable built in message deduplication. Note we still have to do our own as the delayed commits
routingType = RoutingType.ANYCAST // and our own definition of commit mean that the built in deduplication cannot remove all duplicates.
isExclusive = true idCacheSize = 2000 // Artemis Default duplicate cache size i.e. a guess
} isPersistIDCache = true
CoreAddressConfiguration().apply { isPopulateValidatedUser = true
name = queueName journalBufferSize_NIO = maxMessageSize + JOURNAL_HEADER_SIZE // Artemis default is 490KiB - required to address IllegalArgumentException (when Artemis uses Java NIO): Record is too large to store.
queueConfigurations = listOf(queueConfig) journalBufferSize_AIO = maxMessageSize + JOURNAL_HEADER_SIZE // Required to address IllegalArgumentException (when Artemis uses Linux Async IO): Record is too large to store.
addRoutingType(RoutingType.ANYCAST) journalFileSize = maxMessageSize + JOURNAL_HEADER_SIZE// The size of each journal file in bytes. Artemis default is 10MiB.
} managementNotificationAddress = SimpleString(NOTIFICATIONS_ADDRESS)
connectionTtlCheckInterval = config.enterpriseConfiguration.tuning.brokerConnectionTtlCheckIntervalMs
// JMX enablement
if (config.jmxMonitoringHttpPort != null) {
isJMXManagementEnabled = true
isJMXUseBrokerName = true
} }
return SecureArtemisConfiguration().apply {
val artemisDir = config.baseDirectory / "artemis"
bindingsDirectory = (artemisDir / "bindings").toString()
journalDirectory = (artemisDir / "journal").toString()
largeMessagesDirectory = (artemisDir / "large-messages").toString()
acceptorConfigurations = mutableSetOf(p2pAcceptorTcpTransport(NetworkHostAndPort(messagingServerAddress.host, messagingServerAddress.port), config))
// Enable built in message deduplication. Note we still have to do our own as the delayed commits
// and our own definition of commit mean that the built in deduplication cannot remove all duplicates.
idCacheSize = 2000 // Artemis Default duplicate cache size i.e. a guess
isPersistIDCache = true
isPopulateValidatedUser = true
journalBufferSize_NIO = maxMessageSize + JOURNAL_HEADER_SIZE // Artemis default is 490KiB - required to address IllegalArgumentException (when Artemis uses Java NIO): Record is too large to store.
journalBufferSize_AIO = maxMessageSize + JOURNAL_HEADER_SIZE // Required to address IllegalArgumentException (when Artemis uses Linux Async IO): Record is too large to store.
journalFileSize = maxMessageSize + JOURNAL_HEADER_SIZE// The size of each journal file in bytes. Artemis default is 10MiB.
managementNotificationAddress = SimpleString(NOTIFICATIONS_ADDRESS)
connectionTtlCheckInterval = config.enterpriseConfiguration.tuning.brokerConnectionTtlCheckIntervalMs
addressConfigurations = addressConfigs
// JMX enablement }.configureAddressSecurity()
if (config.jmxMonitoringHttpPort != null) {
isJMXManagementEnabled = true
isJMXUseBrokerName = true
}
}.configureAddressSecurity()
}
/** /**
* Authenticated clients connecting to us fall in one of the following groups: * Authenticated clients connecting to us fall in one of the following groups:

View File

@ -293,20 +293,20 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
} }
override fun validateAndGetTlsCrlIssuerCert(): X509Certificate? { override fun validateAndGetTlsCrlIssuerCert(): X509Certificate? {
config.tlsCertCrlIssuer ?: return null val tlsCertCrlIssuer = config.tlsCertCrlIssuer
val tlsCertCrlIssuerPrincipal = X500Principal(config.tlsCertCrlIssuer) tlsCertCrlIssuer ?: return null
if (principalMatchesCertificatePrincipal(tlsCertCrlIssuerPrincipal, rootCert)) { if (principalMatchesCertificatePrincipal(tlsCertCrlIssuer, rootCert)) {
return rootCert return rootCert
} }
return if (config.trustStoreFile.exists()) { return if (config.trustStoreFile.exists()) {
findMatchingCertificate(tlsCertCrlIssuerPrincipal, config.loadTrustStore()) findMatchingCertificate(tlsCertCrlIssuer, config.loadTrustStore())
} else { } else {
null null
} }
} }
override fun isTlsCrlIssuerCertRequired(): Boolean { override fun isTlsCrlIssuerCertRequired(): Boolean {
return !config.tlsCertCrlIssuer.isNullOrEmpty() return config.tlsCertCrlIssuer != null
} }
private fun findMatchingCertificate(principal: X500Principal, trustStore: X509KeyStore): X509Certificate? { private fun findMatchingCertificate(principal: X500Principal, trustStore: X509KeyStore): X509Certificate? {

View File

@ -8,7 +8,7 @@
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited. * Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/ */
package net.corda.node package net.corda.node.internal.cordapp
import com.typesafe.config.Config import com.typesafe.config.Config
import com.typesafe.config.ConfigException import com.typesafe.config.ConfigException
@ -16,7 +16,6 @@ import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigRenderOptions import com.typesafe.config.ConfigRenderOptions
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.internal.writeText import net.corda.core.internal.writeText
import net.corda.node.internal.cordapp.CordappConfigFileProvider
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.Paths import java.nio.file.Paths

View File

@ -57,7 +57,6 @@ import org.junit.rules.RuleChain
import org.slf4j.MDC import org.slf4j.MDC
import java.security.PublicKey import java.security.PublicKey
import java.util.concurrent.Future import java.util.concurrent.Future
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import kotlin.test.assertNotEquals import kotlin.test.assertNotEquals
@ -221,7 +220,7 @@ class TimedFlowTests {
val stx = requestPayload.signedTransaction val stx = requestPayload.signedTransaction
subFlow(ResolveTransactionsFlow(stx, otherSideSession)) subFlow(ResolveTransactionsFlow(stx, otherSideSession))
if (TimedFlowTests.requestsReceived.getAndIncrement() == 0) { if (requestsReceived.getAndIncrement() == 0) {
logger.info("Ignoring") logger.info("Ignoring")
// Waiting forever // Waiting forever
stateMachine.suspend(FlowIORequest.WaitForLedgerCommit(SecureHash.randomSHA256()), false) stateMachine.suspend(FlowIORequest.WaitForLedgerCommit(SecureHash.randomSHA256()), false)

View File

@ -28,6 +28,7 @@ import java.net.InetAddress
import java.net.URL import java.net.URL
import java.net.URI import java.net.URI
import java.nio.file.Paths import java.nio.file.Paths
import javax.security.auth.x500.X500Principal
import java.util.* import java.util.*
import kotlin.test.* import kotlin.test.*
@ -48,13 +49,6 @@ class NodeConfigurationImplTest {
assertThat(configValidationResult.first()).contains("tlsCertCrlDistPoint needs to be specified when tlsCertCrlIssuer is not NULL") assertThat(configValidationResult.first()).contains("tlsCertCrlDistPoint needs to be specified when tlsCertCrlIssuer is not NULL")
} }
@Test
fun `tlsCertCrlIssuer validation fails when misconfigured`() {
val configValidationResult = configTlsCertCrlOptions(URL("http://test.com/crl"), "Corda Root CA").validate()
assertTrue { configValidationResult.isNotEmpty() }
assertThat(configValidationResult.first()).contains("Error when parsing tlsCertCrlIssuer:")
}
@Test @Test
fun `can't have tlsCertCrlDistPoint null when crlCheckSoftFail is false`() { fun `can't have tlsCertCrlDistPoint null when crlCheckSoftFail is false`() {
val configValidationResult = configTlsCertCrlOptions(null, null, false).validate() val configValidationResult = configTlsCertCrlOptions(null, null, false).validate()
@ -265,7 +259,7 @@ class NodeConfigurationImplTest {
} }
private fun configTlsCertCrlOptions(tlsCertCrlDistPoint: URL?, tlsCertCrlIssuer: String?, crlCheckSoftFail: Boolean = true): NodeConfiguration { private fun configTlsCertCrlOptions(tlsCertCrlDistPoint: URL?, tlsCertCrlIssuer: String?, crlCheckSoftFail: Boolean = true): NodeConfiguration {
return testConfiguration.copy(tlsCertCrlDistPoint = tlsCertCrlDistPoint, tlsCertCrlIssuer = tlsCertCrlIssuer, crlCheckSoftFail = crlCheckSoftFail) return testConfiguration.copy(tlsCertCrlDistPoint = tlsCertCrlDistPoint, tlsCertCrlIssuer = tlsCertCrlIssuer?.let { X500Principal(it) }, crlCheckSoftFail = crlCheckSoftFail)
} }
private fun testConfiguration(dataSourceProperties: Properties): NodeConfigurationImpl { private fun testConfiguration(dataSourceProperties: Properties): NodeConfigurationImpl {

View File

@ -10,11 +10,17 @@
package net.corda.bank package net.corda.bank
import net.corda.client.rpc.CordaRPCClient
import net.corda.core.contracts.withoutIssuer
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.POUNDS import net.corda.finance.contracts.asset.Cash
import net.corda.finance.utils.sumCash
import net.corda.testing.core.BOC_NAME
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.demorun.nodeRunner import net.corda.testing.node.internal.demorun.nodeRunner
import org.assertj.core.api.Assertions.assertThat
import org.junit.ClassRule import org.junit.ClassRule
import org.junit.Test import org.junit.Test
@ -27,9 +33,19 @@ class BankOfCordaCordformTest : IntegrationTest() {
@Test @Test
fun `run demo`() { fun `run demo`() {
BankOfCordaCordform().nodeRunner().scanPackages(listOf("net.corda.finance")).deployAndRunNodesThen { BankOfCordaCordform().nodeRunner().scanPackages(listOf("net.corda.finance")).deployAndRunNodesAndThen {
IssueCash.requestWebIssue(30000.POUNDS)
IssueCash.requestRpcIssue(20000.DOLLARS) IssueCash.requestRpcIssue(20000.DOLLARS)
CordaRPCClient(NetworkHostAndPort("localhost", BOC_RPC_PORT)).use(BOC_RPC_USER, BOC_RPC_PWD) {
assertThat(it.proxy.vaultQuery(Cash.State::class.java).states).isEmpty() // All of the issued cash is transferred
}
CordaRPCClient(NetworkHostAndPort("localhost", BIGCORP_RPC_PORT)).use(BIGCORP_RPC_USER, BIGCORP_RPC_PWD) {
val cashStates = it.proxy.vaultQuery(Cash.State::class.java).states.map { it.state.data }
val knownOwner = it.proxy.wellKnownPartyFromAnonymous(cashStates.map { it.owner }.toSet().single())
assertThat(knownOwner?.name).isEqualTo(BIGCORP_NAME)
val totalCash = cashStates.sumCash()
assertThat(totalCash.token.issuer.party.nameOrNull()).isEqualTo(BOC_NAME)
assertThat(totalCash.withoutIssuer()).isEqualTo(20000.DOLLARS)
}
} }
} }
} }

View File

@ -1,112 +0,0 @@
/*
* 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.bank
import net.corda.client.rpc.CordaRPCClient
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow
import net.corda.core.node.services.Vault
import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.finance.DOLLARS
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashIssueAndPaymentFlow
import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.testing.core.*
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver
import net.corda.testing.internal.IntegrationTest
import net.corda.testing.internal.IntegrationTestSchemas
import net.corda.testing.internal.toDatabaseSchemaName
import net.corda.testing.node.User
import org.junit.ClassRule
import org.junit.Test
class BankOfCordaRPCClientTest : IntegrationTest() {
companion object {
@ClassRule
@JvmField
val databaseSchemas = IntegrationTestSchemas(BOC_NAME.toDatabaseSchemaName(), DUMMY_NOTARY_NAME.toDatabaseSchemaName(),
BIGCORP_NAME.organisation)
}
@Test
fun `issuer flow via RPC`() {
val commonPermissions = setOf(
invokeRpc("vaultTrackByCriteria"),
invokeRpc(CordaRPCOps::wellKnownPartyFromX500Name),
invokeRpc(CordaRPCOps::notaryIdentities)
)
driver(DriverParameters(extraCordappPackagesToScan = listOf("net.corda.finance"))) {
val bocManager = User("bocManager", "password1", permissions = setOf(
startFlow<CashIssueAndPaymentFlow>()) + commonPermissions)
val bigCorpCFO = User("bigCorpCFO", "password2", permissions = emptySet<String>() + commonPermissions)
val (nodeBankOfCorda, nodeBigCorporation) = listOf(
startNode(providedName = BOC_NAME, rpcUsers = listOf(bocManager)),
startNode(providedName = BIGCORP_NAME, rpcUsers = listOf(bigCorpCFO))
).map { it.getOrThrow() }
// Bank of Corda RPC Client
val bocClient = CordaRPCClient(nodeBankOfCorda.rpcAddress)
val bocProxy = bocClient.start("bocManager", "password1").proxy
// Big Corporation RPC Client
val bigCorpClient = CordaRPCClient(nodeBigCorporation.rpcAddress)
val bigCorpProxy = bigCorpClient.start("bigCorpCFO", "password2").proxy
// Register for Bank of Corda Vault updates
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
val vaultUpdatesBoc = bocProxy.vaultTrackByCriteria(Cash.State::class.java, criteria).updates
// Register for Big Corporation Vault updates
val vaultUpdatesBigCorp = bigCorpProxy.vaultTrackByCriteria(Cash.State::class.java, criteria).updates
val bigCorporation = bigCorpProxy.wellKnownPartyFromX500Name(BIGCORP_NAME)!!
// Kick-off actual Issuer Flow
val anonymous = true
bocProxy.startFlow(::CashIssueAndPaymentFlow,
1000.DOLLARS, OpaqueBytes.of(1),
bigCorporation,
anonymous,
defaultNotaryIdentity).returnValue.getOrThrow()
// Check Bank of Corda Vault Updates
vaultUpdatesBoc.expectEvents {
sequence(
// ISSUE
expect { update ->
require(update.consumed.isEmpty()) { "Expected 0 consumed states, actual: $update" }
require(update.produced.size == 1) { "Expected 1 produced states, actual: $update" }
},
// MOVE
expect { update ->
require(update.consumed.size == 1) { "Expected 1 consumed states, actual: $update" }
require(update.produced.isEmpty()) { "Expected 0 produced states, actual: $update" }
}
)
}
// Check Big Corporation Vault Updates
vaultUpdatesBigCorp.expectEvents {
sequence(
// MOVE
expect { (consumed, produced) ->
require(consumed.isEmpty()) { consumed.size }
require(produced.size == 1) { produced.size }
}
)
}
}
}
}

View File

@ -31,10 +31,17 @@ import kotlin.system.exitProcess
val BIGCORP_NAME = CordaX500Name(organisation = "BigCorporation", locality = "New York", country = "US") val BIGCORP_NAME = CordaX500Name(organisation = "BigCorporation", locality = "New York", country = "US")
private val NOTARY_NAME = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH") private val NOTARY_NAME = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH")
private const val BOC_RPC_PORT = 10006 const val BOC_RPC_PORT = 10006
const val BIGCORP_RPC_PORT = 10009
private const val BOC_RPC_ADMIN_PORT = 10015 private const val BOC_RPC_ADMIN_PORT = 10015
private const val BOC_WEB_PORT = 10007 private const val BOC_WEB_PORT = 10007
const val BOC_RPC_USER = "bankUser"
const val BOC_RPC_PWD = "test"
const val BIGCORP_RPC_USER = "bigCorpUser"
const val BIGCORP_RPC_PWD = "test"
class BankOfCordaCordform : CordformDefinition() { class BankOfCordaCordform : CordformDefinition() {
init { init {
@ -57,18 +64,18 @@ class BankOfCordaCordform : CordformDefinition() {
adminAddress("localhost:$BOC_RPC_ADMIN_PORT") adminAddress("localhost:$BOC_RPC_ADMIN_PORT")
} }
webPort(BOC_WEB_PORT) webPort(BOC_WEB_PORT)
rpcUsers(User("bankUser", "test", setOf(all()))) rpcUsers(User(BOC_RPC_USER, BOC_RPC_PWD, setOf(all())))
devMode(true) devMode(true)
} }
node { node {
name(BIGCORP_NAME) name(BIGCORP_NAME)
p2pPort(10008) p2pPort(10008)
rpcSettings { rpcSettings {
address("localhost:10009") address("localhost:$BIGCORP_RPC_PORT")
adminAddress("localhost:10011") adminAddress("localhost:10011")
} }
webPort(10010) webPort(10010)
rpcUsers(User("bigCorpUser", "test", setOf(all()))) rpcUsers(User(BIGCORP_RPC_USER, BIGCORP_RPC_PWD, setOf(all())))
devMode(true) devMode(true)
} }
} }
@ -123,8 +130,7 @@ object IssueCash {
return BankOfCordaClientApi.requestRPCIssue(NetworkHostAndPort("localhost", BOC_RPC_PORT), createParams(amount, NOTARY_NAME)) return BankOfCordaClientApi.requestRPCIssue(NetworkHostAndPort("localhost", BOC_RPC_PORT), createParams(amount, NOTARY_NAME))
} }
@VisibleForTesting private fun requestWebIssue(amount: Amount<Currency>) {
fun requestWebIssue(amount: Amount<Currency>) {
BankOfCordaClientApi.requestWebIssue(NetworkHostAndPort("localhost", BOC_WEB_PORT), createParams(amount, NOTARY_NAME)) BankOfCordaClientApi.requestWebIssue(NetworkHostAndPort("localhost", BOC_WEB_PORT), createParams(amount, NOTARY_NAME))
} }

View File

@ -10,6 +10,8 @@
package net.corda.bank.api package net.corda.bank.api
import net.corda.bank.BOC_RPC_PWD
import net.corda.bank.BOC_RPC_USER
import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams
import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCClient
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
@ -41,7 +43,7 @@ object BankOfCordaClientApi {
fun requestRPCIssue(rpcAddress: NetworkHostAndPort, params: IssueRequestParams): SignedTransaction { fun requestRPCIssue(rpcAddress: NetworkHostAndPort, params: IssueRequestParams): SignedTransaction {
val client = CordaRPCClient(rpcAddress) val client = CordaRPCClient(rpcAddress)
// TODO: privileged security controls required // TODO: privileged security controls required
client.start("bankUser", "test").use { connection -> client.start(BOC_RPC_USER, BOC_RPC_PWD).use { connection ->
val rpc = connection.proxy val rpc = connection.proxy
rpc.waitUntilNetworkReady().getOrThrow() rpc.waitUntilNetworkReady().getOrThrow()

View File

@ -27,7 +27,7 @@ fun CordformDefinition.nodeRunner() = CordformNodeRunner(this)
/** /**
* A node runner creates and runs nodes for a given [[CordformDefinition]]. * A node runner creates and runs nodes for a given [[CordformDefinition]].
*/ */
class CordformNodeRunner(val cordformDefinition: CordformDefinition) { class CordformNodeRunner(private val cordformDefinition: CordformDefinition) {
private var extraPackagesToScan = emptyList<String>() private var extraPackagesToScan = emptyList<String>()
/** /**
@ -55,7 +55,7 @@ class CordformNodeRunner(val cordformDefinition: CordformDefinition) {
* Deploy the nodes specified in the given [CordformDefinition] and then execute the given [block] once all the nodes * Deploy the nodes specified in the given [CordformDefinition] and then execute the given [block] once all the nodes
* and webservers are up. After execution all these processes will be terminated. * and webservers are up. After execution all these processes will be terminated.
*/ */
fun deployAndRunNodesThen(block: () -> Unit) { fun deployAndRunNodesAndThen(block: () -> Unit) {
runNodes(waitForAllNodesToFinish = false, block = block) runNodes(waitForAllNodesToFinish = false, block = block)
} }