mirror of
https://github.com/corda/corda.git
synced 2025-03-16 00:55:24 +00:00
commit
cb4e428ec4
0
CONTRIBUTORS.md
Normal file
0
CONTRIBUTORS.md
Normal 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. */
|
||||
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. */
|
||||
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
|
||||
|
@ -32,6 +32,7 @@ import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.temporal.Temporal
|
||||
import java.util.*
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KType
|
||||
@ -138,6 +139,7 @@ private fun Config.getSingleValue(path: String, type: KType, onUnknownKeys: (Set
|
||||
Path::class -> Paths.get(getString(path))
|
||||
URL::class -> URL(getString(path))
|
||||
UUID::class -> UUID.fromString(getString(path))
|
||||
X500Principal::class -> X500Principal(getString(path))
|
||||
CordaX500Name::class -> {
|
||||
when (getValue(path).valueType()) {
|
||||
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)
|
||||
Path::class -> getStringList(path).map { Paths.get(it) }
|
||||
URL::class -> getStringList(path).map(::URL)
|
||||
X500Principal::class -> getStringList(path).map(::X500Principal)
|
||||
UUID::class -> getStringList(path).map { UUID.fromString(it) }
|
||||
CordaX500Name::class -> getStringList(path).map(CordaX500Name.Companion::parse)
|
||||
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) {
|
||||
// These types are supported by Config as use as is
|
||||
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
|
||||
value.toString()
|
||||
} else if (value is Enum<*>) {
|
||||
@ -271,6 +274,7 @@ private fun Iterable<*>.toConfigIterable(field: Field): Iterable<Any?> {
|
||||
NetworkHostAndPort::class.java -> map(Any?::toString)
|
||||
Path::class.java -> map(Any?::toString)
|
||||
URL::class.java -> map(Any?::toString)
|
||||
X500Principal::class.java -> map(Any?::toString)
|
||||
UUID::class.java -> map(Any?::toString)
|
||||
CordaX500Name::class.java -> map(Any?::toString)
|
||||
Properties::class.java -> map { ConfigFactory.parseMap(uncheckedCast(it)).root() }
|
||||
|
@ -40,7 +40,7 @@ fun loadOrCreateKeyStore(keyStoreFilePath: Path, storePassword: String): KeyStor
|
||||
keyStoreFilePath.read { keyStore.load(it, pass) }
|
||||
} else {
|
||||
keyStore.load(null, pass)
|
||||
keyStoreFilePath.parent.createDirectories()
|
||||
keyStoreFilePath.toAbsolutePath().parent?.createDirectories()
|
||||
keyStoreFilePath.write { keyStore.store(it, pass) }
|
||||
}
|
||||
return keyStore
|
||||
|
@ -11,6 +11,7 @@
|
||||
package net.corda.nodeapi.internal.protonwrapper.netty
|
||||
|
||||
import io.netty.handler.ssl.SslHandler
|
||||
import net.corda.core.crypto.newSecureRandom
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.toHex
|
||||
@ -117,7 +118,7 @@ internal fun createClientSslHelper(target: NetworkHostAndPort,
|
||||
val sslContext = SSLContext.getInstance("TLS")
|
||||
val keyManagers = keyManagerFactory.keyManagers
|
||||
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)
|
||||
sslEngine.useClientMode = true
|
||||
sslEngine.enabledProtocols = ArtemisTcpTransport.TLS_VERSIONS.toTypedArray()
|
||||
@ -131,7 +132,7 @@ internal fun createServerSslHelper(keyManagerFactory: KeyManagerFactory,
|
||||
val sslContext = SSLContext.getInstance("TLS")
|
||||
val keyManagers = keyManagerFactory.keyManagers
|
||||
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()
|
||||
sslEngine.useClientMode = false
|
||||
sslEngine.needClientAuth = true
|
||||
|
@ -24,6 +24,7 @@ import java.nio.file.Path
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.util.*
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
|
||||
class ConfigParsingTest {
|
||||
@ -94,6 +95,11 @@ class ConfigParsingTest {
|
||||
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
|
||||
fun UUID() {
|
||||
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 URLData(override val value: URL) : SingleData<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 UUIDListData(override val values: List<UUID>) : ListData<UUID>
|
||||
data class CordaX500NameData(override val value: CordaX500Name) : SingleData<CordaX500Name>
|
||||
|
@ -13,6 +13,7 @@ package net.corda.nodeapi.internal.crypto
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
|
||||
import net.corda.core.crypto.Crypto.generateKeyPair
|
||||
import net.corda.core.crypto.newSecureRandom
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
@ -248,7 +249,7 @@ class X509UtilitiesTest {
|
||||
val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||
trustMgrFactory.init(trustStore)
|
||||
val trustManagers = trustMgrFactory.trustManagers
|
||||
context.init(keyManagers, trustManagers, SecureRandom())
|
||||
context.init(keyManagers, trustManagers, newSecureRandom())
|
||||
|
||||
val serverSocketFactory = context.serverSocketFactory
|
||||
val clientSocketFactory = context.socketFactory
|
||||
|
@ -14,6 +14,7 @@ import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import io.netty.channel.EventLoopGroup
|
||||
import io.netty.channel.nio.NioEventLoopGroup
|
||||
import net.corda.core.crypto.newSecureRandom
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.toFuture
|
||||
@ -157,7 +158,7 @@ class ProtonWrapperTests {
|
||||
val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||
trustMgrFactory.init(trustStore)
|
||||
val trustManagers = trustMgrFactory.trustManagers
|
||||
context.init(keyManagers, trustManagers, SecureRandom())
|
||||
context.init(keyManagers, trustManagers, newSecureRandom())
|
||||
|
||||
val serverSocketFactory = context.serverSocketFactory
|
||||
|
||||
|
@ -148,12 +148,7 @@ import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||
import net.corda.nodeapi.internal.NodeInfoAndSigned
|
||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
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.persistence.*
|
||||
import net.corda.nodeapi.internal.storeLegalIdentity
|
||||
import net.corda.tools.shell.InteractiveShell
|
||||
import org.apache.activemq.artemis.utils.ReusableLatch
|
||||
@ -1187,7 +1182,7 @@ fun configureDatabase(hikariProperties: Properties,
|
||||
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.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -203,7 +203,7 @@ open class Node(configuration: NodeConfiguration,
|
||||
|
||||
if (!configuration.messagingServerExternal) {
|
||||
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
|
||||
|
@ -33,6 +33,7 @@ import java.net.URL
|
||||
import java.nio.file.Path
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
val Int.MB: Long get() = this * 1024L * 1024L
|
||||
|
||||
@ -78,7 +79,7 @@ interface NodeConfiguration : NodeSSLConfiguration {
|
||||
val drainingModePollPeriod: Duration get() = Duration.ofSeconds(5)
|
||||
val extraNetworkMapKeys: List<UUID>
|
||||
val tlsCertCrlDistPoint: URL?
|
||||
val tlsCertCrlIssuer: String?
|
||||
val tlsCertCrlIssuer: X500Principal?
|
||||
val effectiveH2Settings: NodeH2Settings?
|
||||
val flowMonitorPeriodMillis: Duration get() = DEFAULT_FLOW_MONITOR_PERIOD_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 var networkServices: NetworkServicesConfig? = null,
|
||||
override val tlsCertCrlDistPoint: URL? = null,
|
||||
override val tlsCertCrlIssuer: String? = null,
|
||||
override val tlsCertCrlIssuer: X500Principal? = null,
|
||||
override val rpcUsers: List<User>,
|
||||
override val security: SecurityConfiguration? = null,
|
||||
override val verifierType: VerifierType,
|
||||
@ -296,11 +297,6 @@ data class NodeConfigurationImpl(
|
||||
if (tlsCertCrlDistPoint == 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) {
|
||||
errors += "tlsCertCrlDistPoint needs to be specified when crlCheckSoftFail is FALSE"
|
||||
|
@ -25,18 +25,14 @@ import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.nodeapi.ArtemisTcpTransport.Companion.p2pAcceptorTcpTransport
|
||||
import net.corda.nodeapi.internal.AmqpMessageSizeChecksInterceptor
|
||||
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.JOURNAL_HEADER_SIZE
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATIONS_ADDRESS
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_PREFIX
|
||||
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.management.ActiveMQServerControl
|
||||
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.SecurityConfiguration
|
||||
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 java.io.IOException
|
||||
import java.security.KeyStoreException
|
||||
import java.security.PublicKey
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
import javax.security.auth.login.AppConfigurationEntry
|
||||
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag.REQUIRED
|
||||
@ -66,8 +61,7 @@ import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag.RE
|
||||
@ThreadSafe
|
||||
class ArtemisMessagingServer(private val config: NodeConfiguration,
|
||||
private val messagingServerAddress: NetworkHostAndPort,
|
||||
private val maxMessageSize: Int,
|
||||
private val identities: List<PublicKey> = emptyList()) : ArtemisBroker, SingletonSerializeAsToken() {
|
||||
private val maxMessageSize: Int) : ArtemisBroker, SingletonSerializeAsToken() {
|
||||
companion object {
|
||||
private val log = contextLogger()
|
||||
}
|
||||
@ -131,48 +125,30 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
|
||||
log.info("P2P messaging server listening on $messagingServerAddress")
|
||||
}
|
||||
|
||||
private fun createArtemisConfig(): Configuration {
|
||||
val addressConfigs = identities.map {
|
||||
val queueName = ArtemisMessagingComponent.RemoteInboxAddress(it).queueName
|
||||
log.info("Configuring address $queueName")
|
||||
val queueConfig = CoreQueueConfiguration().apply {
|
||||
address = queueName
|
||||
name = queueName
|
||||
routingType = RoutingType.ANYCAST
|
||||
isExclusive = true
|
||||
}
|
||||
CoreAddressConfiguration().apply {
|
||||
name = queueName
|
||||
queueConfigurations = listOf(queueConfig)
|
||||
addRoutingType(RoutingType.ANYCAST)
|
||||
}
|
||||
private fun createArtemisConfig() = 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
|
||||
|
||||
// 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
|
||||
if (config.jmxMonitoringHttpPort != null) {
|
||||
isJMXManagementEnabled = true
|
||||
isJMXUseBrokerName = true
|
||||
}
|
||||
|
||||
}.configureAddressSecurity()
|
||||
}
|
||||
}.configureAddressSecurity()
|
||||
|
||||
/**
|
||||
* Authenticated clients connecting to us fall in one of the following groups:
|
||||
|
@ -293,20 +293,20 @@ class NodeRegistrationHelper(private val config: NodeConfiguration, certService:
|
||||
}
|
||||
|
||||
override fun validateAndGetTlsCrlIssuerCert(): X509Certificate? {
|
||||
config.tlsCertCrlIssuer ?: return null
|
||||
val tlsCertCrlIssuerPrincipal = X500Principal(config.tlsCertCrlIssuer)
|
||||
if (principalMatchesCertificatePrincipal(tlsCertCrlIssuerPrincipal, rootCert)) {
|
||||
val tlsCertCrlIssuer = config.tlsCertCrlIssuer
|
||||
tlsCertCrlIssuer ?: return null
|
||||
if (principalMatchesCertificatePrincipal(tlsCertCrlIssuer, rootCert)) {
|
||||
return rootCert
|
||||
}
|
||||
return if (config.trustStoreFile.exists()) {
|
||||
findMatchingCertificate(tlsCertCrlIssuerPrincipal, config.loadTrustStore())
|
||||
findMatchingCertificate(tlsCertCrlIssuer, config.loadTrustStore())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun isTlsCrlIssuerCertRequired(): Boolean {
|
||||
return !config.tlsCertCrlIssuer.isNullOrEmpty()
|
||||
return config.tlsCertCrlIssuer != null
|
||||
}
|
||||
|
||||
private fun findMatchingCertificate(principal: X500Principal, trustStore: X509KeyStore): X509Certificate? {
|
||||
|
@ -8,7 +8,7 @@
|
||||
* 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.ConfigException
|
||||
@ -16,7 +16,6 @@ import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigRenderOptions
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.writeText
|
||||
import net.corda.node.internal.cordapp.CordappConfigFileProvider
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
import java.nio.file.Paths
|
@ -57,7 +57,6 @@ import org.junit.rules.RuleChain
|
||||
import org.slf4j.MDC
|
||||
import java.security.PublicKey
|
||||
import java.util.concurrent.Future
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.test.assertNotEquals
|
||||
|
||||
@ -221,7 +220,7 @@ class TimedFlowTests {
|
||||
val stx = requestPayload.signedTransaction
|
||||
subFlow(ResolveTransactionsFlow(stx, otherSideSession))
|
||||
|
||||
if (TimedFlowTests.requestsReceived.getAndIncrement() == 0) {
|
||||
if (requestsReceived.getAndIncrement() == 0) {
|
||||
logger.info("Ignoring")
|
||||
// Waiting forever
|
||||
stateMachine.suspend(FlowIORequest.WaitForLedgerCommit(SecureHash.randomSHA256()), false)
|
@ -28,6 +28,7 @@ import java.net.InetAddress
|
||||
import java.net.URL
|
||||
import java.net.URI
|
||||
import java.nio.file.Paths
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import java.util.*
|
||||
import kotlin.test.*
|
||||
|
||||
@ -48,13 +49,6 @@ class NodeConfigurationImplTest {
|
||||
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
|
||||
fun `can't have tlsCertCrlDistPoint null when crlCheckSoftFail is false`() {
|
||||
val configValidationResult = configTlsCertCrlOptions(null, null, false).validate()
|
||||
@ -265,7 +259,7 @@ class NodeConfigurationImplTest {
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -10,11 +10,17 @@
|
||||
|
||||
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.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.IntegrationTestSchemas
|
||||
import net.corda.testing.node.internal.demorun.nodeRunner
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
|
||||
@ -27,9 +33,19 @@ class BankOfCordaCordformTest : IntegrationTest() {
|
||||
|
||||
@Test
|
||||
fun `run demo`() {
|
||||
BankOfCordaCordform().nodeRunner().scanPackages(listOf("net.corda.finance")).deployAndRunNodesThen {
|
||||
IssueCash.requestWebIssue(30000.POUNDS)
|
||||
BankOfCordaCordform().nodeRunner().scanPackages(listOf("net.corda.finance")).deployAndRunNodesAndThen {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -31,10 +31,17 @@ import kotlin.system.exitProcess
|
||||
|
||||
val BIGCORP_NAME = CordaX500Name(organisation = "BigCorporation", locality = "New York", country = "US")
|
||||
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_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() {
|
||||
|
||||
init {
|
||||
@ -57,18 +64,18 @@ class BankOfCordaCordform : CordformDefinition() {
|
||||
adminAddress("localhost:$BOC_RPC_ADMIN_PORT")
|
||||
}
|
||||
webPort(BOC_WEB_PORT)
|
||||
rpcUsers(User("bankUser", "test", setOf(all())))
|
||||
rpcUsers(User(BOC_RPC_USER, BOC_RPC_PWD, setOf(all())))
|
||||
devMode(true)
|
||||
}
|
||||
node {
|
||||
name(BIGCORP_NAME)
|
||||
p2pPort(10008)
|
||||
rpcSettings {
|
||||
address("localhost:10009")
|
||||
address("localhost:$BIGCORP_RPC_PORT")
|
||||
adminAddress("localhost:10011")
|
||||
}
|
||||
webPort(10010)
|
||||
rpcUsers(User("bigCorpUser", "test", setOf(all())))
|
||||
rpcUsers(User(BIGCORP_RPC_USER, BIGCORP_RPC_PWD, setOf(all())))
|
||||
devMode(true)
|
||||
}
|
||||
}
|
||||
@ -123,8 +130,7 @@ object IssueCash {
|
||||
return BankOfCordaClientApi.requestRPCIssue(NetworkHostAndPort("localhost", BOC_RPC_PORT), createParams(amount, NOTARY_NAME))
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
fun requestWebIssue(amount: Amount<Currency>) {
|
||||
private fun requestWebIssue(amount: Amount<Currency>) {
|
||||
BankOfCordaClientApi.requestWebIssue(NetworkHostAndPort("localhost", BOC_WEB_PORT), createParams(amount, NOTARY_NAME))
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
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.client.rpc.CordaRPCClient
|
||||
import net.corda.core.messaging.startFlow
|
||||
@ -41,7 +43,7 @@ object BankOfCordaClientApi {
|
||||
fun requestRPCIssue(rpcAddress: NetworkHostAndPort, params: IssueRequestParams): SignedTransaction {
|
||||
val client = CordaRPCClient(rpcAddress)
|
||||
// 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
|
||||
rpc.waitUntilNetworkReady().getOrThrow()
|
||||
|
||||
|
@ -27,7 +27,7 @@ fun CordformDefinition.nodeRunner() = CordformNodeRunner(this)
|
||||
/**
|
||||
* 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>()
|
||||
|
||||
/**
|
||||
@ -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
|
||||
* 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)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user