Merge pull request #1357 from corda/christians/merge-ENT-2414

OS -> Ent merge up to  bc330bd
This commit is contained in:
Christian Sailer 2018-08-29 09:52:59 +01:00 committed by GitHub
commit 260e50572a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 716 additions and 1106 deletions

View File

@ -16,6 +16,7 @@ import javafx.collections.FXCollections
import javafx.collections.ObservableList
import net.corda.client.jfx.utils.*
import net.corda.core.identity.AnonymousParty
import net.corda.core.internal.buildNamed
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache.MapChange
import java.security.PublicKey
@ -40,7 +41,7 @@ class NetworkIdentityModel {
private val rpcProxy by observableValue(NodeMonitorModel::proxyObservable)
private val identityCache = Caffeine.newBuilder()
.build<PublicKey, ObservableValue<NodeInfo?>>({ publicKey ->
.buildNamed<PublicKey, ObservableValue<NodeInfo?>>("NetworkIdentityModel_identity", { publicKey ->
publicKey.let { rpcProxy.map { it?.cordaRPCOps?.nodeInfoFromParty(AnonymousParty(publicKey)) } }
})
val notaries = ChosenList(rpcProxy.map { FXCollections.observableList(it?.cordaRPCOps?.notaryIdentities() ?: emptyList()) }, "notaries")

View File

@ -24,10 +24,7 @@ import net.corda.client.rpc.internal.serialization.amqp.RpcClientObservableDeSer
import net.corda.core.context.Actor
import net.corda.core.context.Trace
import net.corda.core.context.Trace.InvocationId
import net.corda.core.internal.LazyStickyPool
import net.corda.core.internal.LifeCycle
import net.corda.core.internal.ThreadBox
import net.corda.core.internal.times
import net.corda.core.internal.*
import net.corda.core.messaging.RPCOps
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.serialize
@ -41,26 +38,15 @@ import org.apache.activemq.artemis.api.core.ActiveMQException
import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException
import org.apache.activemq.artemis.api.core.RoutingType
import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.client.*
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE
import org.apache.activemq.artemis.api.core.client.ClientConsumer
import org.apache.activemq.artemis.api.core.client.ClientMessage
import org.apache.activemq.artemis.api.core.client.ClientProducer
import org.apache.activemq.artemis.api.core.client.ClientSession
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory
import org.apache.activemq.artemis.api.core.client.FailoverEventType
import org.apache.activemq.artemis.api.core.client.ServerLocator
import rx.Notification
import rx.Observable
import rx.subjects.UnicastSubject
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicLong
import kotlin.reflect.jvm.javaMethod
@ -183,9 +169,7 @@ class RPCClientProxyHandler(
observablesToReap.locked { observables.add(observableId) }
}
return Caffeine.newBuilder().
weakValues().
removalListener(onObservableRemove).executor(SameThreadExecutor.getExecutor()).
build()
weakValues().removalListener(onObservableRemove).executor(SameThreadExecutor.getExecutor()).buildNamed("RpcClientProxyHandler_rpcObservable")
}
private var sessionFactory: ClientSessionFactory? = null
@ -298,56 +282,71 @@ class RPCClientProxyHandler(
// The handler for Artemis messages.
private fun artemisMessageHandler(message: ClientMessage) {
val serverToClient = RPCApi.ServerToClient.fromClientMessage(serializationContextWithObservableContext, message)
val deduplicationSequenceNumber = message.getLongProperty(RPCApi.DEDUPLICATION_SEQUENCE_NUMBER_FIELD_NAME)
if (deduplicationChecker.checkDuplicateMessageId(serverToClient.deduplicationIdentity, deduplicationSequenceNumber)) {
log.info("Message duplication detected, discarding message")
return
fun completeExceptionally(id: InvocationId, e: Throwable, future: SettableFuture<Any?>?) {
val rpcCallSite: Throwable? = callSiteMap?.get(id)
if (rpcCallSite != null) addRpcCallSiteToThrowable(e, rpcCallSite)
future?.setException(e.cause ?: e)
}
log.debug { "Got message from RPC server $serverToClient" }
when (serverToClient) {
is RPCApi.ServerToClient.RpcReply -> {
val replyFuture = rpcReplyMap.remove(serverToClient.id)
if (replyFuture == null) {
log.error("RPC reply arrived to unknown RPC ID ${serverToClient.id}, this indicates an internal RPC error.")
} else {
val result = serverToClient.result
when (result) {
is Try.Success -> replyFuture.set(result.value)
is Try.Failure -> {
val rpcCallSite = callSiteMap?.get(serverToClient.id)
if (rpcCallSite != null) addRpcCallSiteToThrowable(result.exception, rpcCallSite)
replyFuture.setException(result.exception)
}
}
}
}
is RPCApi.ServerToClient.Observation -> {
val observable = observableContext.observableMap.getIfPresent(serverToClient.id)
if (observable == null) {
log.debug("Observation ${serverToClient.content} arrived to unknown Observable with ID ${serverToClient.id}. " +
"This may be due to an observation arriving before the server was " +
"notified of observable shutdown")
} else {
// We schedule the onNext() on an executor sticky-pooled based on the Observable ID.
observationExecutorPool.run(serverToClient.id) { executor ->
executor.submit {
val content = serverToClient.content
if (content.isOnCompleted || content.isOnError) {
observableContext.observableMap.invalidate(serverToClient.id)
}
// Add call site information on error
if (content.isOnError) {
val rpcCallSite = callSiteMap?.get(serverToClient.id)
if (rpcCallSite != null) addRpcCallSiteToThrowable(content.throwable, rpcCallSite)
}
observable.onNext(content)
try {
// Deserialize the reply from the server, both the wrapping metadata and the actual body of the return value.
val serverToClient: RPCApi.ServerToClient = try {
RPCApi.ServerToClient.fromClientMessage(serializationContextWithObservableContext, message)
} catch (e: RPCApi.ServerToClient.FailedToDeserializeReply) {
// Might happen if something goes wrong during mapping the response to classes, evolution, class synthesis etc.
log.error("Failed to deserialize RPC body", e)
completeExceptionally(e.id, e, rpcReplyMap.remove(e.id))
return
}
val deduplicationSequenceNumber = message.getLongProperty(RPCApi.DEDUPLICATION_SEQUENCE_NUMBER_FIELD_NAME)
if (deduplicationChecker.checkDuplicateMessageId(serverToClient.deduplicationIdentity, deduplicationSequenceNumber)) {
log.info("Message duplication detected, discarding message")
return
}
log.debug { "Got message from RPC server $serverToClient" }
when (serverToClient) {
is RPCApi.ServerToClient.RpcReply -> {
val replyFuture = rpcReplyMap.remove(serverToClient.id)
if (replyFuture == null) {
log.error("RPC reply arrived to unknown RPC ID ${serverToClient.id}, this indicates an internal RPC error.")
} else {
val result: Try<Any?> = serverToClient.result
when (result) {
is Try.Success -> replyFuture.set(result.value)
is Try.Failure -> {
completeExceptionally(serverToClient.id, result.exception, replyFuture)
}
}
}
}
is RPCApi.ServerToClient.Observation -> {
val observable: UnicastSubject<Notification<*>>? = observableContext.observableMap.getIfPresent(serverToClient.id)
if (observable == null) {
log.debug("Observation ${serverToClient.content} arrived to unknown Observable with ID ${serverToClient.id}. " +
"This may be due to an observation arriving before the server was " +
"notified of observable shutdown")
} else {
// We schedule the onNext() on an executor sticky-pooled based on the Observable ID.
observationExecutorPool.run(serverToClient.id) { executor ->
executor.submit {
val content = serverToClient.content
if (content.isOnCompleted || content.isOnError) {
observableContext.observableMap.invalidate(serverToClient.id)
}
// Add call site information on error
if (content.isOnError) {
val rpcCallSite = callSiteMap?.get(serverToClient.id)
if (rpcCallSite != null) addRpcCallSiteToThrowable(content.throwable, rpcCallSite)
}
observable.onNext(content)
}
}
}
}
}
} finally {
message.acknowledge()
}
message.acknowledge()
}
/**

View File

@ -97,6 +97,9 @@ dependencies {
// Guava: Google utilities library.
testCompile "com.google.guava:guava:$guava_version"
// For caches rather than guava
compile "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
// Smoke tests do NOT have any Node code on the classpath!
smokeTestCompile project(':smoke-test-utils')
smokeTestCompile "org.assertj:assertj-core:${assertj_version}"

View File

@ -0,0 +1,41 @@
package net.corda.core.internal
import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.CacheLoader
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.LoadingCache
/**
* Restrict the allowed characters of a cache name - this ensures that each cache has a name, and that
* the name can be used to create a file name or a metric name.
*/
internal fun checkCacheName(name: String) {
require(!name.isBlank())
require(allowedChars.matches(name))
}
private val allowedChars = Regex("^[0-9A-Za-z_.]*\$")
/* buildNamed is the central helper method to build caffeine caches in Corda.
* This allows to easily add tweaks to all caches built in Corda, and also forces
* cache users to give their cache a (meaningful) name that can be used e.g. for
* capturing cache traces etc.
*
* Currently it is not used in this version of CORDA, but there are plans to do so.
*/
fun <K, V> Caffeine<in K, in V>.buildNamed(name: String): Cache<K, V> {
checkCacheName(name)
return this.build<K, V>()
}
fun <K, V> Caffeine<in K, in V>.buildNamed(name: String, loadFunc: (K) -> V): LoadingCache<K, V> {
checkCacheName(name)
return this.build<K, V>(loadFunc)
}
fun <K, V> Caffeine<in K, in V>.buildNamed(name: String, loader: CacheLoader<K, V>): LoadingCache<K, V> {
checkCacheName(name)
return this.build<K, V>(loader)
}

View File

@ -0,0 +1,24 @@
package net.corda.core.internal
import org.junit.Test
import kotlin.test.assertEquals
class NamedCacheTest {
fun checkNameHelper(name: String, throws: Boolean) {
var exceptionThrown = false
try {
checkCacheName(name)
} catch (e: Exception) {
exceptionThrown = true
}
assertEquals(throws, exceptionThrown)
}
@Test
fun TestCheckCacheName() {
checkNameHelper("abc_123.234", false)
checkNameHelper("", true)
checkNameHelper("abc 123", true)
checkNameHelper("abc/323", true)
}
}

View File

@ -1,20 +1,10 @@
buildscript {
// Shaded version of ASM to avoid conflict with root project.
ext.asm_version = '6.1.1'
ext.deterministic_rt_version = '1.0-20180625.120901-7'
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.3'
}
plugins {
id 'com.github.johnrengelman.shadow' version '2.0.4'
}
plugins {
id 'com.github.johnrengelman.shadow' version '2.0.3'
ext {
// Shaded version of ASM to avoid conflict with root project.
asm_version = '6.1.1'
}
dependencies {
@ -32,47 +22,16 @@ dependencies {
// Classpath scanner
compile "io.github.lukehutch:fast-classpath-scanner:$fast_classpath_scanner_version"
// Deterministic runtime - used in whitelist generation
runtime "net.corda:deterministic-rt:$deterministic_rt_version:api"
// Test utilities
testCompile "junit:junit:$junit_version"
testCompile "org.assertj:assertj-core:$assertj_version"
}
repositories {
mavenLocal()
mavenCentral()
maven {
url "$artifactory_contextUrl/corda-dev"
}
}
task generateWhitelist(type: JavaExec) {
// This is an example of how a whitelist can be generated from a JAR. In most applications though, it is recommended
// thet the minimal set whitelist is used.
def jars = configurations.runtime.collect {
it.toString()
}.findAll {
it.toString().contains("deterministic-rt")
}
classpath = sourceSets.main.runtimeClasspath
main = 'net.corda.djvm.tools.cli.Program'
args = ['whitelist', 'generate', '-o', 'src/main/resources/jdk8-deterministic.dat.gz'] + jars
}
jar {
manifest {
attributes(
'Automatic-Module-Name': 'net.corda.djvm'
)
}
}
jar.enabled = false
shadowJar {
baseName = "djvm"
classifier = ""
exclude 'deterministic-rt*.jar'
dependencies {
exclude(dependency('com.jcabi:.*:.*'))
exclude(dependency('org.apache.*:.*:.*'))
@ -87,3 +46,4 @@ shadowJar {
}
}
}
assemble.dependsOn shadowJar

View File

@ -1,16 +1,15 @@
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.3'
plugins {
id 'com.github.johnrengelman.shadow'
}
repositories {
maven {
url "$artifactory_contextUrl/corda-dev"
}
}
plugins {
id 'com.github.johnrengelman.shadow'
configurations {
deterministicRt
}
dependencies {
@ -23,23 +22,29 @@ dependencies {
compile "info.picocli:picocli:$picocli_version"
compile "io.github.lukehutch:fast-classpath-scanner:$fast_classpath_scanner_version"
compile project(path: ":djvm", configuration: "shadow")
// Deterministic runtime - used in whitelist generation
deterministicRt project(path: ':jdk8u-deterministic', configuration: 'jdk')
}
repositories {
mavenLocal()
mavenCentral()
}
jar {
manifest {
attributes(
'Main-Class': 'net.corda.djvm.tools.cli.Program',
'Automatic-Module-Name': 'net.corda.djvm',
'Build-Date': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
)
}
}
jar.enabled = false
shadowJar {
baseName = "corda-djvm"
classifier = 'cli'
manifest {
attributes(
'Automatic-Module-Name': 'net.corda.djvm',
'Main-Class': 'net.corda.djvm.tools.cli.Program',
'Build-Date': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
)
}
}
assemble.dependsOn shadowJar
task generateWhitelist(type: JavaExec, dependsOn: shadowJar) {
// This is an example of how a whitelist can be generated from a JAR. In most applications though, it is recommended
// that the minimal set whitelist is used.
main = '-jar'
args = [shadowJar.outputs.files.singleFile, 'whitelist', 'generate', '-o', "$buildDir/jdk8-deterministic.dat.gz", configurations.deterministicRt.files[0] ]
}

View File

@ -6,6 +6,8 @@ release, see :doc:`upgrade-notes`.
Unreleased
----------
* Removed experimental feature `CordformDefinition`
* Vault query fix: support query by parent classes of Contract State classes (see https://github.com/corda/corda/issues/3714)
* Added ``registerResponderFlow`` method to ``StartedMockNode``, to support isolated testing of responder flow behaviour.

View File

@ -45,3 +45,7 @@ task copyJdk(type: Copy) {
assemble.dependsOn copyJdk
jar.enabled = false
artifacts {
jdk file: file(rt_jar), type: 'jar', builtBy: copyJdk
}

View File

@ -36,8 +36,6 @@ dependencies {
// TODO: Remove this dependency and the code that requires it
compile "commons-fileupload:commons-fileupload:$fileupload_version"
compile "net.corda.plugins:cordform-common:$gradle_plugins_version"
// TypeSafe Config: for simple and human friendly config files.
compile "com.typesafe:config:$typesafe_config_version"

View File

@ -24,7 +24,7 @@ import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.Try
import org.apache.activemq.artemis.api.core.ActiveMQBuffer
import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.client.*
import org.apache.activemq.artemis.api.core.client.ClientMessage
import org.apache.activemq.artemis.api.core.management.CoreNotificationType
import org.apache.activemq.artemis.api.core.management.ManagementHelper
import org.apache.activemq.artemis.reader.MessageUtil
@ -222,6 +222,11 @@ object RPCApi {
}
}
/**
* Thrown if the RPC reply body couldn't be deserialized.
*/
class FailedToDeserializeReply(val id: InvocationId, cause: Throwable) : RuntimeException("Failed to deserialize RPC reply: ${cause.message}", cause)
companion object {
private fun Any.safeSerialize(context: SerializationContext, wrap: (Throwable) -> Any) = try {
serialize(context = context)
@ -236,10 +241,18 @@ object RPCApi {
RPCApi.ServerToClient.Tag.RPC_REPLY -> {
val id = message.invocationId(RPC_ID_FIELD_NAME, RPC_ID_TIMESTAMP_FIELD_NAME) ?: throw IllegalStateException("Cannot parse invocation id from client message.")
val poolWithIdContext = context.withProperty(RpcRequestOrObservableIdKey, id)
// The result here is a Try<> that represents the attempt to try the operation on the server side.
// If anything goes wrong with deserialisation of the response, we propagate it differently because
// we also need to pass through the invocation and dedupe IDs.
val result: Try<Any?> = try {
message.getBodyAsByteArray().deserialize(context = poolWithIdContext)
} catch (e: Exception) {
throw FailedToDeserializeReply(id, e)
}
RpcReply(
id = id,
deduplicationIdentity = deduplicationIdentity,
result = message.getBodyAsByteArray().deserialize(context = poolWithIdContext)
result = result
)
}
RPCApi.ServerToClient.Tag.OBSERVATION -> {

View File

@ -12,6 +12,7 @@ package net.corda.nodeapi.internal
import com.github.benmanes.caffeine.cache.CacheLoader
import com.github.benmanes.caffeine.cache.Caffeine
import net.corda.core.internal.buildNamed
import java.time.Duration
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicLong
@ -19,11 +20,11 @@ import java.util.concurrent.atomic.AtomicLong
/**
* A class allowing the deduplication of a strictly incrementing sequence number.
*/
class DeduplicationChecker(cacheExpiry: Duration) {
class DeduplicationChecker(cacheExpiry: Duration, name: String = "DeduplicationChecker") {
// dedupe identity -> watermark cache
private val watermarkCache = Caffeine.newBuilder()
.expireAfterAccess(cacheExpiry.toNanos(), TimeUnit.NANOSECONDS)
.build(WatermarkCacheLoader)
.buildNamed("${name}_watermark", WatermarkCacheLoader)
private object WatermarkCacheLoader : CacheLoader<Any, AtomicLong> {
override fun load(key: Any) = AtomicLong(-1)

View File

@ -0,0 +1,4 @@
package net.corda.nodeapi.internal
// TODO: Add to Corda node.conf to allow customisation
const val NODE_INFO_DIRECTORY = "additional-node-infos"

View File

@ -12,7 +12,6 @@ package net.corda.nodeapi.internal.network
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import net.corda.cordform.CordformNode
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.*
@ -56,11 +55,11 @@ import kotlin.streams.toList
*/
// TODO Move this to tools:bootstrapper
class NetworkBootstrapper
@VisibleForTesting
internal constructor(private val initSerEnv: Boolean,
private val embeddedCordaJar: () -> InputStream,
private val nodeInfosGenerator: (List<Path>) -> List<Path>,
private val contractsJarConverter: (Path) -> ContractsJar) {
@VisibleForTesting
internal constructor(private val initSerEnv: Boolean,
private val embeddedCordaJar: () -> InputStream,
private val nodeInfosGenerator: (List<Path>) -> List<Path>,
private val contractsJarConverter: (Path) -> ContractsJar) {
constructor() : this(
initSerEnv = true,
@ -112,7 +111,7 @@ class NetworkBootstrapper
process.destroyForcibly()
throw IllegalStateException("Error while generating node info file. Please check the logs in $logsDir.")
}
check(process.exitValue() == 0) { "Error while generating node info file. Please check the logs in $logsDir." }
check(process.exitValue() == 0) { "Error while generating node info file. Please check the logs in $logsDir." }
return nodeDir.list { paths ->
paths.filter { it.fileName.toString().startsWith(NODE_INFO_FILE_NAME_PREFIX) }.findFirst().get()
}
@ -279,7 +278,7 @@ class NetworkBootstrapper
private fun distributeNodeInfos(nodeDirs: List<Path>, nodeInfoFiles: List<Path>) {
for (nodeDir in nodeDirs) {
val additionalNodeInfosDir = (nodeDir / CordformNode.NODE_INFO_DIRECTORY).createDirectories()
val additionalNodeInfosDir = (nodeDir / NODE_INFO_DIRECTORY).createDirectories()
for (nodeInfoFile in nodeInfoFiles) {
nodeInfoFile.copyToDirectory(additionalNodeInfosDir, REPLACE_EXISTING)
}
@ -374,10 +373,10 @@ class NetworkBootstrapper
private fun NodeInfo.notaryIdentity(): Party {
return when (legalIdentities.size) {
// Single node notaries have just one identity like all other nodes. This identity is the notary identity
// Single node notaries have just one identity like all other nodes. This identity is the notary identity
1 -> legalIdentities[0]
// Nodes which are part of a distributed notary have a second identity which is the composite identity of the
// cluster and is shared by all the other members. This is the notary identity.
// Nodes which are part of a distributed notary have a second identity which is the composite identity of the
// cluster and is shared by all the other members. This is the notary identity.
2 -> legalIdentities[1]
else -> throw IllegalArgumentException("Not sure how to get the notary identity in this scenerio: $this")
}

View File

@ -10,9 +10,9 @@
package net.corda.nodeapi.internal.network
import net.corda.cordform.CordformNode
import net.corda.core.internal.*
import net.corda.core.utilities.contextLogger
import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY
import rx.Observable
import rx.Scheduler
import rx.Subscription
@ -106,10 +106,11 @@ class NodeInfoFilesCopier(scheduler: Scheduler = Schedulers.io()) : AutoCloseabl
private fun poll() {
nodeDataMapBox.locked {
for (nodeData in values) {
nodeData.nodeDir.list { paths -> paths
.filter { it.isRegularFile() }
.filter { it.fileName.toString().startsWith(NODE_INFO_FILE_NAME_PREFIX) }
.forEach { processPath(nodeData, it) }
nodeData.nodeDir.list { paths ->
paths
.filter { it.isRegularFile() }
.filter { it.fileName.toString().startsWith(NODE_INFO_FILE_NAME_PREFIX) }
.forEach { processPath(nodeData, it) }
}
}
}
@ -159,7 +160,7 @@ class NodeInfoFilesCopier(scheduler: Scheduler = Schedulers.io()) : AutoCloseabl
* Convenience holder for all the paths and files relative to a single node.
*/
private class NodeData(val nodeDir: Path) {
val additionalNodeInfoDirectory: Path = nodeDir.resolve(CordformNode.NODE_INFO_DIRECTORY)
val additionalNodeInfoDirectory: Path = nodeDir.resolve(NODE_INFO_DIRECTORY)
// Map from Path to its lastModifiedTime.
val previouslySeenFiles = mutableMapOf<Path, FileTime>()

View File

@ -11,6 +11,7 @@
package net.corda.nodeapi.internal.persistence
import com.github.benmanes.caffeine.cache.Caffeine
import net.corda.core.internal.buildNamed
import net.corda.core.internal.castIfPossible
import net.corda.core.schemas.MappedSchema
import net.corda.core.utilities.contextLogger
@ -72,7 +73,7 @@ class HibernateConfiguration(
}
}
private val sessionFactories = Caffeine.newBuilder().maximumSize(databaseConfig.mappedSchemaCacheSize).build<Set<MappedSchema>, SessionFactory>()
private val sessionFactories = Caffeine.newBuilder().maximumSize(databaseConfig.mappedSchemaCacheSize).buildNamed<Set<MappedSchema>, SessionFactory>("HibernateConfiguration_sessionFactories")
val sessionFactoryForRegisteredSchemas = schemas.let {
logger.info("Init HibernateConfiguration for schemas: $it")

View File

@ -1,7 +1,6 @@
package net.corda.nodeapi.internal.network
import com.typesafe.config.ConfigFactory
import net.corda.cordform.CordformNode.NODE_INFO_DIRECTORY
import net.corda.core.crypto.secureRandomBytes
import net.corda.core.crypto.sha256
import net.corda.core.identity.CordaX500Name
@ -11,6 +10,7 @@ import net.corda.core.node.NodeInfo
import net.corda.core.serialization.serialize
import net.corda.node.services.config.NotaryConfig
import net.corda.nodeapi.internal.DEV_ROOT_CA
import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY
import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.config.parseAs
import net.corda.nodeapi.internal.config.toConfig

View File

@ -10,11 +10,11 @@
package net.corda.nodeapi.internal.network
import net.corda.cordform.CordformNode
import net.corda.core.internal.div
import net.corda.core.internal.list
import net.corda.core.internal.write
import net.corda.nodeapi.eventually
import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Rule
@ -44,12 +44,12 @@ class NodeInfoFilesCopierTest {
private val rootPath get() = folder.root.toPath()
private val scheduler = TestScheduler()
private fun nodeDir(nodeBaseDir : String) = rootPath.resolve(nodeBaseDir).resolve(ORGANIZATION.toLowerCase())
private fun nodeDir(nodeBaseDir: String) = rootPath.resolve(nodeBaseDir).resolve(ORGANIZATION.toLowerCase())
private val node1RootPath by lazy { nodeDir(NODE_1_PATH) }
private val node2RootPath by lazy { nodeDir(NODE_2_PATH) }
private val node1AdditionalNodeInfoPath by lazy { node1RootPath.resolve(CordformNode.NODE_INFO_DIRECTORY) }
private val node2AdditionalNodeInfoPath by lazy { node2RootPath.resolve(CordformNode.NODE_INFO_DIRECTORY) }
private val node1AdditionalNodeInfoPath by lazy { node1RootPath.resolve(NODE_INFO_DIRECTORY) }
private val node2AdditionalNodeInfoPath by lazy { node2RootPath.resolve(NODE_INFO_DIRECTORY) }
private lateinit var nodeInfoFilesCopier: NodeInfoFilesCopier

View File

@ -82,6 +82,7 @@ dependencies {
compile project(':client:rpc')
compile project(':tools:shell')
compile "net.corda.plugins:cordform-common:$gradle_plugins_version"
// Log4J: logging framework (with SLF4J bindings)

View File

@ -11,6 +11,7 @@
package net.corda.node.services.network
import net.corda.cordform.CordformNode
import net.corda.core.concurrent.CordaFuture
import net.corda.core.crypto.random63BitValue
import net.corda.core.internal.*
import net.corda.core.internal.concurrent.transpose
@ -19,6 +20,9 @@ import net.corda.core.node.NodeInfo
import net.corda.core.serialization.serialize
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.seconds
import net.corda.node.services.config.configureDevKeyAndTrustStores
import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_UPDATE_FILE_NAME
import net.corda.nodeapi.internal.network.SignedNetworkParameters
@ -252,7 +256,7 @@ class NetworkMapTest(var initFunc: (URL, NetworkMapServer) -> CompatibilityZoneP
private fun NodeHandle.onlySees(vararg nodes: NodeInfo) {
// Make sure the nodes aren't getting the node infos from their additional directories
val nodeInfosDir = baseDirectory / CordformNode.NODE_INFO_DIRECTORY
val nodeInfosDir = baseDirectory / NODE_INFO_DIRECTORY
if (nodeInfosDir.exists()) {
assertThat(nodeInfosDir.list()).isEmpty()
}

View File

@ -15,6 +15,7 @@ import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.Caffeine
import com.google.common.primitives.Ints
import net.corda.core.context.AuthServiceId
import net.corda.core.internal.buildNamed
import net.corda.core.internal.uncheckedCast
import net.corda.core.utilities.loggerFor
import net.corda.node.internal.DataSourceFactory
@ -318,7 +319,7 @@ private class CaffeineCacheManager(val maxSize: Long,
return Caffeine.newBuilder()
.expireAfterWrite(timeToLiveSeconds, TimeUnit.SECONDS)
.maximumSize(maxSize)
.build<K, V>()
.buildNamed<K, V>("RPCSecurityManagerShiroCache_$name")
.toShiroCache()
}

View File

@ -45,6 +45,7 @@ class PersistentIdentityService : SingletonSerializeAsToken(), IdentityServiceIn
fun createPKMap(): AppendOnlyPersistentMap<SecureHash, PartyAndCertificate, PersistentIdentity, String> {
return AppendOnlyPersistentMap(
"PersistentIdentityService_partyByKey",
toPersistentEntityKey = { it.toString() },
fromPersistentEntity = {
Pair(
@ -61,6 +62,7 @@ class PersistentIdentityService : SingletonSerializeAsToken(), IdentityServiceIn
fun createX500Map(): AppendOnlyPersistentMap<CordaX500Name, SecureHash, PersistentIdentityNames, String> {
return AppendOnlyPersistentMap(
"PersistentIdentityService_partyByName",
toPersistentEntityKey = { it.toString() },
fromPersistentEntity = { Pair(CordaX500Name.parse(it.name), SecureHash.parse(it.publicKeyHash)) },
toPersistentEntity = { key: CordaX500Name, value: SecureHash ->

View File

@ -58,6 +58,7 @@ class PersistentKeyManagementService(val identityService: PersistentIdentityServ
private companion object {
fun createKeyMap(): AppendOnlyPersistentMap<PublicKey, PrivateKey, PersistentKey, String> {
return AppendOnlyPersistentMap(
"PersistentKeyManagementService_keys",
toPersistentEntityKey = { it.toStringShort() },
fromPersistentEntity = {
Pair(Crypto.decodePublicKey(it.publicKey),

View File

@ -42,6 +42,7 @@ class P2PMessageDeduplicator(private val database: CordaPersistence) {
private fun createProcessedMessages(): AppendOnlyPersistentMap<DeduplicationId, MessageMeta, ProcessedMessage, String> {
return AppendOnlyPersistentMap(
"P2PMessageDeduplicator_processedMessages",
toPersistentEntityKey = { it.toString },
fromPersistentEntity = { Pair(DeduplicationId(it.id), MessageMeta(it.insertionTime, it.hash, it.seqNo)) },
toPersistentEntity = { key: DeduplicationId, value: MessageMeta ->

View File

@ -23,6 +23,7 @@ import net.corda.core.context.Trace
import net.corda.core.context.Trace.InvocationId
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.LifeCycle
import net.corda.core.internal.buildNamed
import net.corda.core.messaging.RPCOps
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationDefaults
@ -163,7 +164,7 @@ class RPCServer(
log.debug { "Unsubscribing from Observable with id $key because of $cause" }
value!!.subscription.unsubscribe()
}
return Caffeine.newBuilder().removalListener(onObservableRemove).executor(SameThreadExecutor.getExecutor()).build()
return Caffeine.newBuilder().removalListener(onObservableRemove).executor(SameThreadExecutor.getExecutor()).buildNamed("RPCServer_observableSubscription")
}
fun start(activeMqServerControl: ActiveMQServerControl) {

View File

@ -10,7 +10,6 @@
package net.corda.node.services.network
import net.corda.cordform.CordformNode
import net.corda.core.crypto.SecureHash
import net.corda.core.internal.*
import net.corda.core.node.NodeInfo
@ -21,6 +20,7 @@ import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.debug
import net.corda.core.utilities.seconds
import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY
import net.corda.nodeapi.internal.NodeInfoAndSigned
import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier
@ -74,7 +74,8 @@ class NodeInfoWatcher(private val nodePath: Path,
}
internal data class NodeInfoFromFile(val nodeInfohash: SecureHash, val lastModified: FileTime)
private val nodeInfosDir = nodePath / CordformNode.NODE_INFO_DIRECTORY
private val nodeInfosDir = nodePath / NODE_INFO_DIRECTORY
private val nodeInfoFilesMap = HashMap<Path, NodeInfoFromFile>()
val processedNodeInfoHashes: Set<SecureHash> get() = nodeInfoFilesMap.values.map { it.nodeInfohash }.toSet()
@ -84,7 +85,7 @@ class NodeInfoWatcher(private val nodePath: Path,
}
/**
* Read all the files contained in [nodePath] / [CordformNode.NODE_INFO_DIRECTORY] and keep watching
* Read all the files contained in [nodePath] / [NODE_INFO_DIRECTORY] and keep watching
* the folder for further updates.
*
* We simply list the directory content every 5 seconds, the Java implementation of WatchService has been proven to

View File

@ -132,7 +132,9 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence,
override fun getNodesByLegalIdentityKey(identityKey: PublicKey): List<NodeInfo> = nodesByKeyCache[identityKey]!!
private val nodesByKeyCache = NonInvalidatingCache<PublicKey, List<NodeInfo>>(1024) { key ->
private val nodesByKeyCache = NonInvalidatingCache<PublicKey, List<NodeInfo>>(
"PersistentNetworkMap_nodesByKey",
1024) { key ->
database.transaction { queryByIdentityKey(session, key) }
}
@ -150,7 +152,9 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence,
return identityByLegalNameCache.get(name)!!.orElse(null)
}
private val identityByLegalNameCache = NonInvalidatingCache<CordaX500Name, Optional<PartyAndCertificate>>(1024) { name ->
private val identityByLegalNameCache = NonInvalidatingCache<CordaX500Name, Optional<PartyAndCertificate>>(
"PersistentNetworkMap_idByLegalName",
1024) { name ->
Optional.ofNullable(database.transaction { queryIdentityByLegalName(session, name) })
}

View File

@ -63,6 +63,7 @@ class DBTransactionStorage(cacheSizeBytes: Long, private val database: CordaPers
fun createTransactionsMap(maxSizeInBytes: Long)
: AppendOnlyPersistentMapBase<SecureHash, TxCacheValue, DBTransaction, String> {
return WeightBasedAppendOnlyPersistentMap<SecureHash, TxCacheValue, DBTransaction, String>(
name = "DBTransactionStorage_transactions",
toPersistentEntityKey = { it.toString() },
fromPersistentEntity = {
Pair(SecureHash.parse(it.txId),

View File

@ -216,6 +216,7 @@ class NodeAttachmentService(
// a problem somewhere else or this needs to be revisited.
private val attachmentContentCache = NonInvalidatingWeightBasedCache(
name = "NodeAttachmentService_attachmentContent",
maxWeight = attachmentContentCacheSize,
weigher = Weigher<SecureHash, Optional<Pair<Attachment, ByteArray>>> { key, value -> key.size + if (value.isPresent) value.get().second.size else 0 },
loadFunction = { Optional.ofNullable(loadAttachmentContent(it)) }
@ -236,7 +237,9 @@ class NodeAttachmentService(
}
}
private val attachmentCache = NonInvalidatingCache<SecureHash, Optional<Attachment>>(attachmentCacheBound) { key ->
private val attachmentCache = NonInvalidatingCache<SecureHash, Optional<Attachment>>(
"NodeAttachmentService_attachemnt",
attachmentCacheBound) { key ->
Optional.ofNullable(createAttachment(key))
}

View File

@ -58,6 +58,7 @@ class FlowsDrainingModeOperationsImpl(readPhysicalNodeId: () -> String, private
}
internal val map = PersistentMap(
"FlowDrainingMode_nodeProperties",
{ key -> key },
{ entity -> entity.key to entity.value!! },
NodePropertiesPersistentStore::DBNodeProperty,

View File

@ -112,6 +112,7 @@ class BFTNonValidatingNotaryService(
private fun createMap(): AppendOnlyPersistentMap<StateRef, SecureHash, CommittedState, PersistentStateRef> {
return AppendOnlyPersistentMap(
"BFTNonValidatingNotaryService_transactions",
toPersistentEntityKey = { PersistentStateRef(it.txhash.toString(), it.index) },
fromPersistentEntity = {
//TODO null check will become obsolete after making DB/JPA columns not nullable

View File

@ -87,6 +87,7 @@ class PersistentUniquenessProvider(val clock: Clock) : UniquenessProvider, Singl
private val log = contextLogger()
fun createMap(): AppendOnlyPersistentMap<StateRef, SecureHash, CommittedState, PersistentStateRef> =
AppendOnlyPersistentMap(
"PersistentUniquenessProvider_transactions",
toPersistentEntityKey = { PersistentStateRef(it.txhash.toString(), it.index) },
fromPersistentEntity = {
//TODO null check will become obsolete after making DB/JPA columns not nullable

View File

@ -71,6 +71,7 @@ class RaftUniquenessProvider(
private val log = contextLogger()
fun createMap(): AppendOnlyPersistentMap<StateRef, Pair<Long, SecureHash>, CommittedState, PersistentStateRef> =
AppendOnlyPersistentMap(
"RaftUniquenessProvider_transactions",
toPersistentEntityKey = { PersistentStateRef(it) },
fromPersistentEntity = {
val txId = it.id.txId

View File

@ -38,6 +38,7 @@ class ContractUpgradeServiceImpl : ContractUpgradeService, SingletonSerializeAsT
private companion object {
fun createContractUpgradesMap(): PersistentMap<String, String, DBContractUpgrade, String> {
return PersistentMap(
"ContractUpgradeService_upgrades",
toPersistentEntityKey = { it },
fromPersistentEntity = { Pair(it.stateRef, it.upgradedContractClassName ?: "") },
toPersistentEntity = { key: String, value: String ->

View File

@ -319,6 +319,7 @@ abstract class AppendOnlyPersistentMapBase<K, V, E, out EK>(
// Open for tests to override
open class AppendOnlyPersistentMap<K, V, E, out EK>(
name: String,
toPersistentEntityKey: (K) -> EK,
fromPersistentEntity: (E) -> Pair<K, V>,
toPersistentEntity: (key: K, value: V) -> E,
@ -331,6 +332,7 @@ open class AppendOnlyPersistentMap<K, V, E, out EK>(
persistentEntityClass) {
//TODO determine cacheBound based on entity class later or with node config allowing tuning, or using some heuristic based on heap size
override val cache = NonInvalidatingCache(
name = name,
bound = cacheBound,
loadFunction = { key: K ->
// This gets called if a value is read and the cache has no Transactional for this key yet.
@ -363,6 +365,7 @@ open class AppendOnlyPersistentMap<K, V, E, out EK>(
// Same as above, but with weighted values (e.g. memory footprint sensitive).
class WeightBasedAppendOnlyPersistentMap<K, V, E, out EK>(
name: String,
toPersistentEntityKey: (K) -> EK,
fromPersistentEntity: (E) -> Pair<K, V>,
toPersistentEntity: (key: K, value: V) -> E,
@ -375,6 +378,7 @@ class WeightBasedAppendOnlyPersistentMap<K, V, E, out EK>(
toPersistentEntity,
persistentEntityClass) {
override val cache = NonInvalidatingWeightBasedCache(
name,
maxWeight = maxWeight,
weigher = Weigher { key, value -> weighingFunc(key, value) },
loadFunction = { key: K ->

View File

@ -14,18 +14,19 @@ import com.github.benmanes.caffeine.cache.CacheLoader
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.LoadingCache
import com.github.benmanes.caffeine.cache.Weigher
import net.corda.core.internal.buildNamed
class NonInvalidatingCache<K, V> private constructor(
val cache: LoadingCache<K, V>
) : LoadingCache<K, V> by cache {
constructor(bound: Long, loadFunction: (K) -> V) :
this(buildCache(bound, loadFunction))
constructor(name: String, bound: Long, loadFunction: (K) -> V) :
this(buildCache(name, bound, loadFunction))
private companion object {
private fun <K, V> buildCache(bound: Long, loadFunction: (K) -> V): LoadingCache<K, V> {
private fun <K, V> buildCache(name: String, bound: Long, loadFunction: (K) -> V): LoadingCache<K, V> {
val builder = Caffeine.newBuilder().maximumSize(bound)
return builder.build(NonInvalidatingCacheLoader(loadFunction))
return builder.buildNamed(name, NonInvalidatingCacheLoader(loadFunction))
}
}
@ -42,13 +43,13 @@ class NonInvalidatingCache<K, V> private constructor(
class NonInvalidatingWeightBasedCache<K, V> private constructor(
val cache: LoadingCache<K, V>
) : LoadingCache<K, V> by cache {
constructor (maxWeight: Long, weigher: Weigher<K, V>, loadFunction: (K) -> V) :
this(buildCache(maxWeight, weigher, loadFunction))
constructor (name: String, maxWeight: Long, weigher: Weigher<K, V>, loadFunction: (K) -> V) :
this(buildCache(name, maxWeight, weigher, loadFunction))
private companion object {
private fun <K, V> buildCache(maxWeight: Long, weigher: Weigher<K, V>, loadFunction: (K) -> V): LoadingCache<K, V> {
private fun <K, V> buildCache(name: String, maxWeight: Long, weigher: Weigher<K, V>, loadFunction: (K) -> V): LoadingCache<K, V> {
val builder = Caffeine.newBuilder().maximumWeight(maxWeight).weigher(weigher)
return builder.build(NonInvalidatingCache.NonInvalidatingCacheLoader(loadFunction))
return builder.buildNamed(name, NonInvalidatingCache.NonInvalidatingCacheLoader(loadFunction))
}
}
}

View File

@ -15,20 +15,21 @@ import com.github.benmanes.caffeine.cache.CacheLoader
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.LoadingCache
import com.github.benmanes.caffeine.cache.RemovalListener
import net.corda.core.internal.buildNamed
class NonInvalidatingUnboundCache<K, V> private constructor(
val cache: LoadingCache<K, V>
) : LoadingCache<K, V> by cache {
constructor(loadFunction: (K) -> V, removalListener: RemovalListener<K, V> = RemovalListener { _, _, _ -> },
constructor(name: String, loadFunction: (K) -> V, removalListener: RemovalListener<K, V> = RemovalListener { _, _, _ -> },
keysToPreload: () -> Iterable<K> = { emptyList() }) :
this(buildCache(loadFunction, removalListener, keysToPreload))
this(buildCache(name, loadFunction, removalListener, keysToPreload))
private companion object {
private fun <K, V> buildCache(loadFunction: (K) -> V, removalListener: RemovalListener<K, V>,
private fun <K, V> buildCache(name: String, loadFunction: (K) -> V, removalListener: RemovalListener<K, V>,
keysToPreload: () -> Iterable<K>): LoadingCache<K, V> {
val builder = Caffeine.newBuilder().removalListener(removalListener).executor(SameThreadExecutor.getExecutor())
return builder.build(NonInvalidatingCacheLoader(loadFunction)).apply {
return builder.buildNamed(name, NonInvalidatingCacheLoader(loadFunction)).apply {
getAll(keysToPreload())
}
}

View File

@ -20,6 +20,7 @@ import java.util.*
* Implements an unbound caching layer on top of a table accessed via Hibernate mapping.
*/
class PersistentMap<K : Any, V, E, out EK>(
name: String,
val toPersistentEntityKey: (K) -> EK,
val fromPersistentEntity: (E) -> Pair<K, V>,
val toPersistentEntity: (key: K, value: V) -> E,
@ -31,6 +32,7 @@ class PersistentMap<K : Any, V, E, out EK>(
}
private val cache = NonInvalidatingUnboundCache(
name,
loadFunction = { key -> Optional.ofNullable(loadValue(key)) },
removalListener = ExplicitRemoval(toPersistentEntityKey, persistentEntityClass)
)

View File

@ -13,7 +13,6 @@ package net.corda.node.services.network
import com.google.common.jimfs.Configuration.unix
import com.google.common.jimfs.Jimfs
import com.nhaarman.mockito_kotlin.*
import net.corda.cordform.CordformNode.NODE_INFO_DIRECTORY
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sign
@ -25,6 +24,7 @@ import net.corda.core.node.NodeInfo
import net.corda.core.serialization.serialize
import net.corda.core.utilities.millis
import net.corda.node.services.api.NetworkMapCacheInternal
import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY
import net.corda.nodeapi.internal.NodeInfoAndSigned
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_UPDATE_FILE_NAME
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier

View File

@ -12,11 +12,11 @@ package net.corda.node.services.network
import com.google.common.jimfs.Configuration
import com.google.common.jimfs.Jimfs
import net.corda.cordform.CordformNode
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.internal.size
import net.corda.core.node.services.KeyManagementService
import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY
import net.corda.nodeapi.internal.NodeInfoAndSigned
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier
import net.corda.testing.core.ALICE_NAME
@ -61,7 +61,7 @@ class NodeInfoWatcherTest {
val identityService = makeTestIdentityService()
keyManagementService = MockKeyManagementService(identityService)
nodeInfoWatcher = NodeInfoWatcher(tempFolder.root.toPath(), scheduler)
nodeInfoPath = tempFolder.root.toPath() / CordformNode.NODE_INFO_DIRECTORY
nodeInfoPath = tempFolder.root.toPath() / NODE_INFO_DIRECTORY
}
@Test

View File

@ -271,6 +271,7 @@ class AppendOnlyPersistentMapTest(var scenario: Scenario) {
)
class TestMap : AppendOnlyPersistentMap<Long, String, PersistentMapEntry, Long>(
"ApoendOnlyPersistentMap_test",
toPersistentEntityKey = { it },
fromPersistentEntity = { Pair(it.key, it.value) },
toPersistentEntity = { key: Long, value: String ->

View File

@ -16,6 +16,7 @@ class PersistentMapTests {
//create a test map using an existing db table
private fun createTestMap(): PersistentMap<String, String, ContractUpgradeServiceImpl.DBContractUpgrade, String> {
return PersistentMap(
"Test_test",
toPersistentEntityKey = { it },
fromPersistentEntity = { Pair(it.stateRef, it.upgradedContractClassName ?: "") },
toPersistentEntity = { key: String, value: String ->

View File

@ -17,21 +17,6 @@ apply plugin: 'net.corda.plugins.cordapp'
apply plugin: 'net.corda.plugins.cordformation'
apply plugin: 'maven-publish'
sourceSets {
integrationTest {
kotlin {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/kotlin')
}
}
}
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
@ -58,13 +43,46 @@ dependencies {
}
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
// CordformationDefinition is an experimental feature
definitionClass = 'net.corda.bank.BankOfCordaCordform'
}
task integrationTest(type: Test, dependsOn: []) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
nodeDefaults {
cordapp project(':finance')
}
node {
name "O=Notary Service,L=Zurich,C=CH"
notary = [validating: true]
p2pPort 10002
rpcSettings {
address "localhost:10003"
adminAddress "localhost:10004"
}
extraConfig = [h2Settings: [address: "localhost:10016"]]
}
node {
name "O=BankOfCorda,L=London,C=GB"
p2pPort 10005
rpcSettings {
address "localhost:10006"
adminAddress "localhost:10015"
}
webPort 10007
rpcUsers = [[user: "bankUser", password: "test", permissions: ["ALL"]]]
extraConfig = [
custom : [issuableCurrencies: ["USD"]],
h2Settings: [address: "localhost:10017"]
]
}
node {
name "O=BigCorporation,L=New York,C=US"
p2pPort 10008
rpcSettings {
address "localhost:10009"
adminAddress "localhost:10011"
}
webPort 10010
rpcUsers = [[user: "bigCorpUser", password: "test", permissions: ["ALL"]]]
extraConfig = [
h2Settings: [address: "localhost:10018"]
]
}
}
idea {

View File

@ -1,51 +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.contracts.withoutIssuer
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.finance.DOLLARS
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
class BankOfCordaCordformTest : IntegrationTest() {
companion object {
@ClassRule
@JvmField
val databaseSchemas = IntegrationTestSchemas("NotaryService", "BankOfCorda", BIGCORP_NAME.organisation)
}
@Test
fun `run demo`() {
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,159 +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 joptsimple.OptionParser
import net.corda.bank.api.BankOfCordaClientApi
import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams
import net.corda.cordform.CordappDependency
import net.corda.cordform.CordformContext
import net.corda.cordform.CordformDefinition
import net.corda.core.contracts.Amount
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.VisibleForTesting
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.services.Permissions.Companion.all
import net.corda.node.services.config.NotaryConfig
import net.corda.testing.node.internal.demorun.*
import net.corda.testing.core.BOC_NAME
import net.corda.testing.node.User
import java.util.*
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")
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 {
node {
name(NOTARY_NAME)
notary(NotaryConfig(validating = true))
p2pPort(10002)
rpcSettings {
address("localhost:10003")
adminAddress("localhost:10004")
}
devMode(true)
extraConfig = mapOf("h2Settings" to mapOf("address" to "localhost:10016"))
}
node {
name(BOC_NAME)
extraConfig = mapOf("custom" to mapOf("issuableCurrencies" to listOf("USD")),
"h2Settings" to mapOf("address" to "localhost:10017"))
p2pPort(10005)
rpcSettings {
address("localhost:$BOC_RPC_PORT")
adminAddress("localhost:$BOC_RPC_ADMIN_PORT")
}
webPort(BOC_WEB_PORT)
rpcUsers(User(BOC_RPC_USER, BOC_RPC_PWD, setOf(all())))
devMode(true)
}
node {
name(BIGCORP_NAME)
p2pPort(10008)
rpcSettings {
address("localhost:$BIGCORP_RPC_PORT")
adminAddress("localhost:10011")
}
webPort(10010)
rpcUsers(User(BIGCORP_RPC_USER, BIGCORP_RPC_PWD, setOf(all())))
devMode(true)
extraConfig = mapOf("h2Settings" to mapOf("address" to "localhost:10018"))
}
}
override fun setup(context: CordformContext) = Unit
override fun getCordappDependencies(): List<CordappDependency> {
return listOf(CordappDependency(":finance"))
}
}
object DeployNodes {
@JvmStatic
fun main(args: Array<String>) {
BankOfCordaCordform().nodeRunner().scanPackages(listOf("net.corda.finance")).deployAndRunNodes()
}
}
object IssueCash {
@JvmStatic
fun main(args: Array<String>) {
val parser = OptionParser()
val roleArg = parser.accepts("role").withRequiredArg().ofType(Role::class.java).describedAs("[ISSUER|ISSUE_CASH_RPC|ISSUE_CASH_WEB]")
val quantity = parser.accepts("quantity").withOptionalArg().ofType(Long::class.java)
val currency = parser.accepts("currency").withOptionalArg().ofType(String::class.java).describedAs("[GBP|USD|CHF|EUR]")
val options = try {
parser.parse(*args)
} catch (e: Exception) {
println(e.message)
printHelp(parser)
exitProcess(1)
}
val role = options.valueOf(roleArg)!!
val amount = Amount(options.valueOf(quantity), Currency.getInstance(options.valueOf(currency)))
when (role) {
Role.ISSUE_CASH_RPC -> {
println("Requesting Cash via RPC ...")
val result = requestRpcIssue(amount)
println("Success!! Your transaction receipt is ${result.tx.id}")
}
Role.ISSUE_CASH_WEB -> {
println("Requesting Cash via Web ...")
requestWebIssue(amount)
println("Successfully processed Cash Issue request")
}
}
}
@VisibleForTesting
fun requestRpcIssue(amount: Amount<Currency>): SignedTransaction {
return BankOfCordaClientApi.requestRPCIssue(NetworkHostAndPort("localhost", BOC_RPC_PORT), createParams(amount, NOTARY_NAME))
}
private fun requestWebIssue(amount: Amount<Currency>) {
BankOfCordaClientApi.requestWebIssue(NetworkHostAndPort("localhost", BOC_WEB_PORT), createParams(amount, NOTARY_NAME))
}
private fun createParams(amount: Amount<Currency>, notaryName: CordaX500Name): IssueRequestParams {
return IssueRequestParams(amount, BIGCORP_NAME, "1", BOC_NAME, notaryName)
}
private fun printHelp(parser: OptionParser) {
println("""
Usage: bank-of-corda --role ISSUER
bank-of-corda --role (ISSUE_CASH_RPC|ISSUE_CASH_WEB) --quantity <quantity> --currency <currency>
Please refer to the documentation in docs/build/index.html for more info.
""".trimIndent())
parser.printHelpOn(System.out)
}
enum class Role {
ISSUE_CASH_RPC,
ISSUE_CASH_WEB,
}
}

View File

@ -0,0 +1,77 @@
package net.corda.bank
import joptsimple.OptionParser
import net.corda.bank.api.BankOfCordaClientApi
import net.corda.bank.api.BankOfCordaWebApi
import net.corda.core.contracts.Amount
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.VisibleForTesting
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.testing.core.BOC_NAME
import java.util.*
import kotlin.system.exitProcess
object IssueCash {
private val NOTARY_NAME = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH")
private val BIGCORP_NAME = CordaX500Name(organisation = "BigCorporation", locality = "New York", country = "US")
private const val BOC_RPC_PORT = 10006
private const val BOC_WEB_PORT = 10007
@JvmStatic
fun main(args: Array<String>) {
val parser = OptionParser()
val roleArg = parser.accepts("role").withRequiredArg().ofType(Role::class.java).describedAs("[ISSUER|ISSUE_CASH_RPC|ISSUE_CASH_WEB]")
val quantity = parser.accepts("quantity").withOptionalArg().ofType(Long::class.java)
val currency = parser.accepts("currency").withOptionalArg().ofType(String::class.java).describedAs("[GBP|USD|CHF|EUR]")
val options = try {
parser.parse(*args)
} catch (e: Exception) {
println(e.message)
printHelp(parser)
exitProcess(1)
}
val role = options.valueOf(roleArg)!!
val amount = Amount(options.valueOf(quantity), Currency.getInstance(options.valueOf(currency)))
when (role) {
Role.ISSUE_CASH_RPC -> {
println("Requesting Cash via RPC ...")
val result = requestRpcIssue(amount)
println("Success!! Your transaction receipt is ${result.tx.id}")
}
Role.ISSUE_CASH_WEB -> {
println("Requesting Cash via Web ...")
requestWebIssue(amount)
println("Successfully processed Cash Issue request")
}
}
}
@VisibleForTesting
fun requestRpcIssue(amount: Amount<Currency>): SignedTransaction {
return BankOfCordaClientApi.requestRPCIssue(NetworkHostAndPort("localhost", BOC_RPC_PORT), createParams(amount, NOTARY_NAME))
}
private fun requestWebIssue(amount: Amount<Currency>) {
BankOfCordaClientApi.requestWebIssue(NetworkHostAndPort("localhost", BOC_WEB_PORT), createParams(amount, NOTARY_NAME))
}
private fun createParams(amount: Amount<Currency>, notaryName: CordaX500Name): BankOfCordaWebApi.IssueRequestParams {
return BankOfCordaWebApi.IssueRequestParams(amount, BIGCORP_NAME, "1", BOC_NAME, notaryName)
}
private fun printHelp(parser: OptionParser) {
println("""
Usage: bank-of-corda --role ISSUER
bank-of-corda --role (ISSUE_CASH_RPC|ISSUE_CASH_WEB) --quantity <quantity> --currency <currency>
Please refer to the documentation in docs/build/index.html for more info.
""".trimIndent())
parser.printHelpOn(System.out)
}
enum class Role {
ISSUE_CASH_RPC,
ISSUE_CASH_WEB,
}
}

View File

@ -10,8 +10,6 @@
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
@ -26,6 +24,9 @@ import net.corda.testing.http.HttpApi
* Interface for communicating with Bank of Corda node
*/
object BankOfCordaClientApi {
const val BOC_RPC_USER = "bankUser"
const val BOC_RPC_PWD = "test"
/**
* HTTP API
*/
@ -50,7 +51,8 @@ object BankOfCordaClientApi {
// Resolve parties via RPC
val issueToParty = rpc.wellKnownPartyFromX500Name(params.issueToPartyName)
?: throw IllegalStateException("Unable to locate ${params.issueToPartyName} in Network Map Service")
val notaryLegalIdentity = rpc.notaryIdentities().firstOrNull { it.name == params.notaryName } ?: throw IllegalStateException("Couldn't locate notary ${params.notaryName} in NetworkMapCache")
val notaryLegalIdentity = rpc.notaryIdentities().firstOrNull { it.name == params.notaryName }
?: throw IllegalStateException("Couldn't locate notary ${params.notaryName} in NetworkMapCache")
val anonymous = true
val issuerBankPartyRef = OpaqueBytes.of(params.issuerBankPartyRef.toByte())

View File

@ -59,19 +59,229 @@ publishing {
task deployNodes(dependsOn: ['deployNodesSingle', 'deployNodesRaft', 'deployNodesBFT', 'deployNodesCustom'])
task deployNodesSingle(type: Cordform, dependsOn: 'jar') {
definitionClass = 'net.corda.notarydemo.SingleNotaryCordform'
directory file("$buildDir/nodes/nodesSingle")
nodeDefaults {
extraConfig = [h2Settings: [address: "localhost:0"]]
}
node {
name "O=Alice Corp,L=Madrid,C=ES"
p2pPort 10002
rpcSettings {
address "localhost:10003"
adminAddress "localhost:10103"
}
rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]]
}
node {
name "O=Bob Plc,L=Rome,C=IT"
p2pPort 10005
rpcSettings {
address "localhost:10006"
adminAddress "localhost:10106"
}
}
node {
name "O=Notary Service,L=Zurich,C=CH"
p2pPort 10009
rpcSettings {
address "localhost:10010"
adminAddress "localhost:10110"
}
notary = [validating: true]
}
}
task deployNodesCustom(type: Cordform, dependsOn: 'jar') {
definitionClass = 'net.corda.notarydemo.CustomNotaryCordform'
directory file("$buildDir/nodes/nodesCustom")
nodeDefaults {
extraConfig = [h2Settings: [address: "localhost:0"]]
}
node {
name "O=Alice Corp,L=Madrid,C=ES"
p2pPort 10002
rpcSettings {
address "localhost:10003"
adminAddress "localhost:10103"
}
rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]]
}
node {
name "O=Bob Plc,L=Rome,C=IT"
p2pPort 10005
rpcSettings {
address "localhost:10006"
adminAddress "localhost:10106"
}
}
node {
name "O=Notary Service,L=Zurich,C=CH"
p2pPort 10009
rpcSettings {
address "localhost:10010"
adminAddress "localhost:10110"
}
notary = [validating: true, "custom": true]
}
}
task deployNodesRaft(type: Cordform, dependsOn: 'jar') {
definitionClass = 'net.corda.notarydemo.RaftNotaryCordform'
directory file("$buildDir/nodes/nodesRaft")
nodeDefaults {
extraConfig = [h2Settings: [address: "localhost:0"]]
}
node {
name "O=Alice Corp,L=Madrid,C=ES"
p2pPort 10002
rpcSettings {
address "localhost:10003"
adminAddress "localhost:10103"
}
rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]]
}
node {
name "O=Bob Plc,L=Rome,C=IT"
p2pPort 10005
rpcSettings {
address "localhost:10006"
adminAddress "localhost:10106"
}
}
node {
name "O=Notary Service 0,L=Zurich,C=CH"
p2pPort 10009
rpcSettings {
address "localhost:10010"
adminAddress "localhost:10110"
}
notary = [
validating: true,
serviceLegalName: "O=Raft,L=Zurich,C=CH",
raft: [
nodeAddress: "localhost:10008"
]
]
}
node {
name "O=Notary Service 1,L=Zurich,C=CH"
p2pPort 10013
rpcSettings {
address "localhost:10014"
adminAddress "localhost:10114"
}
notary = [
validating: true,
serviceLegalName: "O=Raft,L=Zurich,C=CH",
raft: [
nodeAddress: "localhost:10012",
clusterAddresses: ["localhost:10008"]
]
]
}
node {
name "O=Notary Service 2,L=Zurich,C=CH"
p2pPort 10017
rpcSettings {
address "localhost:10018"
adminAddress "localhost:10118"
}
notary = [
validating: true,
serviceLegalName: "O=Raft,L=Zurich,C=CH",
raft: [
nodeAddress: "localhost:10016",
clusterAddresses: ["localhost:10008"]
]
]
}
}
task deployNodesBFT(type: Cordform, dependsOn: 'jar') {
definitionClass = 'net.corda.notarydemo.BFTNotaryCordform'
def clusterAddresses = ["localhost:11000", "localhost:11010", "localhost:11020", "localhost:11030"]
directory file("$buildDir/nodes/nodesBFT")
nodeDefaults {
extraConfig = [h2Settings: [address: "localhost:0"]]
}
node {
name "O=Alice Corp,L=Madrid,C=ES"
p2pPort 10002
rpcSettings {
address "localhost:10003"
adminAddress "localhost:10103"
}
rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]]
}
node {
name "O=Bob Plc,L=Rome,C=IT"
p2pPort 10005
rpcSettings {
address "localhost:10006"
adminAddress "localhost:10106"
}
}
node {
name "O=Notary Service 0,L=Zurich,C=CH"
p2pPort 10009
rpcSettings {
address "localhost:10010"
adminAddress "localhost:10110"
}
notary = [
validating: false,
serviceLegalName: "O=BFT,L=Zurich,C=CH",
bftSMaRt: [
replicaId: 0,
clusterAddresses: clusterAddresses
]
]
}
node {
name "O=Notary Service 1,L=Zurich,C=CH"
p2pPort 10013
rpcSettings {
address "localhost:10014"
adminAddress "localhost:10114"
}
notary = [
validating: false,
serviceLegalName: "O=BFT,L=Zurich,C=CH",
bftSMaRt: [
replicaId: 0,
clusterAddresses: clusterAddresses
]
]
}
node {
name "O=Notary Service 2,L=Zurich,C=CH"
p2pPort 10017
rpcSettings {
address "localhost:10018"
adminAddress "localhost:10118"
}
notary = [
validating: false,
serviceLegalName: "O=BFT,L=Zurich,C=CH",
bftSMaRt: [
replicaId: 0,
clusterAddresses: clusterAddresses
]
]
}
node {
name "O=Notary Service 3,L=Zurich,C=CH"
p2pPort 10021
rpcSettings {
address "localhost:10022"
adminAddress "localhost:10122"
}
notary = [
validating: false,
serviceLegalName: "O=BFT,L=Zurich,C=CH",
bftSMaRt: [
replicaId: 0,
clusterAddresses: clusterAddresses
]
]
}
}
task notarise(type: JavaExec) {

View File

@ -1,103 +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.notarydemo
import net.corda.cordform.CordformContext
import net.corda.cordform.CordformDefinition
import net.corda.cordform.CordformNode
import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.services.config.BFTSMaRtConfiguration
import net.corda.node.services.config.NotaryConfig
import net.corda.node.services.transactions.minCorrectReplicas
import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.testing.node.internal.demorun.*
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import java.nio.file.Paths
fun main(args: Array<String>) = BFTNotaryCordform().nodeRunner().deployAndRunNodes()
private const val clusterSize = 4 // Minimum size that tolerates a faulty replica.
private val notaryNames = createNotaryNames(clusterSize)
// This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO
// NOT use this as a design to copy.
class BFTNotaryCordform : CordformDefinition() {
private val clusterName = CordaX500Name("BFT", "Zurich", "CH")
init {
nodesDirectory = Paths.get("build", "nodes", "nodesBFT")
node {
name(ALICE_NAME)
p2pPort(10002)
rpcSettings {
address("localhost:10003")
adminAddress("localhost:10103")
}
rpcUsers(notaryDemoUser)
devMode(true)
extraConfig = mapOf("h2Settings" to mapOf("address" to "localhost:0"))
}
node {
name(BOB_NAME)
p2pPort(10005)
rpcSettings {
address("localhost:10006")
adminAddress("localhost:10106")
}
devMode(true)
extraConfig = mapOf("h2Settings" to mapOf("address" to "localhost:0"))
}
val clusterAddresses = (0 until clusterSize).map { NetworkHostAndPort("localhost", 11000 + it * 10) }
fun notaryNode(replicaId: Int, configure: CordformNode.() -> Unit) = node {
name(notaryNames[replicaId])
notary(NotaryConfig(validating = false, serviceLegalName = clusterName, bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses)))
extraConfig = mapOf("h2Settings" to mapOf("address" to "localhost:0"))
configure()
}
notaryNode(0) {
p2pPort(10009)
rpcSettings {
address("localhost:10010")
adminAddress("localhost:10110")
}
devMode(true)
}
notaryNode(1) {
p2pPort(10013)
rpcSettings {
address("localhost:10014")
adminAddress("localhost:10114")
}
devMode(true)
}
notaryNode(2) {
p2pPort(10017)
rpcSettings {
address("localhost:10018")
adminAddress("localhost:10118")
}
devMode(true)
}
notaryNode(3) {
p2pPort(10021)
rpcSettings {
address("localhost:10022")
adminAddress("localhost:10122")
}
devMode(true)
}
}
override fun setup(context: CordformContext) {
}
}

View File

@ -1,19 +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.notarydemo
import net.corda.testing.node.internal.demorun.nodeRunner
fun main(args: Array<String>) {
listOf(SingleNotaryCordform(), RaftNotaryCordform(), BFTNotaryCordform()).map { it.nodeRunner() }.forEach {
it.clean()
}
}

View File

@ -1,62 +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.notarydemo
import net.corda.cordform.CordformContext
import net.corda.cordform.CordformDefinition
import net.corda.node.services.config.NotaryConfig
import net.corda.testing.node.internal.demorun.*
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME
import java.nio.file.Paths
fun main(args: Array<String>) = CustomNotaryCordform().nodeRunner().deployAndRunNodes()
class CustomNotaryCordform : CordformDefinition() {
init {
nodesDirectory = Paths.get("build", "nodes", "nodesCustom")
node {
name(ALICE_NAME)
p2pPort(10002)
rpcSettings {
address("localhost:10003")
adminAddress("localhost:10103")
}
rpcUsers(notaryDemoUser)
devMode(true)
extraConfig = mapOf("h2Settings" to mapOf("address" to "localhost:0"))
}
node {
name(BOB_NAME)
p2pPort(10005)
rpcSettings {
address("localhost:10006")
adminAddress("localhost:10106")
}
devMode(true)
extraConfig = mapOf("h2Settings" to mapOf("address" to "localhost:0"))
}
node {
name(DUMMY_NOTARY_NAME)
p2pPort(10009)
rpcSettings {
address("localhost:10010")
adminAddress("localhost:10110")
}
notary(NotaryConfig(validating = true, custom = true))
devMode(true)
extraConfig = mapOf("h2Settings" to mapOf("address" to "localhost:0"))
}
}
override fun setup(context: CordformContext) {}
}

View File

@ -27,7 +27,7 @@ import java.util.concurrent.Future
fun main(args: Array<String>) {
val address = NetworkHostAndPort("localhost", 10003)
println("Connecting to the recipient node ($address)")
CordaRPCClient(address).start(notaryDemoUser.username, notaryDemoUser.password).use {
CordaRPCClient(address).start("demou", "demop").use {
NotaryDemoClientApi(it.proxy).notarise(10)
}
}

View File

@ -1,96 +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.notarydemo
import net.corda.cordform.CordformContext
import net.corda.cordform.CordformDefinition
import net.corda.cordform.CordformNode
import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.services.config.NotaryConfig
import net.corda.node.services.config.RaftConfig
import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.node.internal.demorun.*
import java.nio.file.Paths
fun main(args: Array<String>) = RaftNotaryCordform().nodeRunner().deployAndRunNodes()
internal fun createNotaryNames(clusterSize: Int) = (0 until clusterSize).map { CordaX500Name("Notary Service $it", "Zurich", "CH") }
private val notaryNames = createNotaryNames(3)
// This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO
// NOT use this as a design to copy.
class RaftNotaryCordform : CordformDefinition() {
private val clusterName = CordaX500Name("Raft", "Zurich", "CH")
init {
nodesDirectory = Paths.get("build", "nodes", "nodesRaft")
node {
name(ALICE_NAME)
p2pPort(10002)
rpcSettings {
address("localhost:10003")
adminAddress("localhost:10103")
}
rpcUsers(notaryDemoUser)
devMode(true)
extraConfig = mapOf("h2Settings" to mapOf("address" to "localhost:0"))
}
node {
name(BOB_NAME)
p2pPort(10005)
rpcSettings {
address("localhost:10006")
adminAddress("localhost:10106")
}
devMode(true)
extraConfig = mapOf("h2Settings" to mapOf("address" to "localhost:0"))
}
fun notaryNode(index: Int, nodePort: Int, clusterPort: Int? = null, configure: CordformNode.() -> Unit) = node {
name(notaryNames[index])
val clusterAddresses = if (clusterPort != null) listOf(NetworkHostAndPort("localhost", clusterPort)) else emptyList()
notary(NotaryConfig(validating = true, serviceLegalName = clusterName, raft = RaftConfig(NetworkHostAndPort("localhost", nodePort), clusterAddresses)))
extraConfig = mapOf("h2Settings" to mapOf("address" to "localhost:0"))
configure()
devMode(true)
}
notaryNode(0, 10008) {
p2pPort(10009)
rpcSettings {
address("localhost:10010")
adminAddress("localhost:10110")
}
devMode(true)
}
notaryNode(1, 10012, 10008) {
p2pPort(10013)
rpcSettings {
address("localhost:10014")
adminAddress("localhost:10114")
}
devMode(true)
}
notaryNode(2, 10016, 10008) {
p2pPort(10017)
rpcSettings {
address("localhost:10018")
adminAddress("localhost:10118")
}
devMode(true)
}
}
override fun setup(context: CordformContext) {
}
}

View File

@ -1,68 +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.notarydemo
import net.corda.cordform.CordformContext
import net.corda.cordform.CordformDefinition
import net.corda.node.services.Permissions.Companion.all
import net.corda.node.services.config.NotaryConfig
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.node.User
import net.corda.testing.node.internal.demorun.*
import java.nio.file.Paths
fun main(args: Array<String>) = SingleNotaryCordform().nodeRunner().deployAndRunNodes()
val notaryDemoUser = User("demou", "demop", setOf(all()))
// This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO
// NOT use this as a design to copy.
class SingleNotaryCordform : CordformDefinition() {
init {
nodesDirectory = Paths.get("build", "nodes", "nodesSingle")
node {
name(ALICE_NAME)
p2pPort(10002)
rpcSettings {
address("localhost:10003")
adminAddress("localhost:10103")
}
rpcUsers(notaryDemoUser)
devMode(true)
extraConfig = mapOf("h2Settings" to mapOf("address" to "localhost:0"))
}
node {
name(BOB_NAME)
p2pPort(10005)
rpcSettings {
address("localhost:10006")
adminAddress("localhost:10106")
}
devMode(true)
extraConfig = mapOf("h2Settings" to mapOf("address" to "localhost:0"))
}
node {
name(DUMMY_NOTARY_NAME)
p2pPort(10009)
rpcSettings {
address("localhost:10010")
adminAddress("localhost:10110")
}
notary(NotaryConfig(validating = true))
devMode(true)
extraConfig = mapOf("h2Settings" to mapOf("address" to "localhost:0"))
}
}
override fun setup(context: CordformContext) {}
}

View File

@ -16,6 +16,7 @@ import net.corda.core.DeleteForDJVM
import net.corda.core.KeepForDJVM
import net.corda.core.contracts.Attachment
import net.corda.core.crypto.SecureHash
import net.corda.core.internal.buildNamed
import net.corda.core.internal.copyBytes
import net.corda.core.serialization.*
import net.corda.core.utilities.ByteSequence
@ -86,7 +87,7 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe
*/
@DeleteForDJVM
internal class AttachmentsClassLoaderBuilder(private val properties: Map<Any, Any>, private val deserializationClassLoader: ClassLoader) {
private val cache: Cache<List<SecureHash>, AttachmentsClassLoader> = Caffeine.newBuilder().weakValues().maximumSize(1024).build()
private val cache: Cache<List<SecureHash>, AttachmentsClassLoader> = Caffeine.newBuilder().weakValues().maximumSize(1024).buildNamed("SerializationScheme_attachmentClassloader")
fun build(attachmentHashes: List<SecureHash>): AttachmentsClassLoader? {
val serializationContext = properties[serializationContextKey] as? SerializeAsTokenContext ?: return null // Some tests don't set one.

View File

@ -111,7 +111,7 @@ class DeserializationInput constructor(
} catch (nse: NotSerializableException) {
throw nse
} catch (t: Throwable) {
throw NotSerializableException("Unexpected throwable: ${t.message}").apply { initCause(t) }
throw NotSerializableException("Internal deserialization failure: ${t.javaClass.name}: ${t.message}").apply { initCause(t) }
} finally {
objectHistory.clear()
}

View File

@ -36,7 +36,6 @@ sourceSets {
dependencies {
compile project(':test-utils')
compile "net.corda.plugins:cordform-common:$gradle_plugins_version"
// Integration test helpers
testCompile "org.assertj:assertj-core:$assertj_version"

View File

@ -10,15 +10,12 @@
package net.corda.testing.node.internal
import com.google.common.collect.HashMultimap
import com.google.common.util.concurrent.ThreadFactoryBuilder
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigRenderOptions
import com.typesafe.config.ConfigValueFactory
import net.corda.client.rpc.internal.createCordaRPCClientWithInternalSslAndClassLoader
import net.corda.cordform.CordformContext
import net.corda.cordform.CordformNode
import net.corda.core.concurrent.CordaFuture
import net.corda.core.concurrent.firstOf
import net.corda.core.identity.CordaX500Name
@ -40,6 +37,7 @@ import net.corda.node.utilities.registration.NodeRegistrationHelper
import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.addShutdownHook
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
import net.corda.nodeapi.internal.config.parseAs
import net.corda.nodeapi.internal.config.toConfig
@ -60,8 +58,6 @@ import net.corda.testing.internal.setGlobalSerialization
import net.corda.testing.node.ClusterSpec
import net.corda.testing.node.NotarySpec
import net.corda.testing.node.User
import net.corda.testing.node.internal.DriverDSLImpl.ClusterType.NON_VALIDATING_RAFT
import net.corda.testing.node.internal.DriverDSLImpl.ClusterType.VALIDATING_RAFT
import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages
import okhttp3.OkHttpClient
import okhttp3.Request
@ -344,97 +340,6 @@ class DriverDSLImpl(
NON_VALIDATING_BFT(false, CordaX500Name("BFT", "Zurich", "CH"))
}
// TODO remove this
internal fun startCordformNodes(cordforms: List<CordformNode>): CordaFuture<*> {
check(notarySpecs.isEmpty()) { "Specify notaries in the CordformDefinition" }
check(compatibilityZone == null) { "Cordform nodes cannot be run with compatibilityZoneURL" }
val clusterNodes = HashMultimap.create<ClusterType, CordaX500Name>()
val notaryInfos = ArrayList<NotaryInfo>()
// Go though the node definitions and pick out the notaries so that we can generate their identities to be used
// in the network parameters
for (cordform in cordforms) {
if (cordform.notary == null) continue
val name = CordaX500Name.parse(cordform.name)
val notaryConfig = ConfigFactory.parseMap(cordform.notary).parseAs<NotaryConfig>()
// We need to first group the nodes that form part of a cluster. We assume for simplicity that nodes of the
// same cluster type and validating flag are part of the same cluster.
when {
notaryConfig.raft != null -> {
val key = if (notaryConfig.validating) VALIDATING_RAFT else NON_VALIDATING_RAFT
clusterNodes.put(key, name)
}
notaryConfig.bftSMaRt != null -> clusterNodes.put(ClusterType.NON_VALIDATING_BFT, name)
else -> {
// We have all we need here to generate the identity for single node notaries
val identity = DevIdentityGenerator.installKeyStoreWithNodeIdentity(baseDirectory(name), legalName = name)
notaryInfos += NotaryInfo(identity, notaryConfig.validating)
}
}
}
clusterNodes.asMap().forEach { type, nodeNames ->
val identity = if (type == ClusterType.NON_VALIDATING_RAFT || type == ClusterType.VALIDATING_RAFT) {
DevIdentityGenerator.generateDistributedNotarySingularIdentity(
dirs = nodeNames.map { baseDirectory(it) },
notaryName = type.clusterName
)
} else {
DevIdentityGenerator.generateDistributedNotaryCompositeIdentity(
dirs = nodeNames.map { baseDirectory(it) },
notaryName = type.clusterName
)
}
notaryInfos += NotaryInfo(identity, type.validating)
}
val localNetworkMap = LocalNetworkMap(notaryInfos)
return cordforms.map {
val startedNode = startCordformNode(it, localNetworkMap)
if (it.webAddress != null) {
// Start a webserver if an address for it was specified
startedNode.flatMap { startWebserver(it) }
} else {
startedNode
}
}.transpose()
}
// TODO remove this
private fun startCordformNode(cordform: CordformNode, localNetworkMap: LocalNetworkMap): CordaFuture<NodeHandle> {
val name = CordaX500Name.parse(cordform.name)
// TODO We shouldn't have to allocate an RPC or web address if they're not specified. We're having to do this because of startNodeInternal
val rpcAddress = if (cordform.rpcAddress == null) {
val overrides = mutableMapOf<String, Any>("rpcSettings.address" to portAllocation.nextHostAndPort().toString())
cordform.config.apply {
overrides += "rpcSettings.adminAddress" to portAllocation.nextHostAndPort().toString()
}
overrides
} else {
val overrides = mutableMapOf<String, Any>()
cordform.config.apply {
overrides += "rpcSettings.adminAddress" to portAllocation.nextHostAndPort().toString()
}
overrides
}
val webAddress = cordform.webAddress?.let { NetworkHostAndPort.parse(it) } ?: portAllocation.nextHostAndPort()
val notary = if (cordform.notary != null) mapOf("notary" to cordform.notary) else emptyMap()
val rpcUsers = cordform.rpcUsers
val rawConfig = cordform.config + rpcAddress + notary + mapOf(
"rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers
)
val typesafe = ConfigHelper.loadConfig(
baseDirectory = baseDirectory(name),
allowMissingConfig = true,
configOverrides = rawConfig.toNodeOnly()
)
val config = NodeConfig(typesafe).checkAndOverrideForInMemoryDB()
return startNodeInternal(config, webAddress, null, "512m", localNetworkMap, emptySet())
}
@Suppress("DEPRECATION")
private fun queryWebserver(handle: NodeHandle, process: Process): WebserverHandle {
val protocol = if ((handle as NodeHandleInternal).useHTTPS) "https://" else "http://"
@ -1045,7 +950,7 @@ private class NetworkVisibilityController {
}
}
interface InternalDriverDSL : DriverDSL, CordformContext {
interface InternalDriverDSL : DriverDSL {
private companion object {
private val DEFAULT_POLL_INTERVAL = 500.millis
private const val DEFAULT_WARN_COUNT = 120
@ -1053,7 +958,7 @@ interface InternalDriverDSL : DriverDSL, CordformContext {
val shutdownManager: ShutdownManager
override fun baseDirectory(nodeName: String): Path = baseDirectory(CordaX500Name.parse(nodeName))
fun baseDirectory(nodeName: String): Path = baseDirectory(CordaX500Name.parse(nodeName))
/**
* Polls a function until it returns a non-null value. Note that there is no timeout on the polling.
@ -1287,4 +1192,3 @@ fun DriverDSL.startNode(providedName: CordaX500Name, devMode: Boolean, parameter
}
return startNode(parameters, providedName = providedName, customOverrides = customOverrides)
}

View File

@ -1,88 +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.testing.node.internal.demorun
import net.corda.cordform.CordformDefinition
import net.corda.cordform.CordformNode
import net.corda.core.internal.deleteRecursively
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.getOrThrow
import net.corda.testing.driver.JmxPolicy
import net.corda.testing.driver.PortAllocation
import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages
import net.corda.testing.node.internal.internalDriver
/**
* Creates a demo runner for this cordform definition
*/
fun CordformDefinition.nodeRunner() = CordformNodeRunner(this)
/**
* A node runner creates and runs nodes for a given [[CordformDefinition]].
*/
class CordformNodeRunner(private val cordformDefinition: CordformDefinition) {
private var extraPackagesToScan = emptyList<String>()
/**
* Builder method to sets the extra cordapp scan packages
*/
fun scanPackages(packages: List<String>): CordformNodeRunner {
extraPackagesToScan = packages
return this
}
fun clean() {
System.err.println("Deleting: ${cordformDefinition.nodesDirectory}")
cordformDefinition.nodesDirectory.deleteRecursively()
}
/**
* Deploy the nodes specified in the given [CordformDefinition]. This will block until all the nodes and webservers
* have terminated.
*/
fun deployAndRunNodes() {
runNodes(waitForAllNodesToFinish = true) { }
}
/**
* 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 deployAndRunNodesAndThen(block: () -> Unit) {
runNodes(waitForAllNodesToFinish = false, block = block)
}
private fun runNodes(waitForAllNodesToFinish: Boolean, block: () -> Unit) {
clean()
val nodes = cordformDefinition.nodeConfigurers.map { configurer -> CordformNode().also { configurer.accept(it) } }
val maxPort = nodes
.flatMap { listOf(it.p2pAddress, it.rpcAddress, it.webAddress) }
.mapNotNull { address -> address?.let { NetworkHostAndPort.parse(it).port } }
.max()!!
internalDriver(
jmxPolicy = JmxPolicy(true),
driverDirectory = cordformDefinition.nodesDirectory,
// Notaries are manually specified in Cordform so we don't want the driver automatically starting any
notarySpecs = emptyList(),
// Start from after the largest port used to prevent port clash
portAllocation = PortAllocation.Incremental(maxPort + 1),
waitForAllNodesToFinish = waitForAllNodesToFinish,
cordappsForAllNodes = cordappsInCurrentAndAdditionalPackages(extraPackagesToScan)
) {
cordformDefinition.setup(this)
startCordformNodes(nodes).getOrThrow() // Only proceed once everything is up and running
println("All nodes and webservers are ready...")
block()
}
}
}

View File

@ -1,44 +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.
*/
@file:JvmName("CordformUtils")
package net.corda.testing.node.internal.demorun
import net.corda.cordform.CordformDefinition
import net.corda.cordform.CordformNode
import net.corda.cordform.RpcSettings
import net.corda.cordform.SslOptions
import net.corda.core.identity.CordaX500Name
import net.corda.node.services.config.NotaryConfig
import net.corda.nodeapi.internal.config.toConfig
import net.corda.testing.node.User
fun CordformDefinition.node(configure: CordformNode.() -> Unit) {
addNode { cordformNode -> cordformNode.configure() }
}
fun CordformNode.name(name: CordaX500Name) = name(name.toString())
fun CordformNode.rpcUsers(vararg users: User) {
rpcUsers = users.map { it.toConfig().root().unwrapped() }
}
fun CordformNode.notary(notaryConfig: NotaryConfig) {
notary = notaryConfig.toConfig().root().unwrapped()
}
fun CordformNode.rpcSettings(configure: RpcSettings.() -> Unit) {
RpcSettings().also(configure).also(this::rpcSettings)
}
fun RpcSettings.ssl(configure: SslOptions.() -> Unit) {
SslOptions().also(configure).also(this::ssl)
}

View File

@ -66,8 +66,6 @@ dependencies {
// Controls FX: more java FX components http://fxexperience.com/controlsfx/
compile "org.controlsfx:controlsfx:$controlsfx_version"
compile "net.corda.plugins:cordform-common:$gradle_plugins_version"
compile project(':client:rpc')
compile project(':finance')

View File

@ -24,6 +24,7 @@ import javafx.scene.image.WritableImage
import javafx.scene.paint.Color
import javafx.scene.text.TextAlignment
import net.corda.core.crypto.SecureHash
import net.corda.core.internal.buildNamed
/**
* (The MIT License)
@ -85,7 +86,7 @@ object IdenticonRenderer {
private const val renderingSize = 30.0
private val cache = Caffeine.newBuilder().build(CacheLoader<SecureHash, Image> { key ->
private val cache = Caffeine.newBuilder().buildNamed("IdentIconRenderer_image", CacheLoader<SecureHash, Image> { key ->
key.let { render(key.hashCode(), renderingSize) }
})

View File

@ -1,92 +1,87 @@
/*
* 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.notarytest
import net.corda.cordform.CordformContext
import net.corda.cordform.CordformDefinition
import net.corda.cordform.CordformNode
import net.corda.core.identity.CordaX500Name
import net.corda.node.services.Permissions
import net.corda.node.services.config.NotaryConfig
import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.testing.node.User
import net.corda.testing.node.internal.demorun.*
fun main(args: Array<String>) = JDBCNotaryCordform().nodeRunner().deployAndRunNodes()
internal val notaryDemoUser = User("demou", "demop", setOf(Permissions.all()))
class JDBCNotaryCordform : CordformDefinition() {
private val clusterName = CordaX500Name("Mysql Notary", "Zurich", "CH")
private val notaryNames = createNotaryNames(3)
private fun createNotaryNames(clusterSize: Int) = (0 until clusterSize).map {
CordaX500Name("Notary Service $it", "Zurich", "CH")
}
init {
fun notaryNode(index: Int, configure: CordformNode.() -> Unit) = node {
name(notaryNames[index])
notary(
NotaryConfig(
validating = true,
custom = true
)
)
extraConfig = mapOf("custom" to
mapOf(
"mysql" to mapOf(
"dataSource" to mapOf(
// Update the db address/port accordingly
"jdbcUrl" to "jdbc:mysql://localhost:330${6 + index}/corda?rewriteBatchedStatements=true&useSSL=false&failOverReadOnly=false",
"username" to "corda",
"password" to "awesome",
"autoCommit" to "false")
),
"graphiteAddress" to "performance-metrics.northeurope.cloudapp.azure.com:2004"
)
)
configure()
}
notaryNode(0) {
p2pPort(10009)
rpcSettings {
address("localhost:10010")
adminAddress("localhost:10110")
}
rpcUsers(notaryDemoUser)
}
notaryNode(1) {
p2pPort(10013)
rpcSettings {
address("localhost:10014")
adminAddress("localhost:10114")
}
rpcUsers(notaryDemoUser)
}
notaryNode(2) {
p2pPort(10017)
rpcSettings {
address("localhost:10018")
adminAddress("localhost:10118")
}
rpcUsers(notaryDemoUser)
}
}
override fun setup(context: CordformContext) {
DevIdentityGenerator.generateDistributedNotarySingularIdentity(
notaryNames.map { context.baseDirectory(it.toString()) },
clusterName
)
}
}
///*
// * 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.notarytest
//
//import net.corda.cordform.CordformContext
//import net.corda.cordform.CordformDefinition
//import net.corda.cordform.CordformNode
//import net.corda.core.identity.CordaX500Name
//import net.corda.node.services.config.NotaryConfig
//import net.corda.nodeapi.internal.DevIdentityGenerator
//
//fun main(args: Array<String>) = JDBCNotaryCordform().nodeRunner().deployAndRunNodes()
//
//class JDBCNotaryCordform : CordformDefinition() {
// private val clusterName = CordaX500Name("Mysql Notary", "Zurich", "CH")
// private val notaryNames = createNotaryNames(3)
//
// private fun createNotaryNames(clusterSize: Int) = (0 until clusterSize).map {
// CordaX500Name("Notary Service $it", "Zurich", "CH")
// }
//
// init {
// fun notaryNode(index: Int, configure: CordformNode.() -> Unit) = node {
// name(notaryNames[index])
// notary(
// NotaryConfig(
// validating = true,
// custom = true
// )
// )
// extraConfig = mapOf("custom" to
// mapOf(
// "mysql" to mapOf(
// "dataSource" to mapOf(
// // Update the db address/port accordingly
// "jdbcUrl" to "jdbc:mysql://localhost:330${6 + index}/corda?rewriteBatchedStatements=true&useSSL=false&failOverReadOnly=false",
// "username" to "corda",
// "password" to "awesome",
// "autoCommit" to "false")
// ),
// "graphiteAddress" to "performance-metrics.northeurope.cloudapp.azure.com:2004"
// )
// )
// configure()
// }
//
// notaryNode(0) {
// p2pPort(10009)
// rpcSettings {
// address("localhost:10010")
// adminAddress("localhost:10110")
// }
// rpcUsers(notaryDemoUser)
// }
// notaryNode(1) {
// p2pPort(10013)
// rpcSettings {
// address("localhost:10014")
// adminAddress("localhost:10114")
// }
// rpcUsers(notaryDemoUser)
// }
// notaryNode(2) {
// p2pPort(10017)
// rpcSettings {
// address("localhost:10018")
// adminAddress("localhost:10118")
// }
// rpcUsers(notaryDemoUser)
// }
// }
//
// override fun setup(context: CordformContext) {
// DevIdentityGenerator.generateDistributedNotarySingularIdentity(
// notaryNames.map { context.baseDirectory(it.toString()) },
// clusterName
// )
// }
//}

View File

@ -15,7 +15,9 @@ import net.corda.client.rpc.CordaRPCClient
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.services.Permissions
import net.corda.notarytest.service.JDBCLoadTestFlow
import net.corda.testing.node.User
import java.util.concurrent.TimeUnit
/** The number of test flows to run on each notary node */
@ -62,3 +64,5 @@ private fun run(rpc: CordaRPCOps, inputStateCount: Int? = null): List<Long> {
flowDuration
}
}
internal val notaryDemoUser = User("demou", "demop", setOf(Permissions.all()))