OS-ENT merge

This commit is contained in:
bpaunescu 2018-07-16 10:00:16 +01:00
commit 33ce848e0c
28 changed files with 248 additions and 214 deletions

0
CONTRIBUTORS.md Normal file
View File

View File

@ -0,0 +1,56 @@
Building Corda
==============
These instructions are for downloading and building the Corda code locally. If you only wish to develop CorDapps for
use on Corda, you don't need to do this, follow the instructions at :doc:`getting-set-up` and use the precompiled binaries.
Windows
-------
Java
~~~~
1. Visit http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
2. Scroll down to "Java SE Development Kit 8uXXX" (where "XXX" is the latest minor version number)
3. Toggle "Accept License Agreement"
4. Click the download link for jdk-8uXXX-windows-x64.exe (where "XXX" is the latest minor version number)
5. Download and run the executable to install Java (use the default settings)
6. Add Java to the PATH environment variable by following the instructions at https://docs.oracle.com/javase/7/docs/webnotes/install/windows/jdk-installation-windows.html#path
7. Open a new command prompt and run ``java -version`` to test that Java is installed correctly
Git
~~~
1. Visit https://git-scm.com/download/win
2. Click the "64-bit Git for Windows Setup" download link.
3. Download and run the executable to install Git (use the default installation values) and make a note of the installation directory.
4. Open a new command prompt and type ``git --version`` to test that Git is installed correctly
Buillding Corda
~~~~~~~~~~~~~~~
1. Open a command prompt
2. Run ``git clone https://github.com/corda/corda.git``
3. Run ``gradlew build``
Debian/Ubuntu Linux
-------------------
These instructions were tested on Ubuntu Server 18.04 LTS. This distribution includes ``git`` and ``python`` so only the following steps are required:
Java
~~~~
1. Run ``sudo add-apt-repository ppa:webupd8team/java`` from the terminal. Press ENTER when prompted.
2. Run ``sudo apt-get update``
3. Then run ``sudo apt-get install oracle-java8-installer``. Press Y when prompted and agree to the licence terms.
4. Run ``java --version`` to verify that java is installed correctly
Building Corda
~~~~~~~~~~~~~~
1. Open the terminal
2. Run ``git clone https://github.com/corda/corda.git``
3. Run ``./gradlew build``

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. */
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

View File

@ -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() }

View File

@ -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

View File

@ -94,6 +94,8 @@ class CordaPersistence(
// Check not in read-only mode.
transaction {
check(!connection.metaData.isReadOnly) { "Database should not be readonly." }
checkCorrectAttachmentsContractsTableName(connection)
}
}
@ -298,4 +300,19 @@ private fun Throwable.hasSQLExceptionCause(): Boolean =
else -> cause?.hasSQLExceptionCause() ?: false
}
class CouldNotCreateDataSourceException(override val message: String?, override val cause: Throwable? = null) : Exception()
class CouldNotCreateDataSourceException(override val message: String?, override val cause: Throwable? = null) : Exception()
class IncompatibleAttachmentsContractsTableName(override val message: String?, override val cause: Throwable? = null) : Exception()
private fun checkCorrectAttachmentsContractsTableName(connection: Connection) {
val correctName = "NODE_ATTACHMENTS_CONTRACTS"
val incorrectV30Name = "NODE_ATTACHMENTS_CONTRACT_CLASS_NAME"
val incorrectV31Name = "NODE_ATTCHMENTS_CONTRACTS"
fun warning(incorrectName: String, version: String) = "The database contains the older table name $incorrectName instead of $correctName, see upgrade notes to migrate from Corda database version $version https://docs.corda.net/head/upgrade-notes.html."
if (!connection.metaData.getTables(null, null, correctName, null).next()) {
if (connection.metaData.getTables(null, null, incorrectV30Name, null).next()) { throw IncompatibleAttachmentsContractsTableName(warning(incorrectV30Name, "3.0")) }
if (connection.metaData.getTables(null, null, incorrectV31Name, null).next()) { throw IncompatibleAttachmentsContractsTableName(warning(incorrectV31Name, "3.1")) }
}
}

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,66 @@
package net.corda.node.persistence
import net.corda.client.rpc.CordaRPCClient
import net.corda.core.internal.packageName
import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow
import net.corda.node.services.Permissions
import net.corda.testMessage.Message
import net.corda.testMessage.MessageState
import net.corda.testing.core.singleIdentity
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver
import net.corda.testing.driver.internal.RandomFree
import net.corda.testing.node.User
import org.junit.Test
import java.nio.file.Path
import java.sql.DriverManager
import kotlin.test.*
class FailNodeOnNotMigratedAttachmentContractsTableNameTests {
@Test
fun `node fails when detecting table name not migrated from version 3 dot 0`() {
`node fails when not detecting compatible table name`("NODE_ATTACHMENTS_CONTRACTS", "NODE_ATTACHMENTS_CONTRACT_CLASS_NAME")
}
@Test
fun `node fails when detecting table name not migrated from version 3 dot 1`() {
`node fails when not detecting compatible table name`("NODE_ATTACHMENTS_CONTRACTS", "NODE_ATTCHMENTS_CONTRACTS")
}
fun `node fails when not detecting compatible table name`(tableNameFromMapping: String, tableNameInDB: String) {
val user = User("mark", "dadada", setOf(Permissions.startFlow<SendMessageFlow>(), Permissions.invokeRpc("vaultQuery")))
val message = Message("Hello world!")
val baseDir: Path = driver(DriverParameters(inMemoryDB = false, startNodesInProcess = isQuasarAgentSpecified(),
portAllocation = RandomFree, extraCordappPackagesToScan = listOf(MessageState::class.packageName))) {
val (nodeName, baseDir) = {
val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
val nodeName = nodeHandle.nodeInfo.singleIdentity().name
CordaRPCClient(nodeHandle.rpcAddress).start(user.username, user.password).use {
it.proxy.startFlow(::SendMessageFlow, message, defaultNotaryIdentity).returnValue.getOrThrow()
}
nodeHandle.stop()
Pair(nodeName, nodeHandle.baseDirectory)
}()
// replace the correct table name with one from the former release
DriverManager.getConnection("jdbc:h2:file://$baseDir/persistence", "sa", "").use {
it.createStatement().execute("ALTER TABLE $tableNameFromMapping RENAME TO $tableNameInDB")
it.commit()
}
assertFailsWith(net.corda.nodeapi.internal.persistence.IncompatibleAttachmentsContractsTableName::class) {
val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user)).getOrThrow()
nodeHandle.stop()
}
baseDir
}
// check that the node didn't recreated the correct table matching it's entity mapping
val (hasTableFromMapping, hasTableFromDB) = DriverManager.getConnection("jdbc:h2:file://$baseDir/persistence", "sa", "").use {
Pair(it.metaData.getTables(null, null, tableNameFromMapping, null).next(),
it.metaData.getTables(null, null, tableNameInDB, null).next())
}
assertFalse(hasTableFromMapping)
assertTrue(hasTableFromDB)
}
}

View File

@ -150,12 +150,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
@ -1179,7 +1174,8 @@ 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
ex is IncompatibleAttachmentsContractsTableName -> 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) {
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

View File

@ -45,6 +45,7 @@ import net.corda.nodeapi.internal.config.UnknownConfigurationKeysException
import net.corda.nodeapi.internal.persistence.DatabaseMigrationException
import net.corda.nodeapi.internal.persistence.oracleJdbcDriverSerialFilter
import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException
import net.corda.nodeapi.internal.persistence.IncompatibleAttachmentsContractsTableName
import net.corda.tools.shell.InteractiveShell
import org.fusesource.jansi.Ansi
import org.fusesource.jansi.AnsiConsole
@ -178,6 +179,10 @@ open class NodeStartup(val args: Array<String>) {
} catch (e: NetworkParametersReader.Error) {
logger.error(e.message)
return false
} catch (e: IncompatibleAttachmentsContractsTableName) {
e.message?.let { Node.printWarning(it) }
logger.error(e.message)
return false
} catch (e: Exception) {
if (e is Errors.NativeIoException && e.message?.contains("Address already in use") == true) {
logger.error("One of the ports required by the Corda node is already in use.")

View File

@ -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"

View File

@ -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,29 @@ 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)
// 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:

View File

@ -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? {

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.
*/
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

View File

@ -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)

View File

@ -28,8 +28,9 @@ import java.net.InetAddress
import java.net.URL
import java.net.URI
import java.nio.file.Paths
import java.util.*
import kotlin.test.*
import javax.security.auth.x500.X500Principal
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class NodeConfigurationImplTest {
@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 {

View File

@ -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)
}
}
}
}

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")
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))
}

View File

@ -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()

View File

@ -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)
}