mirror of
https://github.com/corda/corda.git
synced 2025-06-01 07:00:54 +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. */
|
/** 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
|
||||||
|
@ -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() }
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
@ -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:
|
||||||
|
@ -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? {
|
||||||
|
@ -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
|
@ -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)
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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")
|
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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user