mirror of
https://github.com/corda/corda.git
synced 2025-06-13 20:58:19 +00:00
Introducing network parameters.
network-parameters file read in by the node at startup, of which only the list of notaries is used. For now, the driver and MockNetwork have been updated to require notaries to be started first. This is so that the same set of network parameters can be defined for all the nodes. CN in the legal name is not longer disallowed since it's no longer reserved for distributed notary names. Single-node notaries now only have one identity, their main identity. Nodes part of a cluster continue to have two. (Based off Kasia's work)
This commit is contained in:
@ -11,6 +11,7 @@ import net.corda.core.flows.StateMachineRunId
|
|||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.bufferUntilSubscribed
|
import net.corda.core.internal.bufferUntilSubscribed
|
||||||
|
import net.corda.core.internal.concurrent.transpose
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.messaging.StateMachineTransactionMapping
|
import net.corda.core.messaging.StateMachineTransactionMapping
|
||||||
import net.corda.core.messaging.StateMachineUpdate
|
import net.corda.core.messaging.StateMachineUpdate
|
||||||
@ -35,20 +36,20 @@ import org.junit.Test
|
|||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
|
||||||
class NodeMonitorModelTest {
|
class NodeMonitorModelTest {
|
||||||
lateinit var aliceNode: NodeInfo
|
private lateinit var aliceNode: NodeInfo
|
||||||
lateinit var bobNode: NodeInfo
|
private lateinit var bobNode: NodeInfo
|
||||||
lateinit var notaryParty: Party
|
private lateinit var notaryParty: Party
|
||||||
|
|
||||||
lateinit var rpc: CordaRPCOps
|
private lateinit var rpc: CordaRPCOps
|
||||||
lateinit var rpcBob: CordaRPCOps
|
private lateinit var rpcBob: CordaRPCOps
|
||||||
lateinit var stateMachineTransactionMapping: Observable<StateMachineTransactionMapping>
|
private lateinit var stateMachineTransactionMapping: Observable<StateMachineTransactionMapping>
|
||||||
lateinit var stateMachineUpdates: Observable<StateMachineUpdate>
|
private lateinit var stateMachineUpdates: Observable<StateMachineUpdate>
|
||||||
lateinit var stateMachineUpdatesBob: Observable<StateMachineUpdate>
|
private lateinit var stateMachineUpdatesBob: Observable<StateMachineUpdate>
|
||||||
lateinit var progressTracking: Observable<ProgressTrackingEvent>
|
private lateinit var progressTracking: Observable<ProgressTrackingEvent>
|
||||||
lateinit var transactions: Observable<SignedTransaction>
|
private lateinit var transactions: Observable<SignedTransaction>
|
||||||
lateinit var vaultUpdates: Observable<Vault.Update<ContractState>>
|
private lateinit var vaultUpdates: Observable<Vault.Update<ContractState>>
|
||||||
lateinit var networkMapUpdates: Observable<NetworkMapCache.MapChange>
|
private lateinit var networkMapUpdates: Observable<NetworkMapCache.MapChange>
|
||||||
lateinit var newNode: (CordaX500Name) -> NodeInfo
|
private lateinit var newNode: (CordaX500Name) -> NodeInfo
|
||||||
private fun setup(runTest: () -> Unit) = driver(extraCordappPackagesToScan = listOf("net.corda.finance")) {
|
private fun setup(runTest: () -> Unit) = driver(extraCordappPackagesToScan = listOf("net.corda.finance")) {
|
||||||
val cashUser = User("user1", "test", permissions = setOf(
|
val cashUser = User("user1", "test", permissions = setOf(
|
||||||
startFlow<CashIssueFlow>(),
|
startFlow<CashIssueFlow>(),
|
||||||
@ -62,9 +63,10 @@ class NodeMonitorModelTest {
|
|||||||
invokeRpc(CordaRPCOps::stateMachinesFeed),
|
invokeRpc(CordaRPCOps::stateMachinesFeed),
|
||||||
invokeRpc(CordaRPCOps::networkMapFeed))
|
invokeRpc(CordaRPCOps::networkMapFeed))
|
||||||
)
|
)
|
||||||
val aliceNodeFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser))
|
val (notaryHandle, aliceNodeHandle) = listOf(
|
||||||
val notaryHandle = startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow()
|
startNotaryNode(DUMMY_NOTARY.name, validating = false),
|
||||||
val aliceNodeHandle = aliceNodeFuture.getOrThrow()
|
startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser))
|
||||||
|
).transpose().getOrThrow()
|
||||||
aliceNode = aliceNodeHandle.nodeInfo
|
aliceNode = aliceNodeHandle.nodeInfo
|
||||||
newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo }
|
newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo }
|
||||||
val monitor = NodeMonitorModel()
|
val monitor = NodeMonitorModel()
|
||||||
@ -77,7 +79,7 @@ class NodeMonitorModelTest {
|
|||||||
|
|
||||||
monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
|
monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
|
||||||
rpc = monitor.proxyObservable.value!!
|
rpc = monitor.proxyObservable.value!!
|
||||||
notaryParty = notaryHandle.nodeInfo.legalIdentities[1]
|
notaryParty = notaryHandle.nodeInfo.legalIdentities[0]
|
||||||
|
|
||||||
val bobNodeHandle = startNode(providedName = BOB.name, rpcUsers = listOf(cashUser)).getOrThrow()
|
val bobNodeHandle = startNode(providedName = BOB.name, rpcUsers = listOf(cashUser)).getOrThrow()
|
||||||
bobNode = bobNodeHandle.nodeInfo
|
bobNode = bobNodeHandle.nodeInfo
|
||||||
|
@ -5,6 +5,7 @@ import com.google.common.cache.CacheLoader
|
|||||||
import javafx.beans.value.ObservableValue
|
import javafx.beans.value.ObservableValue
|
||||||
import javafx.collections.FXCollections
|
import javafx.collections.FXCollections
|
||||||
import javafx.collections.ObservableList
|
import javafx.collections.ObservableList
|
||||||
|
import net.corda.client.jfx.utils.ChosenList
|
||||||
import net.corda.client.jfx.utils.filterNotNull
|
import net.corda.client.jfx.utils.filterNotNull
|
||||||
import net.corda.client.jfx.utils.fold
|
import net.corda.client.jfx.utils.fold
|
||||||
import net.corda.client.jfx.utils.map
|
import net.corda.client.jfx.utils.map
|
||||||
@ -12,7 +13,6 @@ import net.corda.core.identity.AnonymousParty
|
|||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.services.NetworkMapCache.MapChange
|
import net.corda.core.node.services.NetworkMapCache.MapChange
|
||||||
import net.corda.nodeapi.internal.ServiceType
|
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
class NetworkIdentityModel {
|
class NetworkIdentityModel {
|
||||||
@ -36,16 +36,10 @@ class NetworkIdentityModel {
|
|||||||
.build<PublicKey, ObservableValue<NodeInfo?>>(CacheLoader.from { publicKey ->
|
.build<PublicKey, ObservableValue<NodeInfo?>>(CacheLoader.from { publicKey ->
|
||||||
publicKey?.let { rpcProxy.map { it?.nodeInfoFromParty(AnonymousParty(publicKey)) } }
|
publicKey?.let { rpcProxy.map { it?.nodeInfoFromParty(AnonymousParty(publicKey)) } }
|
||||||
})
|
})
|
||||||
|
val notaries = ChosenList(rpcProxy.map { FXCollections.observableList(it?.notaryIdentities() ?: emptyList()) })
|
||||||
val notaries: ObservableList<Party> = networkIdentities.map {
|
|
||||||
it.legalIdentitiesAndCerts.find { it.name.commonName?.let { ServiceType.parse(it).isNotary() } == true }
|
|
||||||
}.map { it?.party }.filterNotNull()
|
|
||||||
|
|
||||||
val notaryNodes: ObservableList<NodeInfo> = notaries.map { rpcProxy.value?.nodeInfoFromParty(it) }.filterNotNull()
|
val notaryNodes: ObservableList<NodeInfo> = notaries.map { rpcProxy.value?.nodeInfoFromParty(it) }.filterNotNull()
|
||||||
val parties: ObservableList<NodeInfo> = networkIdentities
|
val parties: ObservableList<NodeInfo> = networkIdentities
|
||||||
.filtered { it.legalIdentities.all { it !in notaries } }
|
.filtered { it.legalIdentities.all { it !in notaries } }
|
||||||
// TODO: REMOVE THIS HACK WHEN NETWORK MAP REDESIGN WORK IS COMPLETED.
|
|
||||||
.filtered { it.legalIdentities.all { it.name.organisation != "Network Map Service" } }
|
|
||||||
val myIdentity = rpcProxy.map { it?.nodeInfo()?.legalIdentitiesAndCerts?.first()?.party }
|
val myIdentity = rpcProxy.map { it?.nodeInfo()?.legalIdentitiesAndCerts?.first()?.party }
|
||||||
|
|
||||||
fun partyFromPublicKey(publicKey: PublicKey): ObservableValue<NodeInfo?> = identityCache[publicKey]
|
fun partyFromPublicKey(publicKey: PublicKey): ObservableValue<NodeInfo?> = identityCache[publicKey]
|
||||||
|
@ -13,7 +13,7 @@ interface CordaFuture<V> : Future<V> {
|
|||||||
* If the completion thread is problematic for you e.g. deadlock, you can submit to an executor manually.
|
* If the completion thread is problematic for you e.g. deadlock, you can submit to an executor manually.
|
||||||
* If callback fails, its throwable is logged.
|
* If callback fails, its throwable is logged.
|
||||||
*/
|
*/
|
||||||
fun <W> then(callback: (CordaFuture<V>) -> W): Unit
|
fun <W> then(callback: (CordaFuture<V>) -> W)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a new [CompletableFuture] with the same outcome as this Future.
|
* @return a new [CompletableFuture] with the same outcome as this Future.
|
||||||
|
@ -33,7 +33,9 @@ fun PrivateKey.sign(bytesToSign: ByteArray): DigitalSignature = DigitalSignature
|
|||||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||||
*/
|
*/
|
||||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||||
fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey) = DigitalSignature.WithKey(publicKey, this.sign(bytesToSign).bytes)
|
fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
|
||||||
|
return DigitalSignature.WithKey(publicKey, this.sign(bytesToSign).bytes)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to sign with a key pair.
|
* Helper function to sign with a key pair.
|
||||||
@ -44,11 +46,11 @@ fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey) = DigitalSigna
|
|||||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||||
*/
|
*/
|
||||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||||
fun KeyPair.sign(bytesToSign: ByteArray) = private.sign(bytesToSign, public)
|
fun KeyPair.sign(bytesToSign: ByteArray): DigitalSignature.WithKey = private.sign(bytesToSign, public)
|
||||||
|
|
||||||
/** Helper function to sign the bytes of [bytesToSign] with a key pair. */
|
/** Helper function to sign the bytes of [bytesToSign] with a key pair. */
|
||||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||||
fun KeyPair.sign(bytesToSign: OpaqueBytes) = sign(bytesToSign.bytes)
|
fun KeyPair.sign(bytesToSign: OpaqueBytes): DigitalSignature.WithKey = sign(bytesToSign.bytes)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function for signing a [SignableData] object.
|
* Helper function for signing a [SignableData] object.
|
||||||
|
@ -49,6 +49,7 @@ class NotaryFlow {
|
|||||||
progressTracker.currentStep = REQUESTING
|
progressTracker.currentStep = REQUESTING
|
||||||
|
|
||||||
val notaryParty = stx.notary ?: throw IllegalStateException("Transaction does not specify a Notary")
|
val notaryParty = stx.notary ?: throw IllegalStateException("Transaction does not specify a Notary")
|
||||||
|
check(serviceHub.networkMapCache.isNotary(notaryParty)) { "$notaryParty is not a notary on the network" }
|
||||||
check(stx.inputs.all { stateRef -> serviceHub.loadState(stateRef).notary == notaryParty }) {
|
check(stx.inputs.all { stateRef -> serviceHub.loadState(stateRef).notary == notaryParty }) {
|
||||||
"Input states must have the same Notary"
|
"Input states must have the same Notary"
|
||||||
}
|
}
|
||||||
@ -115,6 +116,9 @@ class NotaryFlow {
|
|||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): Void? {
|
override fun call(): Void? {
|
||||||
|
check(serviceHub.myInfo.legalIdentities.any { serviceHub.networkMapCache.isNotary(it) }) {
|
||||||
|
"We are not a notary on the network"
|
||||||
|
}
|
||||||
val (id, inputs, timeWindow, notary) = receiveAndVerifyTx()
|
val (id, inputs, timeWindow, notary) = receiveAndVerifyTx()
|
||||||
checkNotary(notary)
|
checkNotary(notary)
|
||||||
service.validateTimeWindow(timeWindow)
|
service.validateTimeWindow(timeWindow)
|
||||||
@ -135,7 +139,7 @@ class NotaryFlow {
|
|||||||
protected fun checkNotary(notary: Party?) {
|
protected fun checkNotary(notary: Party?) {
|
||||||
// TODO This check implies that it's OK to use the node's main identity. Shouldn't it be just limited to the
|
// TODO This check implies that it's OK to use the node's main identity. Shouldn't it be just limited to the
|
||||||
// notary identities?
|
// notary identities?
|
||||||
if (notary !in serviceHub.myInfo.legalIdentities) {
|
if (notary == null || !serviceHub.myInfo.isLegalIdentity(notary)) {
|
||||||
throw NotaryException(NotaryError.WrongNotary)
|
throw NotaryException(NotaryError.WrongNotary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
package net.corda.core.node
|
||||||
|
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.serialization.CordaSerializable
|
||||||
|
import java.time.Duration
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property minimumPlatformVersion
|
||||||
|
* @property notaries
|
||||||
|
* @property eventHorizon
|
||||||
|
* @property maxMessageSize Maximum P2P message sent over the wire in bytes.
|
||||||
|
* @property maxTransactionSize Maximum permitted transaction size in bytes.
|
||||||
|
* @property modifiedTime
|
||||||
|
* @property epoch Version number of the network parameters. Starting from 1, this will always increment on each new set
|
||||||
|
* of parameters.
|
||||||
|
*/
|
||||||
|
// TODO Wire up the parameters
|
||||||
|
@CordaSerializable
|
||||||
|
data class NetworkParameters(
|
||||||
|
val minimumPlatformVersion: Int,
|
||||||
|
val notaries: List<NotaryInfo>,
|
||||||
|
val eventHorizon: Duration,
|
||||||
|
val maxMessageSize: Int,
|
||||||
|
val maxTransactionSize: Int,
|
||||||
|
val modifiedTime: Instant,
|
||||||
|
val epoch: Int
|
||||||
|
) {
|
||||||
|
init {
|
||||||
|
require(minimumPlatformVersion > 0) { "minimumPlatformVersion must be at least 1" }
|
||||||
|
require(notaries.distinctBy { it.identity } == notaries) { "Duplicate notary identities" }
|
||||||
|
require(epoch > 0) { "epoch must be at least 1" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@CordaSerializable
|
||||||
|
data class NotaryInfo(val identity: Party, val validating: Boolean)
|
@ -52,7 +52,6 @@ interface NetworkMapCacheBase {
|
|||||||
*
|
*
|
||||||
* Note that the identities are sorted based on legal name, and the ordering might change once new notaries are introduced.
|
* Note that the identities are sorted based on legal name, and the ordering might change once new notaries are introduced.
|
||||||
*/
|
*/
|
||||||
// TODO this list will be taken from NetworkParameters distributed by NetworkMap.
|
|
||||||
val notaryIdentities: List<Party>
|
val notaryIdentities: List<Party>
|
||||||
// DOCEND 1
|
// DOCEND 1
|
||||||
|
|
||||||
@ -116,12 +115,15 @@ interface NetworkMapCacheBase {
|
|||||||
fun getNotary(name: CordaX500Name): Party? = notaryIdentities.firstOrNull { it.name == name }
|
fun getNotary(name: CordaX500Name): Party? = notaryIdentities.firstOrNull { it.name == name }
|
||||||
// DOCEND 2
|
// DOCEND 2
|
||||||
|
|
||||||
/** Checks whether a given party is an advertised notary identity. */
|
/** Returns true if and only if the given [Party] is a notary, which is defined by the network parameters. */
|
||||||
fun isNotary(party: Party): Boolean = party in notaryIdentities
|
fun isNotary(party: Party): Boolean = party in notaryIdentities
|
||||||
|
|
||||||
/** Checks whether a given party is an validating notary identity. */
|
/**
|
||||||
// TODO This implementation will change after introducing of NetworkParameters.
|
* Returns true if and only if the given [Party] is validating notary. For every party that is a validating notary,
|
||||||
fun isValidatingNotary(party: Party): Boolean = isNotary(party) && "validating" in party.name.commonName!!
|
* [isNotary] is only true.
|
||||||
|
* @see isNotary
|
||||||
|
*/
|
||||||
|
fun isValidatingNotary(party: Party): Boolean
|
||||||
|
|
||||||
/** Clear all network map data from local node cache. */
|
/** Clear all network map data from local node cache. */
|
||||||
fun clearNetworkMapCache()
|
fun clearNetworkMapCache()
|
||||||
|
@ -118,10 +118,6 @@ path to the node's base directory.
|
|||||||
|
|
||||||
Only one of ``raft``, ``bftSMaRt`` or ``custom`` configuration values may be specified.
|
Only one of ``raft``, ``bftSMaRt`` or ``custom`` configuration values may be specified.
|
||||||
|
|
||||||
:minimumPlatformVersion: Used by the node if it's running the network map service to enforce a minimum version requirement
|
|
||||||
on registrations - any node on a Platform Version lower than this value will have their registration rejected.
|
|
||||||
Defaults to 1 if absent.
|
|
||||||
|
|
||||||
:useHTTPS: If false the node's web server will be plain HTTP. If true the node will use the same certificate and private
|
:useHTTPS: If false the node's web server will be plain HTTP. If true the node will use the same certificate and private
|
||||||
key from the ``<workspace>/certificates/sslkeystore.jks`` file as the ArtemisMQ port for HTTPS. If HTTPS is enabled
|
key from the ``<workspace>/certificates/sslkeystore.jks`` file as the ArtemisMQ port for HTTPS. If HTTPS is enabled
|
||||||
then unencrypted HTTP traffic to the node's **webAddress** port is not supported.
|
then unencrypted HTTP traffic to the node's **webAddress** port is not supported.
|
||||||
|
@ -39,10 +39,10 @@ class IntegrationTestingTutorial {
|
|||||||
invokeRpc("vaultTrackBy"),
|
invokeRpc("vaultTrackBy"),
|
||||||
invokeRpc(CordaRPCOps::networkMapFeed)
|
invokeRpc(CordaRPCOps::networkMapFeed)
|
||||||
))
|
))
|
||||||
val (alice, bob) = listOf(
|
val (_, alice, bob) = listOf(
|
||||||
|
startNotaryNode(DUMMY_NOTARY.name),
|
||||||
startNode(providedName = ALICE.name, rpcUsers = listOf(aliceUser)),
|
startNode(providedName = ALICE.name, rpcUsers = listOf(aliceUser)),
|
||||||
startNode(providedName = BOB.name, rpcUsers = listOf(bobUser)),
|
startNode(providedName = BOB.name, rpcUsers = listOf(bobUser))
|
||||||
startNotaryNode(DUMMY_NOTARY.name)
|
|
||||||
).transpose().getOrThrow()
|
).transpose().getOrThrow()
|
||||||
// END 1
|
// END 1
|
||||||
|
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
package net.corda.nodeapi.internal
|
|
||||||
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A container for additional information for an advertised service.
|
|
||||||
*
|
|
||||||
* @param type the ServiceType identifier
|
|
||||||
* @param name the service name, used for differentiating multiple services of the same type. Can also be used as a
|
|
||||||
* grouping identifier for nodes collectively running a distributed service.
|
|
||||||
*/
|
|
||||||
@CordaSerializable
|
|
||||||
data class ServiceInfo(val type: ServiceType, val name: CordaX500Name? = null) {
|
|
||||||
companion object {
|
|
||||||
fun parse(encoded: String): ServiceInfo {
|
|
||||||
val parts = encoded.split("|")
|
|
||||||
require(parts.size in 1..2) { "Invalid number of elements found" }
|
|
||||||
val type = ServiceType.parse(parts[0])
|
|
||||||
val name = parts.getOrNull(1)
|
|
||||||
val principal = name?.let { CordaX500Name.parse(it) }
|
|
||||||
return ServiceInfo(type, principal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString() = if (name != null) "$type|$name" else type.toString()
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package net.corda.nodeapi.internal
|
|
||||||
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Identifier for service types a node can expose over the network to other peers. These types are placed into network
|
|
||||||
* map advertisements. Services that are purely local and are not providing functionality to other parts of the network
|
|
||||||
* don't need a declared service type.
|
|
||||||
*/
|
|
||||||
@CordaSerializable
|
|
||||||
class ServiceType private constructor(val id: String) {
|
|
||||||
init {
|
|
||||||
// Enforce:
|
|
||||||
//
|
|
||||||
// * IDs must start with a lower case letter
|
|
||||||
// * IDs can only contain alphanumeric, full stop and underscore ASCII characters
|
|
||||||
require(id.matches(Regex("[a-z][a-zA-Z0-9._]+"))) { id }
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val corda: ServiceType
|
|
||||||
get() {
|
|
||||||
val stack = Throwable().stackTrace
|
|
||||||
val caller = stack.first().className
|
|
||||||
require(caller.startsWith("net.corda.")) { "Corda ServiceType namespace is reserved for Corda core components" }
|
|
||||||
return ServiceType("corda")
|
|
||||||
}
|
|
||||||
|
|
||||||
val notary: ServiceType = corda.getSubType("notary")
|
|
||||||
|
|
||||||
fun parse(id: String): ServiceType = ServiceType(id)
|
|
||||||
|
|
||||||
private fun baseWithSubType(baseId: String, subTypeId: String) = ServiceType("$baseId.$subTypeId")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSubType(subTypeId: String): ServiceType = baseWithSubType(id, subTypeId)
|
|
||||||
|
|
||||||
fun isSubTypeOf(superType: ServiceType) = (id == superType.id) || id.startsWith(superType.id + ".")
|
|
||||||
fun isNotary() = isSubTypeOf(notary)
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean = other === this || other is ServiceType && other.id == this.id
|
|
||||||
override fun hashCode(): Int = id.hashCode()
|
|
||||||
override fun toString(): String = id
|
|
||||||
}
|
|
@ -9,20 +9,15 @@ import net.corda.core.utilities.getOrThrow
|
|||||||
import net.corda.node.internal.NodeStartup
|
import net.corda.node.internal.NodeStartup
|
||||||
import net.corda.node.services.Permissions.Companion.startFlow
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.nodeapi.internal.ServiceInfo
|
|
||||||
import net.corda.nodeapi.internal.ServiceType
|
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.ProjectStructure.projectRootDir
|
import net.corda.testing.ProjectStructure.projectRootDir
|
||||||
import net.corda.testing.driver.ListenProcessDeathException
|
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.Ignore
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
|
||||||
|
|
||||||
class BootTests {
|
class BootTests {
|
||||||
|
|
||||||
@ -53,16 +48,6 @@ class BootTests {
|
|||||||
assertEquals(1, numberOfNodesThatLogged)
|
assertEquals(1, numberOfNodesThatLogged)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("Need rewriting to produce too big network map registration (adverticed services trick doesn't work after services removal).")
|
|
||||||
@Test
|
|
||||||
fun `node quits on failure to register with network map`() {
|
|
||||||
val tooManyAdvertisedServices = (1..100).map { ServiceInfo(ServiceType.notary.getSubType("$it")) }.toSet()
|
|
||||||
driver {
|
|
||||||
val future = startNode(providedName = ALICE.name)
|
|
||||||
assertFailsWith(ListenProcessDeathException::class) { future.getOrThrow() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
|
@ -20,10 +20,13 @@ import net.corda.core.utilities.seconds
|
|||||||
import net.corda.node.internal.cordapp.CordappLoader
|
import net.corda.node.internal.cordapp.CordappLoader
|
||||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.*
|
import net.corda.testing.DUMMY_BANK_A
|
||||||
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.driver.DriverDSLExposedInterface
|
import net.corda.testing.driver.DriverDSLExposedInterface
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.eventually
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -54,16 +57,16 @@ class AttachmentLoadingTests {
|
|||||||
val bankAName = CordaX500Name("BankA", "Zurich", "CH")
|
val bankAName = CordaX500Name("BankA", "Zurich", "CH")
|
||||||
val bankBName = CordaX500Name("BankB", "Zurich", "CH")
|
val bankBName = CordaX500Name("BankB", "Zurich", "CH")
|
||||||
val notaryName = CordaX500Name("Notary", "Zurich", "CH")
|
val notaryName = CordaX500Name("Notary", "Zurich", "CH")
|
||||||
val flowInitiatorClass =
|
val flowInitiatorClass: Class<out FlowLogic<*>> =
|
||||||
Class.forName("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator", true, URLClassLoader(arrayOf(isolatedJAR)))
|
Class.forName("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator", true, URLClassLoader(arrayOf(isolatedJAR)))
|
||||||
.asSubclass(FlowLogic::class.java)
|
.asSubclass(FlowLogic::class.java)
|
||||||
|
|
||||||
private fun DriverDSLExposedInterface.createTwoNodesAndNotary(): List<NodeHandle> {
|
private fun DriverDSLExposedInterface.createNotaryAndTwoNodes(): List<NodeHandle> {
|
||||||
val adminUser = User("admin", "admin", permissions = setOf("ALL"))
|
val adminUser = User("admin", "admin", permissions = setOf("ALL"))
|
||||||
val nodes = listOf(
|
val nodes = listOf(
|
||||||
|
startNotaryNode(providedName = notaryName, rpcUsers = listOf(adminUser), validating = false),
|
||||||
startNode(providedName = bankAName, rpcUsers = listOf(adminUser)),
|
startNode(providedName = bankAName, rpcUsers = listOf(adminUser)),
|
||||||
startNode(providedName = bankBName, rpcUsers = listOf(adminUser)),
|
startNode(providedName = bankBName, rpcUsers = listOf(adminUser))
|
||||||
startNotaryNode(providedName = notaryName, rpcUsers = listOf(adminUser), validating = false)
|
|
||||||
).transpose().getOrThrow() // Wait for all nodes to start up.
|
).transpose().getOrThrow() // Wait for all nodes to start up.
|
||||||
nodes.forEach { it.rpc.waitUntilNetworkReady().getOrThrow() }
|
nodes.forEach { it.rpc.waitUntilNetworkReady().getOrThrow() }
|
||||||
return nodes
|
return nodes
|
||||||
@ -119,7 +122,7 @@ class AttachmentLoadingTests {
|
|||||||
fun `test that attachments retrieved over the network are not used for code`() {
|
fun `test that attachments retrieved over the network are not used for code`() {
|
||||||
driver(initialiseSerialization = false) {
|
driver(initialiseSerialization = false) {
|
||||||
installIsolatedCordappTo(bankAName)
|
installIsolatedCordappTo(bankAName)
|
||||||
val (bankA, bankB, _) = createTwoNodesAndNotary()
|
val (_, bankA, bankB) = createNotaryAndTwoNodes()
|
||||||
eventuallyPassingTest {
|
eventuallyPassingTest {
|
||||||
assertFailsWith<UnexpectedFlowEndException>("Party C=CH,L=Zurich,O=BankB rejected session request: Don't know net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator") {
|
assertFailsWith<UnexpectedFlowEndException>("Party C=CH,L=Zurich,O=BankB rejected session request: Don't know net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator") {
|
||||||
bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
|
bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
|
||||||
@ -133,7 +136,7 @@ class AttachmentLoadingTests {
|
|||||||
driver(initialiseSerialization = false) {
|
driver(initialiseSerialization = false) {
|
||||||
installIsolatedCordappTo(bankAName)
|
installIsolatedCordappTo(bankAName)
|
||||||
installIsolatedCordappTo(bankBName)
|
installIsolatedCordappTo(bankBName)
|
||||||
val (bankA, bankB, _) = createTwoNodesAndNotary()
|
val (_, bankA, bankB) = createNotaryAndTwoNodes()
|
||||||
eventuallyPassingTest {
|
eventuallyPassingTest {
|
||||||
bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
|
bankA.rpc.startFlowDynamic(flowInitiatorClass, bankB.nodeInfo.legalIdentities.first()).returnValue.getOrThrow()
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.deleteIfExists
|
import net.corda.core.internal.deleteIfExists
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.core.node.NotaryInfo
|
||||||
|
import net.corda.core.node.services.NotaryService
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
@ -21,15 +23,16 @@ import net.corda.core.utilities.getOrThrow
|
|||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.node.services.config.BFTSMaRtConfiguration
|
import net.corda.node.services.config.BFTSMaRtConfiguration
|
||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
import net.corda.node.services.transactions.BFTNonValidatingNotaryService
|
|
||||||
import net.corda.node.services.transactions.minClusterSize
|
import net.corda.node.services.transactions.minClusterSize
|
||||||
import net.corda.node.services.transactions.minCorrectReplicas
|
import net.corda.node.services.transactions.minCorrectReplicas
|
||||||
import net.corda.node.utilities.ServiceIdentityGenerator
|
import net.corda.node.utilities.ServiceIdentityGenerator
|
||||||
import net.corda.testing.chooseIdentity
|
import net.corda.testing.chooseIdentity
|
||||||
|
import net.corda.testing.common.internal.NetworkParametersCopier
|
||||||
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.dummyCommand
|
import net.corda.testing.dummyCommand
|
||||||
import net.corda.testing.getDefaultNotary
|
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
|
import net.corda.testing.node.MockNetwork.MockNode
|
||||||
import net.corda.testing.node.MockNodeParameters
|
import net.corda.testing.node.MockNodeParameters
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -38,39 +41,47 @@ import kotlin.test.assertEquals
|
|||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class BFTNotaryServiceTests {
|
class BFTNotaryServiceTests {
|
||||||
companion object {
|
|
||||||
private val clusterName = CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH")
|
|
||||||
}
|
|
||||||
|
|
||||||
private val mockNet = MockNetwork()
|
private val mockNet = MockNetwork()
|
||||||
private val node = mockNet.createNode()
|
private lateinit var notary: Party
|
||||||
|
private lateinit var node: StartedNode<MockNode>
|
||||||
|
|
||||||
@After
|
@After
|
||||||
fun stopNodes() {
|
fun stopNodes() {
|
||||||
mockNet.stopNodes()
|
mockNet.stopNodes()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bftNotaryCluster(clusterSize: Int, exposeRaces: Boolean = false) {
|
private fun startBftClusterAndNode(clusterSize: Int, exposeRaces: Boolean = false) {
|
||||||
(Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists?
|
(Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists?
|
||||||
val replicaIds = (0 until clusterSize)
|
val replicaIds = (0 until clusterSize)
|
||||||
ServiceIdentityGenerator.generateToDisk(
|
|
||||||
|
notary = ServiceIdentityGenerator.generateToDisk(
|
||||||
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
|
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
|
||||||
clusterName)
|
CordaX500Name("BFT", "Zurich", "CH"),
|
||||||
|
NotaryService.constructId(validating = false, bft = true))
|
||||||
|
|
||||||
|
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notary, false))))
|
||||||
|
|
||||||
val clusterAddresses = replicaIds.map { NetworkHostAndPort("localhost", 11000 + it * 10) }
|
val clusterAddresses = replicaIds.map { NetworkHostAndPort("localhost", 11000 + it * 10) }
|
||||||
replicaIds.forEach { replicaId ->
|
|
||||||
mockNet.createNode(MockNodeParameters(configOverrides = {
|
val nodes = replicaIds.map { replicaId ->
|
||||||
|
mockNet.createUnstartedNode(MockNodeParameters(configOverrides = {
|
||||||
val notary = NotaryConfig(validating = false, bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses, exposeRaces = exposeRaces))
|
val notary = NotaryConfig(validating = false, bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses, exposeRaces = exposeRaces))
|
||||||
doReturn(notary).whenever(it).notary
|
doReturn(notary).whenever(it).notary
|
||||||
}))
|
}))
|
||||||
}
|
} + mockNet.createUnstartedNode()
|
||||||
mockNet.runNetwork() // Exchange initial network map registration messages.
|
|
||||||
|
// MockNetwork doesn't support BFT clusters, so we create all the nodes we need unstarted, and then install the
|
||||||
|
// network-parameters in their directories before they're started.
|
||||||
|
node = nodes.map { node ->
|
||||||
|
networkParameters.install(mockNet.baseDirectory(node.id))
|
||||||
|
node.start()
|
||||||
|
}.last()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Failure mode is the redundant replica gets stuck in startup, so we can't dispose it cleanly at the end. */
|
/** Failure mode is the redundant replica gets stuck in startup, so we can't dispose it cleanly at the end. */
|
||||||
@Test
|
@Test
|
||||||
fun `all replicas start even if there is a new consensus during startup`() {
|
fun `all replicas start even if there is a new consensus during startup`() {
|
||||||
bftNotaryCluster(minClusterSize(1), true) // This true adds a sleep to expose the race.
|
startBftClusterAndNode(minClusterSize(1), exposeRaces = true) // This true adds a sleep to expose the race.
|
||||||
val notary = node.services.getDefaultNotary()
|
|
||||||
val f = node.run {
|
val f = node.run {
|
||||||
val trivialTx = signInitialTransaction(notary) {
|
val trivialTx = signInitialTransaction(notary) {
|
||||||
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
|
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
|
||||||
@ -94,8 +105,7 @@ class BFTNotaryServiceTests {
|
|||||||
|
|
||||||
private fun detectDoubleSpend(faultyReplicas: Int) {
|
private fun detectDoubleSpend(faultyReplicas: Int) {
|
||||||
val clusterSize = minClusterSize(faultyReplicas)
|
val clusterSize = minClusterSize(faultyReplicas)
|
||||||
bftNotaryCluster(clusterSize)
|
startBftClusterAndNode(clusterSize)
|
||||||
val notary = node.services.getDefaultNotary()
|
|
||||||
node.run {
|
node.run {
|
||||||
val issueTx = signInitialTransaction(notary) {
|
val issueTx = signInitialTransaction(notary) {
|
||||||
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
|
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
|
||||||
@ -138,15 +148,13 @@ class BFTNotaryServiceTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun StartedNode<*>.signInitialTransaction(
|
private fun StartedNode<*>.signInitialTransaction(notary: Party, block: TransactionBuilder.() -> Any?): SignedTransaction {
|
||||||
notary: Party,
|
return services.signInitialTransaction(
|
||||||
block: TransactionBuilder.() -> Any?
|
TransactionBuilder(notary).apply {
|
||||||
): SignedTransaction {
|
addCommand(dummyCommand(services.myInfo.chooseIdentity().owningKey))
|
||||||
return services.signInitialTransaction(
|
block()
|
||||||
TransactionBuilder(notary).apply {
|
}
|
||||||
addCommand(dummyCommand(services.myInfo.chooseIdentity().owningKey))
|
)
|
||||||
block()
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -38,12 +38,12 @@ class DistributedServiceTests {
|
|||||||
invokeRpc(CordaRPCOps::nodeInfo),
|
invokeRpc(CordaRPCOps::nodeInfo),
|
||||||
invokeRpc(CordaRPCOps::stateMachinesFeed))
|
invokeRpc(CordaRPCOps::stateMachinesFeed))
|
||||||
)
|
)
|
||||||
val aliceFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser))
|
|
||||||
val notariesFuture = startNotaryCluster(
|
val notariesFuture = startNotaryCluster(
|
||||||
DUMMY_NOTARY.name.copy(commonName = RaftValidatingNotaryService.id),
|
DUMMY_NOTARY.name.copy(commonName = RaftValidatingNotaryService.id),
|
||||||
rpcUsers = listOf(testUser),
|
rpcUsers = listOf(testUser),
|
||||||
clusterSize = clusterSize
|
clusterSize = clusterSize
|
||||||
)
|
)
|
||||||
|
val aliceFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser))
|
||||||
|
|
||||||
alice = aliceFuture.get()
|
alice = aliceFuture.get()
|
||||||
val (notaryIdentity, notaryNodes) = notariesFuture.get()
|
val (notaryIdentity, notaryNodes) = notariesFuture.get()
|
||||||
|
@ -6,15 +6,11 @@ import net.corda.cordform.CordformNode
|
|||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.services.KeyManagementService
|
|
||||||
import net.corda.node.services.identity.InMemoryIdentityService
|
|
||||||
import net.corda.nodeapi.NodeInfoFilesCopier
|
import net.corda.nodeapi.NodeInfoFilesCopier
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.ALICE_KEY
|
import net.corda.testing.ALICE_KEY
|
||||||
import net.corda.testing.DEV_TRUST_ROOT
|
|
||||||
import net.corda.testing.getTestPartyAndCertificate
|
import net.corda.testing.getTestPartyAndCertificate
|
||||||
import net.corda.testing.internal.NodeBasedTest
|
import net.corda.testing.internal.NodeBasedTest
|
||||||
import net.corda.testing.node.MockKeyManagementService
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.contentOf
|
import org.assertj.core.api.Assertions.contentOf
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -31,7 +27,6 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
|||||||
val nodeInfo = NodeInfo(listOf(), listOf(getTestPartyAndCertificate(ALICE)), 0, 0)
|
val nodeInfo = NodeInfo(listOf(), listOf(getTestPartyAndCertificate(ALICE)), 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var keyManagementService: KeyManagementService
|
|
||||||
private lateinit var nodeInfoPath: Path
|
private lateinit var nodeInfoPath: Path
|
||||||
private val scheduler = TestScheduler()
|
private val scheduler = TestScheduler()
|
||||||
private val testSubscriber = TestSubscriber<NodeInfo>()
|
private val testSubscriber = TestSubscriber<NodeInfo>()
|
||||||
@ -41,8 +36,6 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun start() {
|
fun start() {
|
||||||
val identityService = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT)
|
|
||||||
keyManagementService = MockKeyManagementService(identityService, ALICE_KEY)
|
|
||||||
nodeInfoWatcher = NodeInfoWatcher(tempFolder.root.toPath(), scheduler = scheduler)
|
nodeInfoWatcher = NodeInfoWatcher(tempFolder.root.toPath(), scheduler = scheduler)
|
||||||
nodeInfoPath = tempFolder.root.toPath() / CordformNode.NODE_INFO_DIRECTORY
|
nodeInfoPath = tempFolder.root.toPath() / CordformNode.NODE_INFO_DIRECTORY
|
||||||
}
|
}
|
||||||
@ -51,7 +44,7 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
|||||||
fun `save a NodeInfo`() {
|
fun `save a NodeInfo`() {
|
||||||
assertEquals(0,
|
assertEquals(0,
|
||||||
tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.size)
|
tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.size)
|
||||||
NodeInfoWatcher.saveToFile(tempFolder.root.toPath(), nodeInfo, keyManagementService)
|
NodeInfoWatcher.saveToFile(tempFolder.root.toPath(), nodeInfo, ALICE_KEY)
|
||||||
|
|
||||||
val nodeInfoFiles = tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }
|
val nodeInfoFiles = tempFolder.root.list().filter { it.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }
|
||||||
assertEquals(1, nodeInfoFiles.size)
|
assertEquals(1, nodeInfoFiles.size)
|
||||||
@ -66,7 +59,7 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
|||||||
fun `save a NodeInfo to JimFs`() {
|
fun `save a NodeInfo to JimFs`() {
|
||||||
val jimFs = Jimfs.newFileSystem(Configuration.unix())
|
val jimFs = Jimfs.newFileSystem(Configuration.unix())
|
||||||
val jimFolder = jimFs.getPath("/nodeInfo")
|
val jimFolder = jimFs.getPath("/nodeInfo")
|
||||||
NodeInfoWatcher.saveToFile(jimFolder, nodeInfo, keyManagementService)
|
NodeInfoWatcher.saveToFile(jimFolder, nodeInfo, ALICE_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -135,6 +128,6 @@ class NodeInfoWatcherTest : NodeBasedTest() {
|
|||||||
|
|
||||||
// Write a nodeInfo under the right path.
|
// Write a nodeInfo under the right path.
|
||||||
private fun createNodeInfoFileInPath(nodeInfo: NodeInfo) {
|
private fun createNodeInfoFileInPath(nodeInfo: NodeInfo) {
|
||||||
NodeInfoWatcher.saveToFile(nodeInfoPath, nodeInfo, keyManagementService)
|
NodeInfoWatcher.saveToFile(nodeInfoPath, nodeInfo, ALICE_KEY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import net.corda.node.internal.Node
|
|||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.BOB
|
import net.corda.testing.BOB
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_REGULATOR
|
||||||
import net.corda.testing.chooseIdentity
|
import net.corda.testing.chooseIdentity
|
||||||
import net.corda.testing.internal.NodeBasedTest
|
import net.corda.testing.internal.NodeBasedTest
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -16,9 +16,9 @@ import org.junit.Test
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class PersistentNetworkMapCacheTest : NodeBasedTest() {
|
class PersistentNetworkMapCacheTest : NodeBasedTest() {
|
||||||
private val partiesList = listOf(DUMMY_NOTARY, ALICE, BOB)
|
private val partiesList = listOf(DUMMY_REGULATOR, ALICE, BOB)
|
||||||
private val addressesMap = HashMap<CordaX500Name, NetworkHostAndPort>()
|
private val addressesMap = HashMap<CordaX500Name, NetworkHostAndPort>()
|
||||||
private val infos: MutableSet<NodeInfo> = HashSet()
|
private val infos = HashSet<NodeInfo>()
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun start() {
|
fun start() {
|
||||||
@ -37,8 +37,8 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() {
|
|||||||
alice.database.transaction {
|
alice.database.transaction {
|
||||||
val res = netCache.getNodeByLegalIdentity(alice.info.chooseIdentity())
|
val res = netCache.getNodeByLegalIdentity(alice.info.chooseIdentity())
|
||||||
assertEquals(alice.info, res)
|
assertEquals(alice.info, res)
|
||||||
val res2 = netCache.getNodeByLegalName(DUMMY_NOTARY.name)
|
val res2 = netCache.getNodeByLegalName(DUMMY_REGULATOR.name)
|
||||||
assertEquals(infos.singleOrNull { DUMMY_NOTARY.name in it.legalIdentitiesAndCerts.map { it.name } }, res2)
|
assertEquals(infos.singleOrNull { DUMMY_REGULATOR.name in it.legalIdentities.map { it.name } }, res2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,11 +8,11 @@ import net.corda.core.messaging.startFlow
|
|||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.testing.BOB
|
import net.corda.testing.BOB
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
import net.corda.testing.aliceBobAndNotary
|
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.contracts.DummyState
|
import net.corda.testing.contracts.DummyState
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.dummyCommand
|
import net.corda.testing.dummyCommand
|
||||||
|
import net.corda.testing.notaryAliceAndBob
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ class LargeTransactionsTest {
|
|||||||
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2)
|
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2)
|
||||||
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3)
|
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3)
|
||||||
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) {
|
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) {
|
||||||
val (alice, _, _) = aliceBobAndNotary()
|
val (_, alice) = notaryAliceAndBob()
|
||||||
alice.useRPC {
|
alice.useRPC {
|
||||||
val hash1 = it.uploadAttachment(bigFile1.inputStream)
|
val hash1 = it.uploadAttachment(bigFile1.inputStream)
|
||||||
val hash2 = it.uploadAttachment(bigFile2.inputStream)
|
val hash2 = it.uploadAttachment(bigFile2.inputStream)
|
||||||
|
@ -18,7 +18,6 @@ import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
|||||||
import org.bouncycastle.asn1.x509.GeneralName
|
import org.bouncycastle.asn1.x509.GeneralName
|
||||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||||
import org.bouncycastle.asn1.x509.NameConstraints
|
import org.bouncycastle.asn1.x509.NameConstraints
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import net.corda.nodeapi.User
|
|||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
import net.corda.testing.chooseIdentity
|
import net.corda.testing.chooseIdentity
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import org.junit.Assume
|
import org.junit.Assume.assumeFalse
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.lang.management.ManagementFactory
|
import java.lang.management.ManagementFactory
|
||||||
import javax.persistence.Column
|
import javax.persistence.Column
|
||||||
@ -33,25 +33,24 @@ import kotlin.test.assertEquals
|
|||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
class NodeStatePersistenceTests {
|
class NodeStatePersistenceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `persistent state survives node restart`() {
|
fun `persistent state survives node restart`() {
|
||||||
// Temporary disable this test when executed on Windows. It is known to be sporadically failing.
|
// Temporary disable this test when executed on Windows. It is known to be sporadically failing.
|
||||||
// More investigation is needed to establish why.
|
// More investigation is needed to establish why.
|
||||||
Assume.assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win"))
|
assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win"))
|
||||||
|
|
||||||
val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery")))
|
val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery")))
|
||||||
val message = Message("Hello world!")
|
val message = Message("Hello world!")
|
||||||
driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) {
|
driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) {
|
||||||
val (nodeName, notaryNodeHandle) = {
|
val nodeName = {
|
||||||
val notaryNodeHandle = startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow()
|
startNotaryNode(DUMMY_NOTARY.name, validating = false).getOrThrow()
|
||||||
val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
|
val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
|
||||||
val nodeName = nodeHandle.nodeInfo.chooseIdentity().name
|
val nodeName = nodeHandle.nodeInfo.chooseIdentity().name
|
||||||
nodeHandle.rpcClientToNode().start(user.username, user.password).use {
|
nodeHandle.rpcClientToNode().start(user.username, user.password).use {
|
||||||
it.proxy.startFlow(::SendMessageFlow, message).returnValue.getOrThrow()
|
it.proxy.startFlow(::SendMessageFlow, message).returnValue.getOrThrow()
|
||||||
}
|
}
|
||||||
nodeHandle.stop()
|
nodeHandle.stop()
|
||||||
nodeName to notaryNodeHandle
|
nodeName
|
||||||
}()
|
}()
|
||||||
|
|
||||||
val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user)).getOrThrow()
|
val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user)).getOrThrow()
|
||||||
|
@ -7,6 +7,7 @@ import net.corda.confidential.SwapIdentitiesFlow
|
|||||||
import net.corda.confidential.SwapIdentitiesHandler
|
import net.corda.confidential.SwapIdentitiesHandler
|
||||||
import net.corda.core.CordaException
|
import net.corda.core.CordaException
|
||||||
import net.corda.core.concurrent.CordaFuture
|
import net.corda.core.concurrent.CordaFuture
|
||||||
|
import net.corda.core.crypto.SignedData
|
||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.*
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
@ -20,6 +21,7 @@ import net.corda.core.node.services.*
|
|||||||
import net.corda.core.serialization.SerializationWhitelist
|
import net.corda.core.serialization.SerializationWhitelist
|
||||||
import net.corda.core.serialization.SerializeAsToken
|
import net.corda.core.serialization.SerializeAsToken
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.debug
|
import net.corda.core.utilities.debug
|
||||||
@ -42,7 +44,9 @@ import net.corda.node.services.events.ScheduledActivityObserver
|
|||||||
import net.corda.node.services.identity.PersistentIdentityService
|
import net.corda.node.services.identity.PersistentIdentityService
|
||||||
import net.corda.node.services.keys.PersistentKeyManagementService
|
import net.corda.node.services.keys.PersistentKeyManagementService
|
||||||
import net.corda.node.services.messaging.MessagingService
|
import net.corda.node.services.messaging.MessagingService
|
||||||
import net.corda.node.services.network.*
|
import net.corda.node.services.network.NetworkMapCacheImpl
|
||||||
|
import net.corda.node.services.network.NodeInfoWatcher
|
||||||
|
import net.corda.node.services.network.PersistentNetworkMapCache
|
||||||
import net.corda.node.services.persistence.DBCheckpointStorage
|
import net.corda.node.services.persistence.DBCheckpointStorage
|
||||||
import net.corda.node.services.persistence.DBTransactionMappingStorage
|
import net.corda.node.services.persistence.DBTransactionMappingStorage
|
||||||
import net.corda.node.services.persistence.DBTransactionStorage
|
import net.corda.node.services.persistence.DBTransactionStorage
|
||||||
@ -82,18 +86,15 @@ import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair
|
|||||||
* Marked as SingletonSerializeAsToken to prevent the invisible reference to AbstractNode in the ServiceHub accidentally
|
* Marked as SingletonSerializeAsToken to prevent the invisible reference to AbstractNode in the ServiceHub accidentally
|
||||||
* sweeping up the Node into the Kryo checkpoint serialization via any flows holding a reference to ServiceHub.
|
* sweeping up the Node into the Kryo checkpoint serialization via any flows holding a reference to ServiceHub.
|
||||||
*/
|
*/
|
||||||
|
// TODO Log warning if this node is a notary but not one of the ones specified in the network parameters, both for core and custom
|
||||||
|
|
||||||
// In theory the NodeInfo for the node should be passed in, instead, however currently this is constructed by the
|
// In theory the NodeInfo for the node should be passed in, instead, however currently this is constructed by the
|
||||||
// AbstractNode. It should be possible to generate the NodeInfo outside of AbstractNode, so it can be passed in.
|
// AbstractNode. It should be possible to generate the NodeInfo outside of AbstractNode, so it can be passed in.
|
||||||
abstract class AbstractNode(config: NodeConfiguration,
|
abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||||
val platformClock: Clock,
|
val platformClock: Clock,
|
||||||
protected val versionInfo: VersionInfo,
|
protected val versionInfo: VersionInfo,
|
||||||
protected val cordappLoader: CordappLoader,
|
protected val cordappLoader: CordappLoader,
|
||||||
@VisibleForTesting val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() {
|
private val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() {
|
||||||
open val configuration = config.apply {
|
|
||||||
require(minimumPlatformVersion <= versionInfo.platformVersion) {
|
|
||||||
"minimumPlatformVersion cannot be greater than the node's own version"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class StartedNodeImpl<out N : AbstractNode>(
|
private class StartedNodeImpl<out N : AbstractNode>(
|
||||||
override val internals: N,
|
override val internals: N,
|
||||||
@ -116,14 +117,12 @@ abstract class AbstractNode(config: NodeConfiguration,
|
|||||||
// low-performance prototyping period.
|
// low-performance prototyping period.
|
||||||
protected abstract val serverThread: AffinityExecutor
|
protected abstract val serverThread: AffinityExecutor
|
||||||
|
|
||||||
|
protected lateinit var networkParameters: NetworkParameters
|
||||||
private val cordappServices = MutableClassToInstanceMap.create<SerializeAsToken>()
|
private val cordappServices = MutableClassToInstanceMap.create<SerializeAsToken>()
|
||||||
private val flowFactories = ConcurrentHashMap<Class<out FlowLogic<*>>, InitiatedFlowFactory<*>>()
|
private val flowFactories = ConcurrentHashMap<Class<out FlowLogic<*>>, InitiatedFlowFactory<*>>()
|
||||||
protected val partyKeys = mutableSetOf<KeyPair>()
|
|
||||||
|
|
||||||
protected val services: ServiceHubInternal get() = _services
|
protected val services: ServiceHubInternal get() = _services
|
||||||
private lateinit var _services: ServiceHubInternalImpl
|
private lateinit var _services: ServiceHubInternalImpl
|
||||||
protected lateinit var legalIdentity: PartyAndCertificate
|
|
||||||
private lateinit var allIdentities: List<PartyAndCertificate>
|
|
||||||
protected lateinit var info: NodeInfo
|
protected lateinit var info: NodeInfo
|
||||||
protected var myNotaryIdentity: PartyAndCertificate? = null
|
protected var myNotaryIdentity: PartyAndCertificate? = null
|
||||||
protected lateinit var checkpointStorage: CheckpointStorage
|
protected lateinit var checkpointStorage: CheckpointStorage
|
||||||
@ -157,10 +156,6 @@ abstract class AbstractNode(config: NodeConfiguration,
|
|||||||
return SecureCordaRPCOps(services, smm, database, flowStarter)
|
return SecureCordaRPCOps(services, smm, database, flowStarter)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveOwnNodeInfo() {
|
|
||||||
NodeInfoWatcher.saveToFile(configuration.baseDirectory, info, services.keyManagementService)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initCertificate() {
|
private fun initCertificate() {
|
||||||
if (configuration.devMode) {
|
if (configuration.devMode) {
|
||||||
log.warn("Corda node is running in dev mode.")
|
log.warn("Corda node is running in dev mode.")
|
||||||
@ -169,33 +164,34 @@ abstract class AbstractNode(config: NodeConfiguration,
|
|||||||
validateKeystore()
|
validateKeystore()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeSchemaService() = NodeSchemaService(cordappLoader)
|
|
||||||
open fun generateNodeInfo() {
|
open fun generateNodeInfo() {
|
||||||
check(started == null) { "Node has already been started" }
|
check(started == null) { "Node has already been started" }
|
||||||
initCertificate()
|
|
||||||
log.info("Generating nodeInfo ...")
|
log.info("Generating nodeInfo ...")
|
||||||
val schemaService = makeSchemaService()
|
initCertificate()
|
||||||
initialiseDatabasePersistence(schemaService) {
|
initNodeInfo()
|
||||||
val transactionStorage = makeTransactionStorage()
|
|
||||||
makeServices(schemaService, transactionStorage, StateLoaderImpl(transactionStorage))
|
|
||||||
saveOwnNodeInfo()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun start(): StartedNode<AbstractNode> {
|
open fun start(): StartedNode<AbstractNode> {
|
||||||
check(started == null) { "Node has already been started" }
|
check(started == null) { "Node has already been started" }
|
||||||
initCertificate()
|
|
||||||
log.info("Node starting up ...")
|
log.info("Node starting up ...")
|
||||||
val schemaService = makeSchemaService()
|
initCertificate()
|
||||||
|
val keyPairs = initNodeInfo()
|
||||||
|
readNetworkParameters()
|
||||||
|
val schemaService = NodeSchemaService(cordappLoader)
|
||||||
// Do all of this in a database transaction so anything that might need a connection has one.
|
// Do all of this in a database transaction so anything that might need a connection has one.
|
||||||
val startedImpl = initialiseDatabasePersistence(schemaService) {
|
val startedImpl = initialiseDatabasePersistence(schemaService) {
|
||||||
val transactionStorage = makeTransactionStorage()
|
val transactionStorage = makeTransactionStorage()
|
||||||
val stateLoader = StateLoaderImpl(transactionStorage)
|
val stateLoader = StateLoaderImpl(transactionStorage)
|
||||||
val services = makeServices(schemaService, transactionStorage, stateLoader)
|
val services = makeServices(keyPairs, schemaService, transactionStorage, stateLoader)
|
||||||
saveOwnNodeInfo()
|
|
||||||
smm = makeStateMachineManager()
|
smm = makeStateMachineManager()
|
||||||
val flowStarter = FlowStarterImpl(serverThread, smm)
|
val flowStarter = FlowStarterImpl(serverThread, smm)
|
||||||
val schedulerService = NodeSchedulerService(platformClock, this@AbstractNode.database, flowStarter, stateLoader, unfinishedSchedules = busyNodeLatch, serverThread = serverThread)
|
val schedulerService = NodeSchedulerService(
|
||||||
|
platformClock,
|
||||||
|
this@AbstractNode.database,
|
||||||
|
flowStarter,
|
||||||
|
stateLoader,
|
||||||
|
unfinishedSchedules = busyNodeLatch,
|
||||||
|
serverThread = serverThread)
|
||||||
if (serverThread is ExecutorService) {
|
if (serverThread is ExecutorService) {
|
||||||
runOnStop += {
|
runOnStop += {
|
||||||
// We wait here, even though any in-flight messages should have been drained away because the
|
// We wait here, even though any in-flight messages should have been drained away because the
|
||||||
@ -232,6 +228,35 @@ abstract class AbstractNode(config: NodeConfiguration,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun initNodeInfo(): Set<KeyPair> {
|
||||||
|
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
|
||||||
|
val keyPairs = mutableSetOf(identityKeyPair)
|
||||||
|
|
||||||
|
myNotaryIdentity = configuration.notary?.let {
|
||||||
|
if (it.isClusterConfig) {
|
||||||
|
val (notaryIdentity, notaryIdentityKeyPair) = obtainIdentity(it)
|
||||||
|
keyPairs += notaryIdentityKeyPair
|
||||||
|
notaryIdentity
|
||||||
|
} else {
|
||||||
|
// In case of a single notary service myNotaryIdentity will be the node's single identity.
|
||||||
|
identity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info = NodeInfo(
|
||||||
|
myAddresses(),
|
||||||
|
setOf(identity, myNotaryIdentity).filterNotNull(),
|
||||||
|
versionInfo.platformVersion,
|
||||||
|
platformClock.instant().toEpochMilli()
|
||||||
|
)
|
||||||
|
|
||||||
|
NodeInfoWatcher.saveToFile(configuration.baseDirectory, info, identityKeyPair)
|
||||||
|
|
||||||
|
return keyPairs
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun myAddresses(): List<NetworkHostAndPort>
|
||||||
|
|
||||||
protected open fun makeStateMachineManager(): StateMachineManager {
|
protected open fun makeStateMachineManager(): StateMachineManager {
|
||||||
return StateMachineManagerImpl(
|
return StateMachineManagerImpl(
|
||||||
services,
|
services,
|
||||||
@ -458,23 +483,25 @@ abstract class AbstractNode(config: NodeConfiguration,
|
|||||||
* Builds node internal, advertised, and plugin services.
|
* Builds node internal, advertised, and plugin services.
|
||||||
* Returns a list of tokenizable services to be added to the serialisation context.
|
* Returns a list of tokenizable services to be added to the serialisation context.
|
||||||
*/
|
*/
|
||||||
private fun makeServices(schemaService: SchemaService, transactionStorage: WritableTransactionStorage, stateLoader: StateLoader): MutableList<Any> {
|
private fun makeServices(keyPairs: Set<KeyPair>, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, stateLoader: StateLoader): MutableList<Any> {
|
||||||
checkpointStorage = DBCheckpointStorage()
|
checkpointStorage = DBCheckpointStorage()
|
||||||
val metrics = MetricRegistry()
|
val metrics = MetricRegistry()
|
||||||
attachments = NodeAttachmentService(metrics)
|
attachments = NodeAttachmentService(metrics)
|
||||||
val cordappProvider = CordappProviderImpl(cordappLoader, attachments)
|
val cordappProvider = CordappProviderImpl(cordappLoader, attachments)
|
||||||
_services = ServiceHubInternalImpl(schemaService, transactionStorage, stateLoader, MonitoringService(metrics), cordappProvider)
|
val identityService = makeIdentityService()
|
||||||
legalIdentity = obtainIdentity(notaryConfig = null)
|
val keyManagementService = makeKeyManagementService(identityService, keyPairs)
|
||||||
// TODO We keep only notary identity as additional legalIdentity if we run it on a node . Multiple identities need more design thinking.
|
_services = ServiceHubInternalImpl(
|
||||||
myNotaryIdentity = getNotaryIdentity()
|
identityService,
|
||||||
allIdentities = listOf(legalIdentity, myNotaryIdentity).filterNotNull()
|
keyManagementService,
|
||||||
network = makeMessagingService(legalIdentity)
|
schemaService,
|
||||||
val addresses = myAddresses() // TODO There is no support for multiple IP addresses yet.
|
transactionStorage,
|
||||||
info = NodeInfo(addresses, allIdentities, versionInfo.platformVersion, platformClock.instant().toEpochMilli())
|
stateLoader,
|
||||||
val networkMapCache = services.networkMapCache
|
MonitoringService(metrics),
|
||||||
|
cordappProvider)
|
||||||
|
network = makeMessagingService()
|
||||||
val tokenizableServices = mutableListOf(attachments, network, services.vaultService,
|
val tokenizableServices = mutableListOf(attachments, network, services.vaultService,
|
||||||
services.keyManagementService, services.identityService, platformClock,
|
services.keyManagementService, services.identityService, platformClock,
|
||||||
services.auditService, services.monitoringService, networkMapCache, services.schemaService,
|
services.auditService, services.monitoringService, services.networkMapCache, services.schemaService,
|
||||||
services.transactionVerifierService, services.validatedTransactions, services.contractUpgradeService,
|
services.transactionVerifierService, services.validatedTransactions, services.contractUpgradeService,
|
||||||
services, cordappProvider, this)
|
services, cordappProvider, this)
|
||||||
makeNetworkServices(tokenizableServices)
|
makeNetworkServices(tokenizableServices)
|
||||||
@ -489,12 +516,6 @@ abstract class AbstractNode(config: NodeConfiguration,
|
|||||||
HibernateObserver.install(services.vaultService.rawUpdates, database.hibernateConfig)
|
HibernateObserver.install(services.vaultService.rawUpdates, database.hibernateConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain the node's notary identity if it's configured to be one. If part of a distributed notary then this will be
|
|
||||||
* the distributed identity shared across all the nodes of the cluster.
|
|
||||||
*/
|
|
||||||
protected fun getNotaryIdentity(): PartyAndCertificate? = configuration.notary?.let { obtainIdentity(it) }
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
protected open fun acceptableLiveFiberCountOnStop(): Int = 0
|
protected open fun acceptableLiveFiberCountOnStop(): Int = 0
|
||||||
|
|
||||||
@ -549,9 +570,6 @@ abstract class AbstractNode(config: NodeConfiguration,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return list of node's addresses. It's overridden in MockNetwork as we don't have real addresses for MockNodes. */
|
|
||||||
protected abstract fun myAddresses(): List<NetworkHostAndPort>
|
|
||||||
|
|
||||||
open protected fun checkNetworkMapIsInitialized() {
|
open protected fun checkNetworkMapIsInitialized() {
|
||||||
if (!services.networkMapCache.loadDBSuccess) {
|
if (!services.networkMapCache.loadDBSuccess) {
|
||||||
// TODO: There should be a consistent approach to configuration error exceptions.
|
// TODO: There should be a consistent approach to configuration error exceptions.
|
||||||
@ -559,8 +577,15 @@ abstract class AbstractNode(config: NodeConfiguration,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun makeKeyManagementService(identityService: IdentityService): KeyManagementService {
|
protected open fun makeKeyManagementService(identityService: IdentityService, keyPairs: Set<KeyPair>): KeyManagementService {
|
||||||
return PersistentKeyManagementService(identityService, partyKeys)
|
return PersistentKeyManagementService(identityService, keyPairs)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun readNetworkParameters() {
|
||||||
|
val file = configuration.baseDirectory / "network-parameters"
|
||||||
|
networkParameters = file.readAll().deserialize<SignedData<NetworkParameters>>().verified()
|
||||||
|
log.info(networkParameters.toString())
|
||||||
|
check(networkParameters.minimumPlatformVersion <= versionInfo.platformVersion) { "Node is too old for the network" }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeCoreNotaryService(notaryConfig: NotaryConfig): NotaryService {
|
private fun makeCoreNotaryService(notaryConfig: NotaryConfig): NotaryService {
|
||||||
@ -593,13 +618,13 @@ abstract class AbstractNode(config: NodeConfiguration,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun makeIdentityService(trustRoot: X509Certificate,
|
private fun makeIdentityService(): IdentityService {
|
||||||
clientCa: CertificateAndKeyPair?,
|
val trustStore = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword)
|
||||||
legalIdentity: PartyAndCertificate): IdentityService {
|
val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||||
val caCertificates: Array<X509Certificate> = listOf(legalIdentity.certificate, clientCa?.certificate?.cert)
|
val trustRoot = trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA)
|
||||||
.filterNotNull()
|
val clientCa = caKeyStore.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA)
|
||||||
.toTypedArray()
|
val caCertificates = arrayOf(info.legalIdentitiesAndCerts[0].certificate, clientCa.certificate.cert)
|
||||||
return PersistentIdentityService(allIdentities, trustRoot = trustRoot, caCertificates = *caCertificates)
|
return PersistentIdentityService(info.legalIdentitiesAndCerts, trustRoot = trustRoot, caCertificates = *caCertificates)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun makeTransactionVerifierService(): TransactionVerifierService
|
protected abstract fun makeTransactionVerifierService(): TransactionVerifierService
|
||||||
@ -619,29 +644,23 @@ abstract class AbstractNode(config: NodeConfiguration,
|
|||||||
_started = null
|
_started = null
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun makeMessagingService(legalIdentity: PartyAndCertificate): MessagingService
|
protected abstract fun makeMessagingService(): MessagingService
|
||||||
|
|
||||||
protected abstract fun startMessagingService(rpcOps: RPCOps)
|
protected abstract fun startMessagingService(rpcOps: RPCOps)
|
||||||
|
|
||||||
private fun obtainIdentity(notaryConfig: NotaryConfig?): PartyAndCertificate {
|
private fun obtainIdentity(notaryConfig: NotaryConfig?): Pair<PartyAndCertificate, KeyPair> {
|
||||||
val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||||
|
|
||||||
val (id, singleName) = if (notaryConfig == null) {
|
val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) {
|
||||||
// Node's main identity
|
// Node's main identity or if it's a single node notary
|
||||||
Pair("identity", myLegalName)
|
Pair("identity", myLegalName)
|
||||||
} else {
|
} else {
|
||||||
val notaryId = notaryConfig.run {
|
val notaryId = notaryConfig.run {
|
||||||
NotaryService.constructId(validating, raft != null, bftSMaRt != null, custom)
|
NotaryService.constructId(validating, raft != null, bftSMaRt != null, custom)
|
||||||
}
|
}
|
||||||
if (notaryConfig.bftSMaRt == null && notaryConfig.raft == null) {
|
// The node is part of a distributed notary whose identity must already be generated beforehand.
|
||||||
// Node's notary identity
|
Pair(notaryId, null)
|
||||||
Pair(notaryId, myLegalName.copy(commonName = notaryId))
|
|
||||||
} else {
|
|
||||||
// The node is part of a distributed notary whose identity must already be generated beforehand
|
|
||||||
Pair(notaryId, null)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Integrate with Key management service?
|
// TODO: Integrate with Key management service?
|
||||||
val privateKeyAlias = "$id-private-key"
|
val privateKeyAlias = "$id-private-key"
|
||||||
|
|
||||||
@ -653,7 +672,7 @@ abstract class AbstractNode(config: NodeConfiguration,
|
|||||||
keyStore.signAndSaveNewKeyPair(singleName, privateKeyAlias, generateKeyPair())
|
keyStore.signAndSaveNewKeyPair(singleName, privateKeyAlias, generateKeyPair())
|
||||||
}
|
}
|
||||||
|
|
||||||
val (x509Cert, keys) = keyStore.certificateAndKeyPair(privateKeyAlias)
|
val (x509Cert, keyPair) = keyStore.certificateAndKeyPair(privateKeyAlias)
|
||||||
|
|
||||||
// TODO: Use configuration to indicate composite key should be used instead of public key for the identity.
|
// TODO: Use configuration to indicate composite key should be used instead of public key for the identity.
|
||||||
val compositeKeyAlias = "$id-composite-key"
|
val compositeKeyAlias = "$id-composite-key"
|
||||||
@ -679,8 +698,8 @@ abstract class AbstractNode(config: NodeConfiguration,
|
|||||||
throw ConfigurationException("The name '$singleName' for $id doesn't match what's in the key store: $subject")
|
throw ConfigurationException("The name '$singleName' for $id doesn't match what's in the key store: $subject")
|
||||||
}
|
}
|
||||||
|
|
||||||
partyKeys += keys
|
val certPath = CertificateFactory.getInstance("X509").generateCertPath(certificates)
|
||||||
return PartyAndCertificate(CertificateFactory.getInstance("X509").generateCertPath(certificates))
|
return Pair(PartyAndCertificate(certPath), keyPair)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun generateKeyPair() = cryptoGenerateKeyPair()
|
protected open fun generateKeyPair() = cryptoGenerateKeyPair()
|
||||||
@ -689,6 +708,11 @@ abstract class AbstractNode(config: NodeConfiguration,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private inner class ServiceHubInternalImpl(
|
private inner class ServiceHubInternalImpl(
|
||||||
|
override val identityService: IdentityService,
|
||||||
|
// Place the long term identity key in the KMS. Eventually, this is likely going to be separated again because
|
||||||
|
// the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with
|
||||||
|
// the identity key. But the infrastructure to make that easy isn't here yet.
|
||||||
|
override val keyManagementService: KeyManagementService,
|
||||||
override val schemaService: SchemaService,
|
override val schemaService: SchemaService,
|
||||||
override val validatedTransactions: WritableTransactionStorage,
|
override val validatedTransactions: WritableTransactionStorage,
|
||||||
private val stateLoader: StateLoader,
|
private val stateLoader: StateLoader,
|
||||||
@ -699,22 +723,16 @@ abstract class AbstractNode(config: NodeConfiguration,
|
|||||||
override val stateMachineRecordedTransactionMapping = DBTransactionMappingStorage()
|
override val stateMachineRecordedTransactionMapping = DBTransactionMappingStorage()
|
||||||
override val auditService = DummyAuditService()
|
override val auditService = DummyAuditService()
|
||||||
override val transactionVerifierService by lazy { makeTransactionVerifierService() }
|
override val transactionVerifierService by lazy { makeTransactionVerifierService() }
|
||||||
override val networkMapCache by lazy { NetworkMapCacheImpl(PersistentNetworkMapCache(this@AbstractNode.database, this@AbstractNode.configuration), identityService) }
|
override val networkMapCache by lazy {
|
||||||
|
NetworkMapCacheImpl(
|
||||||
|
PersistentNetworkMapCache(
|
||||||
|
this@AbstractNode.database,
|
||||||
|
this@AbstractNode.configuration,
|
||||||
|
networkParameters.notaries),
|
||||||
|
identityService)
|
||||||
|
}
|
||||||
override val vaultService by lazy { makeVaultService(keyManagementService, stateLoader) }
|
override val vaultService by lazy { makeVaultService(keyManagementService, stateLoader) }
|
||||||
override val contractUpgradeService by lazy { ContractUpgradeServiceImpl() }
|
override val contractUpgradeService by lazy { ContractUpgradeServiceImpl() }
|
||||||
|
|
||||||
// Place the long term identity key in the KMS. Eventually, this is likely going to be separated again because
|
|
||||||
// the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with
|
|
||||||
// the identity key. But the infrastructure to make that easy isn't here yet.
|
|
||||||
override val keyManagementService by lazy { makeKeyManagementService(identityService) }
|
|
||||||
override val identityService by lazy {
|
|
||||||
val trustStore = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword)
|
|
||||||
val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
|
||||||
makeIdentityService(
|
|
||||||
trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA),
|
|
||||||
caKeyStore.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA),
|
|
||||||
legalIdentity)
|
|
||||||
}
|
|
||||||
override val attachments: AttachmentStorage get() = this@AbstractNode.attachments
|
override val attachments: AttachmentStorage get() = this@AbstractNode.attachments
|
||||||
override val networkService: MessagingService get() = network
|
override val networkService: MessagingService get() = network
|
||||||
override val clock: Clock get() = platformClock
|
override val clock: Clock get() = platformClock
|
||||||
|
@ -4,7 +4,6 @@ import com.codahale.metrics.JmxReporter
|
|||||||
import net.corda.core.CordaException
|
import net.corda.core.CordaException
|
||||||
import net.corda.core.concurrent.CordaFuture
|
import net.corda.core.concurrent.CordaFuture
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
|
||||||
import net.corda.core.internal.concurrent.openFuture
|
import net.corda.core.internal.concurrent.openFuture
|
||||||
import net.corda.core.internal.concurrent.thenMatch
|
import net.corda.core.internal.concurrent.thenMatch
|
||||||
import net.corda.core.internal.uncheckedCast
|
import net.corda.core.internal.uncheckedCast
|
||||||
@ -45,7 +44,7 @@ import kotlin.system.exitProcess
|
|||||||
*
|
*
|
||||||
* @param configuration This is typically loaded from a TypeSafe HOCON configuration file.
|
* @param configuration This is typically loaded from a TypeSafe HOCON configuration file.
|
||||||
*/
|
*/
|
||||||
open class Node(override val configuration: NodeConfiguration,
|
open class Node(configuration: NodeConfiguration,
|
||||||
versionInfo: VersionInfo,
|
versionInfo: VersionInfo,
|
||||||
val initialiseSerialization: Boolean = true,
|
val initialiseSerialization: Boolean = true,
|
||||||
cordappLoader: CordappLoader = makeCordappLoader(configuration)
|
cordappLoader: CordappLoader = makeCordappLoader(configuration)
|
||||||
@ -131,11 +130,11 @@ open class Node(override val configuration: NodeConfiguration,
|
|||||||
|
|
||||||
private lateinit var userService: RPCUserService
|
private lateinit var userService: RPCUserService
|
||||||
|
|
||||||
override fun makeMessagingService(legalIdentity: PartyAndCertificate): MessagingService {
|
override fun makeMessagingService(): MessagingService {
|
||||||
userService = RPCUserServiceImpl(configuration.rpcUsers)
|
userService = RPCUserServiceImpl(configuration.rpcUsers)
|
||||||
|
|
||||||
val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker()
|
val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker()
|
||||||
val advertisedAddress = configuration.messagingServerAddress ?: getAdvertisedAddress()
|
val advertisedAddress = info.addresses.single()
|
||||||
|
|
||||||
printBasicNodeInfo("Incoming connection address", advertisedAddress.toString())
|
printBasicNodeInfo("Incoming connection address", advertisedAddress.toString())
|
||||||
|
|
||||||
@ -143,7 +142,7 @@ open class Node(override val configuration: NodeConfiguration,
|
|||||||
configuration,
|
configuration,
|
||||||
versionInfo,
|
versionInfo,
|
||||||
serverAddress,
|
serverAddress,
|
||||||
legalIdentity.owningKey,
|
info.legalIdentities[0].owningKey,
|
||||||
serverThread,
|
serverThread,
|
||||||
database,
|
database,
|
||||||
services.monitoringService,
|
services.monitoringService,
|
||||||
@ -157,14 +156,18 @@ open class Node(override val configuration: NodeConfiguration,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun myAddresses(): List<NetworkHostAndPort> {
|
||||||
|
return listOf(configuration.messagingServerAddress ?: getAdvertisedAddress())
|
||||||
|
}
|
||||||
|
|
||||||
private fun getAdvertisedAddress(): NetworkHostAndPort {
|
private fun getAdvertisedAddress(): NetworkHostAndPort {
|
||||||
return with(configuration) {
|
return with(configuration) {
|
||||||
val useHost = if (detectPublicIp) {
|
val host = if (detectPublicIp) {
|
||||||
tryDetectIfNotPublicHost(p2pAddress.host) ?: p2pAddress.host
|
tryDetectIfNotPublicHost(p2pAddress.host) ?: p2pAddress.host
|
||||||
} else {
|
} else {
|
||||||
p2pAddress.host
|
p2pAddress.host
|
||||||
}
|
}
|
||||||
NetworkHostAndPort(useHost, p2pAddress.port)
|
NetworkHostAndPort(host, p2pAddress.port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,11 +199,6 @@ open class Node(override val configuration: NodeConfiguration,
|
|||||||
(network as NodeMessagingClient).start(rpcOps, userService)
|
(network as NodeMessagingClient).start(rpcOps, userService)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun myAddresses(): List<NetworkHostAndPort> {
|
|
||||||
val address = network.myAddress as ArtemisMessagingComponent.ArtemisPeerAddress
|
|
||||||
return listOf(address.hostAndPort)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the node is persisting to an embedded H2 database, then expose this via TCP with a JDBC URL of the form:
|
* If the node is persisting to an embedded H2 database, then expose this via TCP with a JDBC URL of the form:
|
||||||
* jdbc:h2:tcp://<host>:<port>/node
|
* jdbc:h2:tcp://<host>:<port>/node
|
||||||
|
@ -18,7 +18,6 @@ interface NodeConfiguration : NodeSSLConfiguration {
|
|||||||
// myLegalName should be only used in the initial network registration, we should use the name from the certificate instead of this.
|
// myLegalName should be only used in the initial network registration, we should use the name from the certificate instead of this.
|
||||||
// TODO: Remove this so we don't accidentally use this identity in the code?
|
// TODO: Remove this so we don't accidentally use this identity in the code?
|
||||||
val myLegalName: CordaX500Name
|
val myLegalName: CordaX500Name
|
||||||
val minimumPlatformVersion: Int
|
|
||||||
val emailAddress: String
|
val emailAddress: String
|
||||||
val exportJMXto: String
|
val exportJMXto: String
|
||||||
val dataSourceProperties: Properties
|
val dataSourceProperties: Properties
|
||||||
@ -52,15 +51,17 @@ data class NotaryConfig(val validating: Boolean,
|
|||||||
"raft, bftSMaRt, and custom configs cannot be specified together"
|
"raft, bftSMaRt, and custom configs cannot be specified together"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val isClusterConfig: Boolean get() = raft != null || bftSMaRt != null
|
||||||
}
|
}
|
||||||
|
|
||||||
data class RaftConfig(val nodeAddress: NetworkHostAndPort, val clusterAddresses: List<NetworkHostAndPort>)
|
data class RaftConfig(val nodeAddress: NetworkHostAndPort, val clusterAddresses: List<NetworkHostAndPort>)
|
||||||
|
|
||||||
/** @param exposeRaces for testing only, so its default is not in reference.conf but here. */
|
/** @param exposeRaces for testing only, so its default is not in reference.conf but here. */
|
||||||
data class BFTSMaRtConfiguration constructor(val replicaId: Int,
|
data class BFTSMaRtConfiguration(
|
||||||
val clusterAddresses: List<NetworkHostAndPort>,
|
val replicaId: Int,
|
||||||
val debug: Boolean = false,
|
val clusterAddresses: List<NetworkHostAndPort>,
|
||||||
val exposeRaces: Boolean = false
|
val debug: Boolean = false,
|
||||||
|
val exposeRaces: Boolean = false
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
require(replicaId >= 0) { "replicaId cannot be negative" }
|
require(replicaId >= 0) { "replicaId cannot be negative" }
|
||||||
@ -85,7 +86,6 @@ data class NodeConfigurationImpl(
|
|||||||
override val dataSourceProperties: Properties,
|
override val dataSourceProperties: Properties,
|
||||||
override val database: Properties?,
|
override val database: Properties?,
|
||||||
override val certificateSigningService: URL,
|
override val certificateSigningService: URL,
|
||||||
override val minimumPlatformVersion: Int = 1,
|
|
||||||
override val rpcUsers: List<User>,
|
override val rpcUsers: List<User>,
|
||||||
override val verifierType: VerifierType,
|
override val verifierType: VerifierType,
|
||||||
// TODO typesafe config supports the notion of durations. Make use of that by mapping it to java.time.Duration.
|
// TODO typesafe config supports the notion of durations. Make use of that by mapping it to java.time.Duration.
|
||||||
@ -113,8 +113,6 @@ data class NodeConfigurationImpl(
|
|||||||
// This is a sanity feature do not remove.
|
// This is a sanity feature do not remove.
|
||||||
require(!useTestClock || devMode) { "Cannot use test clock outside of dev mode" }
|
require(!useTestClock || devMode) { "Cannot use test clock outside of dev mode" }
|
||||||
require(devModeOptions == null || devMode) { "Cannot use devModeOptions outside of dev mode" }
|
require(devModeOptions == null || devMode) { "Cannot use devModeOptions outside of dev mode" }
|
||||||
require(myLegalName.commonName == null) { "Common name must be null: $myLegalName" }
|
|
||||||
require(minimumPlatformVersion >= 1) { "minimumPlatformVersion cannot be less than 1" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@ package net.corda.node.services.network
|
|||||||
|
|
||||||
import net.corda.cordform.CordformNode
|
import net.corda.cordform.CordformNode
|
||||||
import net.corda.core.crypto.SignedData
|
import net.corda.core.crypto.SignedData
|
||||||
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.services.KeyManagementService
|
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
@ -15,6 +15,7 @@ import rx.Scheduler
|
|||||||
import rx.schedulers.Schedulers
|
import rx.schedulers.Schedulers
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
import java.security.KeyPair
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.streams.toList
|
import kotlin.streams.toList
|
||||||
|
|
||||||
@ -48,13 +49,13 @@ class NodeInfoWatcher(private val nodePath: Path,
|
|||||||
*
|
*
|
||||||
* @param path the path where to write the file, if non-existent it will be created.
|
* @param path the path where to write the file, if non-existent it will be created.
|
||||||
* @param nodeInfo the NodeInfo to serialize.
|
* @param nodeInfo the NodeInfo to serialize.
|
||||||
* @param keyManager a KeyManagementService used to sign the NodeInfo data.
|
* @param signingKey used to sign the NodeInfo data.
|
||||||
*/
|
*/
|
||||||
fun saveToFile(path: Path, nodeInfo: NodeInfo, keyManager: KeyManagementService) {
|
fun saveToFile(path: Path, nodeInfo: NodeInfo, signingKey: KeyPair) {
|
||||||
try {
|
try {
|
||||||
path.createDirectories()
|
path.createDirectories()
|
||||||
val serializedBytes = nodeInfo.serialize()
|
val serializedBytes = nodeInfo.serialize()
|
||||||
val regSig = keyManager.sign(serializedBytes.bytes, nodeInfo.legalIdentities.first().owningKey)
|
val regSig = signingKey.sign(serializedBytes.bytes)
|
||||||
val signedData = SignedData(serializedBytes, regSig)
|
val signedData = SignedData(serializedBytes, regSig)
|
||||||
signedData.serialize().open().copyTo(
|
signedData.serialize().open().copyTo(
|
||||||
path / "${NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX}${serializedBytes.hash}")
|
path / "${NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX}${serializedBytes.hash}")
|
||||||
|
@ -10,9 +10,9 @@ import net.corda.core.internal.bufferUntilSubscribed
|
|||||||
import net.corda.core.internal.concurrent.openFuture
|
import net.corda.core.internal.concurrent.openFuture
|
||||||
import net.corda.core.messaging.DataFeed
|
import net.corda.core.messaging.DataFeed
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
|
import net.corda.core.node.NotaryInfo
|
||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.node.services.NetworkMapCache.MapChange
|
import net.corda.core.node.services.NetworkMapCache.MapChange
|
||||||
import net.corda.core.node.services.NotaryService
|
|
||||||
import net.corda.core.node.services.PartyInfo
|
import net.corda.core.node.services.PartyInfo
|
||||||
import net.corda.core.schemas.NodeInfoSchemaV1
|
import net.corda.core.schemas.NodeInfoSchemaV1
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
@ -32,7 +32,10 @@ import java.util.*
|
|||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
class NetworkMapCacheImpl(networkMapCacheBase: NetworkMapCacheBaseInternal, private val identityService: IdentityService) : NetworkMapCacheBaseInternal by networkMapCacheBase, NetworkMapCacheInternal {
|
class NetworkMapCacheImpl(
|
||||||
|
networkMapCacheBase: NetworkMapCacheBaseInternal,
|
||||||
|
private val identityService: IdentityService
|
||||||
|
) : NetworkMapCacheBaseInternal by networkMapCacheBase, NetworkMapCacheInternal {
|
||||||
init {
|
init {
|
||||||
networkMapCacheBase.allNodes.forEach { it.legalIdentitiesAndCerts.forEach { identityService.verifyAndRegisterIdentity(it) } }
|
networkMapCacheBase.allNodes.forEach { it.legalIdentitiesAndCerts.forEach { identityService.verifyAndRegisterIdentity(it) } }
|
||||||
networkMapCacheBase.changed.subscribe { mapChange ->
|
networkMapCacheBase.changed.subscribe { mapChange ->
|
||||||
@ -57,13 +60,16 @@ class NetworkMapCacheImpl(networkMapCacheBase: NetworkMapCacheBaseInternal, priv
|
|||||||
* Extremely simple in-memory cache of the network map.
|
* Extremely simple in-memory cache of the network map.
|
||||||
*/
|
*/
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
open class PersistentNetworkMapCache(private val database: CordaPersistence, configuration: NodeConfiguration) : SingletonSerializeAsToken(), NetworkMapCacheBaseInternal {
|
open class PersistentNetworkMapCache(
|
||||||
|
private val database: CordaPersistence,
|
||||||
|
val configuration: NodeConfiguration,
|
||||||
|
notaries: List<NotaryInfo>
|
||||||
|
) : SingletonSerializeAsToken(), NetworkMapCacheBaseInternal {
|
||||||
companion object {
|
companion object {
|
||||||
val logger = loggerFor<PersistentNetworkMapCache>()
|
val logger = loggerFor<PersistentNetworkMapCache>()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Small explanation, partyNodes and registeredNodes is left in memory as it was before, because it will be removed in
|
// TODO Cleanup registered and party nodes
|
||||||
// next PR that gets rid of services. These maps are used only for queries by service.
|
|
||||||
protected val registeredNodes: MutableMap<PublicKey, NodeInfo> = Collections.synchronizedMap(HashMap())
|
protected val registeredNodes: MutableMap<PublicKey, NodeInfo> = Collections.synchronizedMap(HashMap())
|
||||||
protected val partyNodes: MutableList<NodeInfo> get() = registeredNodes.map { it.value }.toMutableList()
|
protected val partyNodes: MutableList<NodeInfo> get() = registeredNodes.map { it.value }.toMutableList()
|
||||||
private val _changed = PublishSubject.create<MapChange>()
|
private val _changed = PublishSubject.create<MapChange>()
|
||||||
@ -77,22 +83,9 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence, con
|
|||||||
override val nodeReady: CordaFuture<Void?> get() = _registrationFuture
|
override val nodeReady: CordaFuture<Void?> get() = _registrationFuture
|
||||||
private var _loadDBSuccess: Boolean = false
|
private var _loadDBSuccess: Boolean = false
|
||||||
override val loadDBSuccess get() = _loadDBSuccess
|
override val loadDBSuccess get() = _loadDBSuccess
|
||||||
// TODO From the NetworkMapService redesign doc: Remove the concept of network services.
|
|
||||||
// As a temporary hack, just assume for now that every network has a notary service named "Notary Service" that can be looked up in the map.
|
override val notaryIdentities: List<Party> = notaries.map { it.identity }
|
||||||
// This should eliminate the only required usage of services.
|
private val validatingNotaries = notaries.mapNotNull { if (it.validating) it.identity else null }
|
||||||
// It is ensured on node startup when constructing a notary that the name contains "notary".
|
|
||||||
override val notaryIdentities: List<Party>
|
|
||||||
get() {
|
|
||||||
return partyNodes
|
|
||||||
.flatMap {
|
|
||||||
// TODO: validate notary identity certificates before loading into network map cache.
|
|
||||||
// Notary certificates have to be signed by the doorman directly
|
|
||||||
it.legalIdentities
|
|
||||||
}
|
|
||||||
.filter { it.name.commonName?.startsWith(NotaryService.ID_PREFIX) ?: false }
|
|
||||||
.toSet() // Distinct, because of distributed service nodes
|
|
||||||
.sortedBy { it.name.toString() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private val nodeInfoSerializer = NodeInfoWatcher(configuration.baseDirectory,
|
private val nodeInfoSerializer = NodeInfoWatcher(configuration.baseDirectory,
|
||||||
configuration.additionalNodeInfoPollingFrequencyMsec)
|
configuration.additionalNodeInfoPollingFrequencyMsec)
|
||||||
@ -107,6 +100,8 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence, con
|
|||||||
nodeInfoSerializer.nodeInfoUpdates().subscribe { node -> addNode(node) }
|
nodeInfoSerializer.nodeInfoUpdates().subscribe { node -> addNode(node) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun isValidatingNotary(party: Party): Boolean = party in validatingNotaries
|
||||||
|
|
||||||
override fun getPartyInfo(party: Party): PartyInfo? {
|
override fun getPartyInfo(party: Party): PartyInfo? {
|
||||||
val nodes = database.transaction { queryByIdentityKey(session, party.owningKey) }
|
val nodes = database.transaction { queryByIdentityKey(session, party.owningKey) }
|
||||||
if (nodes.size == 1 && nodes[0].isLegalIdentity(party)) {
|
if (nodes.size == 1 && nodes[0].isLegalIdentity(party)) {
|
||||||
@ -286,7 +281,6 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence, con
|
|||||||
return NodeInfoSchemaV1.PersistentNodeInfo(
|
return NodeInfoSchemaV1.PersistentNodeInfo(
|
||||||
id = 0,
|
id = 0,
|
||||||
addresses = nodeInfo.addresses.map { NodeInfoSchemaV1.DBHostAndPort.fromHostAndPort(it) },
|
addresses = nodeInfo.addresses.map { NodeInfoSchemaV1.DBHostAndPort.fromHostAndPort(it) },
|
||||||
// TODO Another ugly hack with special first identity...
|
|
||||||
legalIdentitiesAndCerts = nodeInfo.legalIdentitiesAndCerts.mapIndexed { idx, elem ->
|
legalIdentitiesAndCerts = nodeInfo.legalIdentitiesAndCerts.mapIndexed { idx, elem ->
|
||||||
NodeInfoSchemaV1.DBPartyAndCertificate(elem, isMain = idx == 0)
|
NodeInfoSchemaV1.DBPartyAndCertificate(elem, isMain = idx == 0)
|
||||||
},
|
},
|
||||||
|
@ -5,10 +5,10 @@ import net.corda.core.crypto.generateKeyPair
|
|||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.cert
|
import net.corda.core.internal.cert
|
||||||
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
object ServiceIdentityGenerator {
|
object ServiceIdentityGenerator {
|
||||||
@ -20,13 +20,12 @@ object ServiceIdentityGenerator {
|
|||||||
* This method should be called *before* any of the nodes are started.
|
* This method should be called *before* any of the nodes are started.
|
||||||
*
|
*
|
||||||
* @param dirs List of node directories to place the generated identity and key pairs in.
|
* @param dirs List of node directories to place the generated identity and key pairs in.
|
||||||
* @param serviceId The service id of the distributed service.
|
* @param serviceName The legal name of the distributed service.
|
||||||
* @param serviceName The legal name of the distributed service, with service id as CN.
|
|
||||||
* @param threshold The threshold for the generated group [CompositeKey].
|
* @param threshold The threshold for the generated group [CompositeKey].
|
||||||
*/
|
*/
|
||||||
// TODO: This needs to write out to the key store, not just files on disk
|
|
||||||
fun generateToDisk(dirs: List<Path>,
|
fun generateToDisk(dirs: List<Path>,
|
||||||
serviceName: CordaX500Name,
|
serviceName: CordaX500Name,
|
||||||
|
serviceId: String,
|
||||||
threshold: Int = 1): Party {
|
threshold: Int = 1): Party {
|
||||||
log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" }
|
log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" }
|
||||||
val keyPairs = (1..dirs.size).map { generateKeyPair() }
|
val keyPairs = (1..dirs.size).map { generateKeyPair() }
|
||||||
@ -39,9 +38,8 @@ object ServiceIdentityGenerator {
|
|||||||
keyPairs.zip(dirs) { keyPair, dir ->
|
keyPairs.zip(dirs) { keyPair, dir ->
|
||||||
val serviceKeyCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, issuer.certificate, issuer.keyPair, serviceName, keyPair.public)
|
val serviceKeyCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, issuer.certificate, issuer.keyPair, serviceName, keyPair.public)
|
||||||
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, issuer.certificate, issuer.keyPair, serviceName, notaryKey)
|
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, issuer.certificate, issuer.keyPair, serviceName, notaryKey)
|
||||||
val certPath = Files.createDirectories(dir / "certificates") / "distributedService.jks"
|
val certPath = (dir / "certificates").createDirectories() / "distributedService.jks"
|
||||||
val keystore = loadOrCreateKeyStore(certPath, "cordacadevpass")
|
val keystore = loadOrCreateKeyStore(certPath, "cordacadevpass")
|
||||||
val serviceId = serviceName.commonName
|
|
||||||
keystore.setCertificateEntry("$serviceId-composite-key", compositeKeyCert.cert)
|
keystore.setCertificateEntry("$serviceId-composite-key", compositeKeyCert.cert)
|
||||||
keystore.setKeyEntry("$serviceId-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), arrayOf(serviceKeyCert.cert, issuer.certificate.cert, rootCert))
|
keystore.setKeyEntry("$serviceId-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), arrayOf(serviceKeyCert.cert, issuer.certificate.cert, rootCert))
|
||||||
keystore.save(certPath, "cordacadevpass")
|
keystore.save(certPath, "cordacadevpass")
|
||||||
|
@ -55,22 +55,22 @@ class CordaRPCOpsImplTest {
|
|||||||
val testJar = "net/corda/node/testing/test.jar"
|
val testJar = "net/corda/node/testing/test.jar"
|
||||||
}
|
}
|
||||||
|
|
||||||
lateinit var mockNet: MockNetwork
|
private lateinit var mockNet: MockNetwork
|
||||||
lateinit var aliceNode: StartedNode<MockNode>
|
private lateinit var aliceNode: StartedNode<MockNode>
|
||||||
lateinit var notaryNode: StartedNode<MockNode>
|
private lateinit var notaryNode: StartedNode<MockNode>
|
||||||
lateinit var notary: Party
|
private lateinit var notary: Party
|
||||||
lateinit var rpc: CordaRPCOps
|
private lateinit var rpc: CordaRPCOps
|
||||||
lateinit var stateMachineUpdates: Observable<StateMachineUpdate>
|
private lateinit var stateMachineUpdates: Observable<StateMachineUpdate>
|
||||||
lateinit var transactions: Observable<SignedTransaction>
|
private lateinit var transactions: Observable<SignedTransaction>
|
||||||
lateinit var vaultTrackCash: Observable<Vault.Update<Cash.State>>
|
private lateinit var vaultTrackCash: Observable<Vault.Update<Cash.State>>
|
||||||
|
|
||||||
private val user = User("user", "pwd", permissions = emptySet())
|
private val user = User("user", "pwd", permissions = emptySet())
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
||||||
aliceNode = mockNet.createNode()
|
|
||||||
notaryNode = mockNet.createNotaryNode(validating = false)
|
notaryNode = mockNet.createNotaryNode(validating = false)
|
||||||
|
aliceNode = mockNet.createNode()
|
||||||
rpc = SecureCordaRPCOps(aliceNode.services, aliceNode.smm, aliceNode.database, aliceNode.services)
|
rpc = SecureCordaRPCOps(aliceNode.services, aliceNode.smm, aliceNode.database, aliceNode.services)
|
||||||
CURRENT_RPC_CONTEXT.set(RpcContext(user))
|
CURRENT_RPC_CONTEXT.set(RpcContext(user))
|
||||||
|
|
||||||
|
@ -615,18 +615,25 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) {
|
|||||||
notaryNode: StartedNode<*>,
|
notaryNode: StartedNode<*>,
|
||||||
vararg extraSigningNodes: StartedNode<*>): Map<SecureHash, SignedTransaction> {
|
vararg extraSigningNodes: StartedNode<*>): Map<SecureHash, SignedTransaction> {
|
||||||
|
|
||||||
|
val notaryParty = notaryNode.info.legalIdentities[0]
|
||||||
val signed = wtxToSign.map {
|
val signed = wtxToSign.map {
|
||||||
val id = it.id
|
val id = it.id
|
||||||
val sigs = mutableListOf<TransactionSignature>()
|
val sigs = mutableListOf<TransactionSignature>()
|
||||||
val nodeKey = node.info.chooseIdentity().owningKey
|
val nodeKey = node.info.chooseIdentity().owningKey
|
||||||
sigs.add(node.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(nodeKey).schemeNumberID)), nodeKey))
|
sigs += node.services.keyManagementService.sign(
|
||||||
sigs.add(notaryNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1,
|
SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(nodeKey).schemeNumberID)),
|
||||||
Crypto.findSignatureScheme(notaryNode.info.legalIdentities[1].owningKey).schemeNumberID)), notaryNode.info.legalIdentities[1].owningKey))
|
nodeKey
|
||||||
|
)
|
||||||
|
sigs += notaryNode.services.keyManagementService.sign(
|
||||||
|
SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(notaryParty.owningKey).schemeNumberID)),
|
||||||
|
notaryParty.owningKey
|
||||||
|
)
|
||||||
extraSigningNodes.forEach { currentNode ->
|
extraSigningNodes.forEach { currentNode ->
|
||||||
sigs.add(currentNode.services.keyManagementService.sign(
|
sigs += currentNode.services.keyManagementService.sign(
|
||||||
SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(currentNode.info.chooseIdentity().owningKey).schemeNumberID)),
|
SignableData(id, SignatureMetadata(
|
||||||
|
1,
|
||||||
|
Crypto.findSignatureScheme(currentNode.info.chooseIdentity().owningKey).schemeNumberID)),
|
||||||
currentNode.info.chooseIdentity().owningKey)
|
currentNode.info.chooseIdentity().owningKey)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
SignedTransaction(it, sigs)
|
SignedTransaction(it, sigs)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package net.corda.node.services
|
package net.corda.node.services
|
||||||
|
|
||||||
|
import com.nhaarman.mockito_kotlin.doReturn
|
||||||
|
import com.nhaarman.mockito_kotlin.whenever
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.flows.NotaryChangeFlow
|
import net.corda.core.flows.NotaryChangeFlow
|
||||||
@ -13,8 +15,12 @@ import net.corda.core.transactions.WireTransaction
|
|||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.testing.*
|
import net.corda.node.services.config.NotaryConfig
|
||||||
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
|
import net.corda.testing.chooseIdentity
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
|
import net.corda.testing.dummyCommand
|
||||||
|
import net.corda.testing.getTestPartyAndCertificate
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import net.corda.testing.node.MockNodeParameters
|
import net.corda.testing.node.MockNodeParameters
|
||||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||||
@ -27,24 +33,32 @@ import kotlin.test.assertEquals
|
|||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class NotaryChangeTests {
|
class NotaryChangeTests {
|
||||||
lateinit var mockNet: MockNetwork
|
private lateinit var mockNet: MockNetwork
|
||||||
lateinit var oldNotaryNode: StartedNode<MockNetwork.MockNode>
|
private lateinit var oldNotaryNode: StartedNode<MockNetwork.MockNode>
|
||||||
lateinit var newNotaryNode: StartedNode<MockNetwork.MockNode>
|
private lateinit var clientNodeA: StartedNode<MockNetwork.MockNode>
|
||||||
lateinit var clientNodeA: StartedNode<MockNetwork.MockNode>
|
private lateinit var clientNodeB: StartedNode<MockNetwork.MockNode>
|
||||||
lateinit var clientNodeB: StartedNode<MockNetwork.MockNode>
|
private lateinit var newNotaryParty: Party
|
||||||
lateinit var newNotaryParty: Party
|
private lateinit var oldNotaryParty: Party
|
||||||
lateinit var oldNotaryParty: Party
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts"))
|
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts"))
|
||||||
oldNotaryNode = mockNet.createNotaryNode(MockNodeParameters(legalName = DUMMY_NOTARY.name))
|
val (oldNotaryNode, newNotaryNode) = listOf(
|
||||||
|
createUnstartedNotary(DUMMY_NOTARY.name),
|
||||||
|
createUnstartedNotary(DUMMY_NOTARY.name.copy(organisation = "Dummy Notary 2"))
|
||||||
|
).map { it.start() }
|
||||||
|
this.oldNotaryNode = oldNotaryNode
|
||||||
clientNodeA = mockNet.createNode()
|
clientNodeA = mockNet.createNode()
|
||||||
clientNodeB = mockNet.createNode()
|
clientNodeB = mockNet.createNode()
|
||||||
newNotaryNode = mockNet.createNotaryNode(MockNodeParameters(legalName = DUMMY_NOTARY.name.copy(organisation = "Dummy Notary 2")))
|
oldNotaryParty = newNotaryNode.services.networkMapCache.getNotary(DUMMY_NOTARY.name)!!
|
||||||
mockNet.runNetwork() // Clear network map registration messages
|
newNotaryParty = newNotaryNode.services.networkMapCache.getNotary(DUMMY_NOTARY.name.copy(organisation = "Dummy Notary 2"))!!
|
||||||
oldNotaryParty = newNotaryNode.services.networkMapCache.getNotary(DUMMY_NOTARY_SERVICE_NAME)!!
|
}
|
||||||
newNotaryParty = newNotaryNode.services.networkMapCache.getNotary(DUMMY_NOTARY_SERVICE_NAME.copy(organisation = "Dummy Notary 2"))!!
|
|
||||||
|
private fun createUnstartedNotary(name: CordaX500Name): MockNetwork.MockNode {
|
||||||
|
return mockNet.createUnstartedNode(MockNodeParameters(
|
||||||
|
legalName = name,
|
||||||
|
configOverrides = { doReturn(NotaryConfig(validating = true)).whenever(it).notary }
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -42,6 +42,7 @@ class ArtemisMessagingTests {
|
|||||||
companion object {
|
companion object {
|
||||||
const val TOPIC = "platform.self"
|
const val TOPIC = "platform.self"
|
||||||
}
|
}
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val testSerialization = SerializationEnvironmentRule()
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
@ -50,21 +51,21 @@ class ArtemisMessagingTests {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val temporaryFolder = TemporaryFolder()
|
val temporaryFolder = TemporaryFolder()
|
||||||
|
|
||||||
val serverPort = freePort()
|
private val serverPort = freePort()
|
||||||
val rpcPort = freePort()
|
private val rpcPort = freePort()
|
||||||
val identity = generateKeyPair()
|
private val identity = generateKeyPair()
|
||||||
|
|
||||||
lateinit var config: NodeConfiguration
|
private lateinit var config: NodeConfiguration
|
||||||
lateinit var database: CordaPersistence
|
private lateinit var database: CordaPersistence
|
||||||
lateinit var userService: RPCUserService
|
private lateinit var userService: RPCUserService
|
||||||
lateinit var networkMapRegistrationFuture: CordaFuture<Unit>
|
private lateinit var networkMapRegistrationFuture: CordaFuture<Unit>
|
||||||
|
|
||||||
var messagingClient: NodeMessagingClient? = null
|
private var messagingClient: NodeMessagingClient? = null
|
||||||
var messagingServer: ArtemisMessagingServer? = null
|
private var messagingServer: ArtemisMessagingServer? = null
|
||||||
|
|
||||||
lateinit var networkMapCache: NetworkMapCacheImpl
|
private lateinit var networkMapCache: NetworkMapCacheImpl
|
||||||
|
|
||||||
val rpcOps = object : RPCOps {
|
private val rpcOps = object : RPCOps {
|
||||||
override val protocolVersion: Int get() = throw UnsupportedOperationException()
|
override val protocolVersion: Int get() = throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +79,7 @@ class ArtemisMessagingTests {
|
|||||||
LogHelper.setLevel(PersistentUniquenessProvider::class)
|
LogHelper.setLevel(PersistentUniquenessProvider::class)
|
||||||
database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), ::makeTestIdentityService)
|
database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), ::makeTestIdentityService)
|
||||||
networkMapRegistrationFuture = doneFuture(Unit)
|
networkMapRegistrationFuture = doneFuture(Unit)
|
||||||
networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, config), rigorousMock())
|
networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, config, emptyList()), rigorousMock())
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -72,18 +72,14 @@ class FlowFrameworkTests {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun start() {
|
fun start() {
|
||||||
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts"))
|
mockNet = MockNetwork(
|
||||||
|
servicePeerAllocationStrategy = RoundRobin(),
|
||||||
|
cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts"))
|
||||||
|
val notary = mockNet.createNotaryNode()
|
||||||
aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME))
|
aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME))
|
||||||
bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME))
|
bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME))
|
||||||
mockNet.runNetwork()
|
|
||||||
|
|
||||||
// We intentionally create our own notary and ignore the one provided by the network
|
|
||||||
// Note that these notaries don't operate correctly as they don't share their state. They are only used for testing
|
|
||||||
// service addressing.
|
|
||||||
val notary = mockNet.createNotaryNode()
|
|
||||||
|
|
||||||
receivedSessionMessagesObservable().forEach { receivedSessionMessages += it }
|
receivedSessionMessagesObservable().forEach { receivedSessionMessages += it }
|
||||||
mockNet.runNetwork()
|
|
||||||
|
|
||||||
// Extract identities
|
// Extract identities
|
||||||
alice = aliceNode.info.singleIdentity()
|
alice = aliceNode.info.singleIdentity()
|
||||||
|
@ -27,10 +27,10 @@ class AttachmentDemoTest {
|
|||||||
invokeRpc(CordaRPCOps::wellKnownPartyFromX500Name),
|
invokeRpc(CordaRPCOps::wellKnownPartyFromX500Name),
|
||||||
invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed)
|
invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed)
|
||||||
)))
|
)))
|
||||||
val (nodeA, nodeB) = listOf(
|
val (_, nodeA, nodeB) = listOf(
|
||||||
|
startNotaryNode(DUMMY_NOTARY.name, validating = false),
|
||||||
startNode(providedName = DUMMY_BANK_A.name, rpcUsers = demoUser, maximumHeapSize = "1g"),
|
startNode(providedName = DUMMY_BANK_A.name, rpcUsers = demoUser, maximumHeapSize = "1g"),
|
||||||
startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser, maximumHeapSize = "1g"),
|
startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser, maximumHeapSize = "1g"))
|
||||||
startNotaryNode(DUMMY_NOTARY.name, validating = false))
|
|
||||||
.map { it.getOrThrow() }
|
.map { it.getOrThrow() }
|
||||||
startWebserver(nodeB).getOrThrow()
|
startWebserver(nodeB).getOrThrow()
|
||||||
|
|
||||||
|
@ -2,23 +2,26 @@ package net.corda.bank
|
|||||||
|
|
||||||
import net.corda.bank.api.BankOfCordaClientApi
|
import net.corda.bank.api.BankOfCordaClientApi
|
||||||
import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams
|
import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams
|
||||||
|
import net.corda.core.internal.concurrent.transpose
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.testing.BOC
|
import net.corda.testing.BOC
|
||||||
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.notary
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class BankOfCordaHttpAPITest {
|
class BankOfCordaHttpAPITest {
|
||||||
@Test
|
@Test
|
||||||
fun `issuer flow via Http`() {
|
fun `issuer flow via Http`() {
|
||||||
driver(extraCordappPackagesToScan = listOf("net.corda.finance"), dsl = {
|
driver(isDebug = true, extraCordappPackagesToScan = listOf("net.corda.finance")) {
|
||||||
val bigCorpNodeFuture = startNode(providedName = BIGCORP_LEGAL_NAME)
|
val (_, bocNode) = listOf(
|
||||||
val nodeBankOfCordaFuture = startNotaryNode(BOC.name, validating = false)
|
startNotaryNode(providedName = DUMMY_NOTARY.name),
|
||||||
val (nodeBankOfCorda) = listOf(nodeBankOfCordaFuture, bigCorpNodeFuture).map { it.getOrThrow() }
|
startNode(providedName = BOC.name),
|
||||||
val nodeBankOfCordaApiAddr = startWebserver(nodeBankOfCorda).getOrThrow().listenAddress
|
startNode(providedName = BIGCORP_LEGAL_NAME)
|
||||||
val notaryName = notary().node.nodeInfo.legalIdentities[1].name
|
).transpose().getOrThrow()
|
||||||
assertTrue(BankOfCordaClientApi(nodeBankOfCordaApiAddr).requestWebIssue(IssueRequestParams(1000, "USD", BIGCORP_LEGAL_NAME, "1", BOC.name, notaryName)))
|
val bocApiAddress = startWebserver(bocNode).getOrThrow().listenAddress
|
||||||
}, isDebug = true)
|
val issueRequestParams = IssueRequestParams(1000, "USD", BIGCORP_LEGAL_NAME, "1", BOC.name, DUMMY_NOTARY.name)
|
||||||
|
assertTrue(BankOfCordaClientApi(bocApiAddress).requestWebIssue(issueRequestParams))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,9 +28,10 @@ class BankOfCordaRPCClientTest {
|
|||||||
val bocManager = User("bocManager", "password1", permissions = setOf(
|
val bocManager = User("bocManager", "password1", permissions = setOf(
|
||||||
startFlow<CashIssueAndPaymentFlow>()) + commonPermissions)
|
startFlow<CashIssueAndPaymentFlow>()) + commonPermissions)
|
||||||
val bigCorpCFO = User("bigCorpCFO", "password2", permissions = emptySet<String>() + commonPermissions)
|
val bigCorpCFO = User("bigCorpCFO", "password2", permissions = emptySet<String>() + commonPermissions)
|
||||||
val nodeBankOfCordaFuture = startNotaryNode(BOC.name, rpcUsers = listOf(bocManager), validating = false)
|
val (nodeBankOfCorda, nodeBigCorporation) = listOf(
|
||||||
val nodeBigCorporationFuture = startNode(providedName = BIGCORP_LEGAL_NAME, rpcUsers = listOf(bigCorpCFO))
|
startNotaryNode(BOC.name, rpcUsers = listOf(bocManager), validating = false),
|
||||||
val (nodeBankOfCorda, nodeBigCorporation) = listOf(nodeBankOfCordaFuture, nodeBigCorporationFuture).map { it.getOrThrow() }
|
startNode(providedName = BIGCORP_LEGAL_NAME, rpcUsers = listOf(bigCorpCFO))
|
||||||
|
).map { it.getOrThrow() }
|
||||||
|
|
||||||
// Bank of Corda RPC Client
|
// Bank of Corda RPC Client
|
||||||
val bocClient = nodeBankOfCorda.rpcClientToNode()
|
val bocClient = nodeBankOfCorda.rpcClientToNode()
|
||||||
|
@ -57,7 +57,7 @@ private class BankOfCordaDriver {
|
|||||||
try {
|
try {
|
||||||
when (role) {
|
when (role) {
|
||||||
Role.ISSUER -> {
|
Role.ISSUER -> {
|
||||||
driver(dsl = {
|
driver(isDebug = true, extraCordappPackagesToScan = listOf("net.corda.finance.contracts.asset")) {
|
||||||
startNotaryNode(providedName = DUMMY_NOTARY.name, validating = true)
|
startNotaryNode(providedName = DUMMY_NOTARY.name, validating = true)
|
||||||
val bankUser = User(
|
val bankUser = User(
|
||||||
BANK_USERNAME,
|
BANK_USERNAME,
|
||||||
@ -79,7 +79,7 @@ private class BankOfCordaDriver {
|
|||||||
startNode(providedName = BIGCORP_LEGAL_NAME, rpcUsers = listOf(bigCorpUser))
|
startNode(providedName = BIGCORP_LEGAL_NAME, rpcUsers = listOf(bigCorpUser))
|
||||||
startWebserver(bankOfCorda.get())
|
startWebserver(bankOfCorda.get())
|
||||||
waitForAllNodesToFinish()
|
waitForAllNodesToFinish()
|
||||||
}, isDebug = true, extraCordappPackagesToScan = listOf("net.corda.finance.contracts.asset"))
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val requestParams = IssueRequestParams(options.valueOf(quantity), options.valueOf(currency), BIGCORP_LEGAL_NAME,
|
val requestParams = IssueRequestParams(options.valueOf(quantity), options.valueOf(currency), BIGCORP_LEGAL_NAME,
|
||||||
|
@ -205,7 +205,7 @@ class NodeInterestRatesTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `network tearoff`() {
|
fun `network tearoff`() {
|
||||||
val mockNet = MockNetwork(initialiseSerialization = false, cordappPackages = listOf("net.corda.finance.contracts", "net.corda.irs"))
|
val mockNet = MockNetwork(initialiseSerialization = false, cordappPackages = listOf("net.corda.finance.contracts", "net.corda.irs"))
|
||||||
val n1 = mockNet.createNotaryNode()
|
val aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||||
val oracleNode = mockNet.createNode().apply {
|
val oracleNode = mockNet.createNode().apply {
|
||||||
internals.registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java)
|
internals.registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java)
|
||||||
internals.registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java)
|
internals.registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java)
|
||||||
@ -218,7 +218,7 @@ class NodeInterestRatesTest {
|
|||||||
val flow = FilteredRatesFlow(tx, oracleNode.info.chooseIdentity(), fixOf, BigDecimal("0.675"), BigDecimal("0.1"))
|
val flow = FilteredRatesFlow(tx, oracleNode.info.chooseIdentity(), fixOf, BigDecimal("0.675"), BigDecimal("0.1"))
|
||||||
LogHelper.setLevel("rates")
|
LogHelper.setLevel("rates")
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
val future = n1.services.startFlow(flow).resultFuture
|
val future = aliceNode.services.startFlow(flow).resultFuture
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
future.getOrThrow()
|
future.getOrThrow()
|
||||||
// We should now have a valid fix of our tx from the oracle.
|
// We should now have a valid fix of our tx from the oracle.
|
||||||
|
@ -140,7 +140,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
|
|||||||
node1.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java)
|
node1.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java)
|
||||||
node2.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java)
|
node2.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java)
|
||||||
|
|
||||||
val notaryId = notary.info.legalIdentities[1]
|
val notaryId = notary.info.legalIdentities[0]
|
||||||
|
|
||||||
@InitiatingFlow
|
@InitiatingFlow
|
||||||
class StartDealFlow(val otherParty: Party,
|
class StartDealFlow(val otherParty: Party,
|
||||||
|
@ -5,6 +5,7 @@ import net.corda.cordform.CordformDefinition
|
|||||||
import net.corda.cordform.CordformNode
|
import net.corda.cordform.CordformNode
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.core.node.services.NotaryService
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.services.config.BFTSMaRtConfiguration
|
import net.corda.node.services.config.BFTSMaRtConfiguration
|
||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
@ -13,11 +14,7 @@ import net.corda.node.services.transactions.minCorrectReplicas
|
|||||||
import net.corda.node.utilities.ServiceIdentityGenerator
|
import net.corda.node.utilities.ServiceIdentityGenerator
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.BOB
|
import net.corda.testing.BOB
|
||||||
import net.corda.testing.internal.demorun.name
|
import net.corda.testing.internal.demorun.*
|
||||||
import net.corda.testing.internal.demorun.node
|
|
||||||
import net.corda.testing.internal.demorun.notary
|
|
||||||
import net.corda.testing.internal.demorun.rpcUsers
|
|
||||||
import net.corda.testing.internal.demorun.runNodes
|
|
||||||
|
|
||||||
fun main(args: Array<String>) = BFTNotaryCordform().runNodes()
|
fun main(args: Array<String>) = BFTNotaryCordform().runNodes()
|
||||||
|
|
||||||
@ -66,6 +63,10 @@ class BFTNotaryCordform : CordformDefinition("build" / "notary-demo-nodes") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun setup(context: CordformContext) {
|
override fun setup(context: CordformContext) {
|
||||||
ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it.toString()) }, clusterName, minCorrectReplicas(clusterSize))
|
ServiceIdentityGenerator.generateToDisk(
|
||||||
|
notaryNames.map { context.baseDirectory(it.toString()) },
|
||||||
|
clusterName,
|
||||||
|
NotaryService.constructId(validating = false, bft = true),
|
||||||
|
minCorrectReplicas(clusterSize))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import net.corda.cordform.CordformDefinition
|
|||||||
import net.corda.cordform.CordformNode
|
import net.corda.cordform.CordformNode
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.core.node.services.NotaryService
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
import net.corda.node.services.config.RaftConfig
|
import net.corda.node.services.config.RaftConfig
|
||||||
@ -58,6 +59,9 @@ class RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun setup(context: CordformContext) {
|
override fun setup(context: CordformContext) {
|
||||||
ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it.toString()) }, clusterName)
|
ServiceIdentityGenerator.generateToDisk(
|
||||||
|
notaryNames.map { context.baseDirectory(it.toString()) },
|
||||||
|
clusterName,
|
||||||
|
NotaryService.constructId(validating = true, raft = true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,12 +30,12 @@ class TraderDemoTest {
|
|||||||
startFlow<CommercialPaperIssueFlow>(),
|
startFlow<CommercialPaperIssueFlow>(),
|
||||||
all()))
|
all()))
|
||||||
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance")) {
|
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance")) {
|
||||||
val (nodeA, nodeB, bankNode) = listOf(
|
val (_, nodeA, nodeB, bankNode) = listOf(
|
||||||
|
startNotaryNode(DUMMY_NOTARY.name, validating = false),
|
||||||
startNode(providedName = DUMMY_BANK_A.name, rpcUsers = listOf(demoUser)),
|
startNode(providedName = DUMMY_BANK_A.name, rpcUsers = listOf(demoUser)),
|
||||||
startNode(providedName = DUMMY_BANK_B.name, rpcUsers = listOf(demoUser)),
|
startNode(providedName = DUMMY_BANK_B.name, rpcUsers = listOf(demoUser)),
|
||||||
startNode(providedName = BOC.name, rpcUsers = listOf(bankUser)),
|
startNode(providedName = BOC.name, rpcUsers = listOf(bankUser))
|
||||||
startNotaryNode(DUMMY_NOTARY.name, validating = false))
|
).map { (it.getOrThrow() as NodeHandle.InProcess).node }
|
||||||
.map { (it.getOrThrow() as NodeHandle.InProcess).node }
|
|
||||||
|
|
||||||
nodeA.internals.registerInitiatedFlow(BuyerFlow::class.java)
|
nodeA.internals.registerInitiatedFlow(BuyerFlow::class.java)
|
||||||
val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map {
|
val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map {
|
||||||
|
@ -15,7 +15,7 @@ import net.corda.testing.driver.DriverDSLExposedInterface
|
|||||||
/**
|
/**
|
||||||
* A simple wrapper for objects provided by the integration test driver DSL. The fields are lazy so
|
* A simple wrapper for objects provided by the integration test driver DSL. The fields are lazy so
|
||||||
* node construction won't start until you access the members. You can get one of these from the
|
* node construction won't start until you access the members. You can get one of these from the
|
||||||
* [alice], [bob] and [aliceBobAndNotary] functions.
|
* [alice], [bob] and [notaryAliceAndBob] functions.
|
||||||
*/
|
*/
|
||||||
class PredefinedTestNode internal constructor(party: Party, driver: DriverDSLExposedInterface, ifNotaryIsValidating: Boolean?) {
|
class PredefinedTestNode internal constructor(party: Party, driver: DriverDSLExposedInterface, ifNotaryIsValidating: Boolean?) {
|
||||||
val rpcUsers = listOf(User("admin", "admin", setOf("ALL"))) // TODO: Randomize?
|
val rpcUsers = listOf(User("admin", "admin", setOf("ALL"))) // TODO: Randomize?
|
||||||
@ -56,10 +56,10 @@ fun DriverDSLExposedInterface.notary(): PredefinedTestNode = PredefinedTestNode(
|
|||||||
* Returns plain, entirely stock nodes pre-configured with the [ALICE], [BOB] and [DUMMY_NOTARY] X.500 names in that
|
* Returns plain, entirely stock nodes pre-configured with the [ALICE], [BOB] and [DUMMY_NOTARY] X.500 names in that
|
||||||
* order. They have been started up in parallel and are now ready to use.
|
* order. They have been started up in parallel and are now ready to use.
|
||||||
*/
|
*/
|
||||||
fun DriverDSLExposedInterface.aliceBobAndNotary(): List<PredefinedTestNode> {
|
fun DriverDSLExposedInterface.notaryAliceAndBob(): List<PredefinedTestNode> {
|
||||||
|
val notary = notary()
|
||||||
val alice = alice()
|
val alice = alice()
|
||||||
val bob = bob()
|
val bob = bob()
|
||||||
val notary = notary()
|
listOf(notary.nodeFuture, alice.nodeFuture, bob.nodeFuture).transpose().get()
|
||||||
listOf(alice.nodeFuture, bob.nodeFuture, notary.nodeFuture).transpose().get()
|
|
||||||
return listOf(alice, bob, notary)
|
return listOf(alice, bob, notary)
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,6 @@ fun testNodeConfiguration(
|
|||||||
return rigorousMock<MockableNodeConfiguration>().also {
|
return rigorousMock<MockableNodeConfiguration>().also {
|
||||||
doReturn(baseDirectory).whenever(it).baseDirectory
|
doReturn(baseDirectory).whenever(it).baseDirectory
|
||||||
doReturn(myLegalName).whenever(it).myLegalName
|
doReturn(myLegalName).whenever(it).myLegalName
|
||||||
doReturn(1).whenever(it).minimumPlatformVersion
|
|
||||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||||
doReturn("trustpass").whenever(it).trustStorePassword
|
doReturn("trustpass").whenever(it).trustStorePassword
|
||||||
doReturn(emptyList<User>()).whenever(it).rpcUsers
|
doReturn(emptyList<User>()).whenever(it).rpcUsers
|
||||||
|
@ -13,13 +13,13 @@ import net.corda.core.concurrent.CordaFuture
|
|||||||
import net.corda.core.concurrent.firstOf
|
import net.corda.core.concurrent.firstOf
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.ThreadBox
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.internal.concurrent.*
|
import net.corda.core.internal.concurrent.*
|
||||||
import net.corda.core.internal.div
|
|
||||||
import net.corda.core.internal.times
|
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
|
import net.corda.core.node.NotaryInfo
|
||||||
import net.corda.core.node.services.NetworkMapCache
|
import net.corda.core.node.services.NetworkMapCache
|
||||||
|
import net.corda.core.node.services.NotaryService
|
||||||
import net.corda.core.toFuture
|
import net.corda.core.toFuture
|
||||||
import net.corda.core.utilities.*
|
import net.corda.core.utilities.*
|
||||||
import net.corda.node.internal.Node
|
import net.corda.node.internal.Node
|
||||||
@ -36,6 +36,8 @@ import net.corda.nodeapi.internal.addShutdownHook
|
|||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.BOB
|
import net.corda.testing.BOB
|
||||||
import net.corda.testing.DUMMY_BANK_A
|
import net.corda.testing.DUMMY_BANK_A
|
||||||
|
import net.corda.testing.common.internal.NetworkParametersCopier
|
||||||
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.initialiseTestSerialization
|
import net.corda.testing.initialiseTestSerialization
|
||||||
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
@ -43,10 +45,10 @@ import okhttp3.Request
|
|||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.observables.ConnectableObservable
|
import rx.observables.ConnectableObservable
|
||||||
import java.io.File
|
|
||||||
import java.net.*
|
import java.net.*
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
|
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.ZoneOffset.UTC
|
import java.time.ZoneOffset.UTC
|
||||||
@ -59,6 +61,7 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
|
|||||||
import java.util.concurrent.TimeUnit.SECONDS
|
import java.util.concurrent.TimeUnit.SECONDS
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
|
||||||
@ -69,7 +72,6 @@ import kotlin.concurrent.thread
|
|||||||
*
|
*
|
||||||
* TODO this file is getting way too big, it should be split into several files.
|
* TODO this file is getting way too big, it should be split into several files.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private val log: Logger = loggerFor<DriverDSL>()
|
private val log: Logger = loggerFor<DriverDSL>()
|
||||||
|
|
||||||
private val DEFAULT_POLL_INTERVAL = 500.millis
|
private val DEFAULT_POLL_INTERVAL = 500.millis
|
||||||
@ -87,7 +89,7 @@ private val DRIVER_REQUIRED_PERMISSIONS = setOf(
|
|||||||
*/
|
*/
|
||||||
interface DriverDSLExposedInterface : CordformContext {
|
interface DriverDSLExposedInterface : CordformContext {
|
||||||
/**
|
/**
|
||||||
* Starts a [net.corda.node.internal.Node] in a separate process.
|
* Start a node.
|
||||||
*
|
*
|
||||||
* @param defaultParameters The default parameters for the node. Allows the node to be configured in builder style
|
* @param defaultParameters The default parameters for the node. Allows the node to be configured in builder style
|
||||||
* when called from Java code.
|
* when called from Java code.
|
||||||
@ -97,7 +99,7 @@ interface DriverDSLExposedInterface : CordformContext {
|
|||||||
* @param rpcUsers List of users who are authorised to use the RPC system. Defaults to empty list.
|
* @param rpcUsers List of users who are authorised to use the RPC system. Defaults to empty list.
|
||||||
* @param startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
* @param startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
||||||
* in. If null the Driver-level value will be used.
|
* in. If null the Driver-level value will be used.
|
||||||
* @return The [NodeInfo] of the started up node retrieved from the network map service.
|
* @return A [CordaFuture] on the [NodeHandle] to the node. The future will complete when the node is available.
|
||||||
*/
|
*/
|
||||||
fun startNode(
|
fun startNode(
|
||||||
defaultParameters: NodeParameters = NodeParameters(),
|
defaultParameters: NodeParameters = NodeParameters(),
|
||||||
@ -113,7 +115,6 @@ interface DriverDSLExposedInterface : CordformContext {
|
|||||||
rpcUsers: List<User> = emptyList(),
|
rpcUsers: List<User> = emptyList(),
|
||||||
verifierType: VerifierType = VerifierType.InMemory,
|
verifierType: VerifierType = VerifierType.InMemory,
|
||||||
customOverrides: Map<String, Any?> = emptyMap(),
|
customOverrides: Map<String, Any?> = emptyMap(),
|
||||||
//TODO Switch the default value
|
|
||||||
validating: Boolean = true): CordaFuture<NodeHandle>
|
validating: Boolean = true): CordaFuture<NodeHandle>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,9 +123,7 @@ interface DriverDSLExposedInterface : CordformContext {
|
|||||||
* @param parameters The default parameters for the driver.
|
* @param parameters The default parameters for the driver.
|
||||||
* @return The value returned in the [dsl] closure.
|
* @return The value returned in the [dsl] closure.
|
||||||
*/
|
*/
|
||||||
fun startNode(parameters: NodeParameters): CordaFuture<NodeHandle> {
|
fun startNode(parameters: NodeParameters): CordaFuture<NodeHandle> = startNode(defaultParameters = parameters)
|
||||||
return startNode(defaultParameters = parameters)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun startNodes(
|
fun startNodes(
|
||||||
nodes: List<CordformNode>,
|
nodes: List<CordformNode>,
|
||||||
@ -323,7 +322,6 @@ fun <A> driver(
|
|||||||
systemProperties: Map<String, String> = defaultParameters.systemProperties,
|
systemProperties: Map<String, String> = defaultParameters.systemProperties,
|
||||||
useTestClock: Boolean = defaultParameters.useTestClock,
|
useTestClock: Boolean = defaultParameters.useTestClock,
|
||||||
initialiseSerialization: Boolean = defaultParameters.initialiseSerialization,
|
initialiseSerialization: Boolean = defaultParameters.initialiseSerialization,
|
||||||
|
|
||||||
startNodesInProcess: Boolean = defaultParameters.startNodesInProcess,
|
startNodesInProcess: Boolean = defaultParameters.startNodesInProcess,
|
||||||
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
||||||
dsl: DriverDSLExposedInterface.() -> A
|
dsl: DriverDSLExposedInterface.() -> A
|
||||||
@ -466,7 +464,8 @@ fun getTimestampAsDirectoryName(): String {
|
|||||||
return DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(UTC).format(Instant.now())
|
return DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(UTC).format(Instant.now())
|
||||||
}
|
}
|
||||||
|
|
||||||
class ListenProcessDeathException(hostAndPort: NetworkHostAndPort, listenProcess: Process) : CordaException("The process that was expected to listen on $hostAndPort has died with status: ${listenProcess.exitValue()}")
|
class ListenProcessDeathException(hostAndPort: NetworkHostAndPort, listenProcess: Process) :
|
||||||
|
CordaException("The process that was expected to listen on $hostAndPort has died with status: ${listenProcess.exitValue()}")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws ListenProcessDeathException if [listenProcess] dies before the check succeeds, i.e. the check can't succeed as intended.
|
* @throws ListenProcessDeathException if [listenProcess] dies before the check succeeds, i.e. the check can't succeed as intended.
|
||||||
@ -657,6 +656,7 @@ class DriverDSL(
|
|||||||
private val nodeInfoFilesCopier = NodeInfoFilesCopier()
|
private val nodeInfoFilesCopier = NodeInfoFilesCopier()
|
||||||
// Map from a nodes legal name to an observable emitting the number of nodes in its network map.
|
// Map from a nodes legal name to an observable emitting the number of nodes in its network map.
|
||||||
private val countObservables = mutableMapOf<CordaX500Name, Observable<Int>>()
|
private val countObservables = mutableMapOf<CordaX500Name, Observable<Int>>()
|
||||||
|
private var networkParameters: NetworkParametersCopier? = null
|
||||||
|
|
||||||
class State {
|
class State {
|
||||||
val processes = ArrayList<CordaFuture<Process>>()
|
val processes = ArrayList<CordaFuture<Process>>()
|
||||||
@ -749,6 +749,7 @@ class DriverDSL(
|
|||||||
verifierType: VerifierType,
|
verifierType: VerifierType,
|
||||||
customOverrides: Map<String, Any?>,
|
customOverrides: Map<String, Any?>,
|
||||||
validating: Boolean): CordaFuture<NodeHandle> {
|
validating: Boolean): CordaFuture<NodeHandle> {
|
||||||
|
createNetworkParameters(listOf(providedName), providedName, validating, "identity")
|
||||||
val config = customOverrides + NotaryConfig(validating).toConfigMap()
|
val config = customOverrides + NotaryConfig(validating).toConfigMap()
|
||||||
return startNode(providedName = providedName, rpcUsers = rpcUsers, verifierType = verifierType, customOverrides = config)
|
return startNode(providedName = providedName, rpcUsers = rpcUsers, verifierType = verifierType, customOverrides = config)
|
||||||
}
|
}
|
||||||
@ -788,21 +789,28 @@ class DriverDSL(
|
|||||||
return config.toConfigMap()
|
return config.toConfigMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
val nodeNames = (0 until clusterSize).map { CordaX500Name("Notary Service $it", "Zurich", "CH") }
|
require(clusterSize > 0)
|
||||||
val paths = nodeNames.map { baseDirectory(it) }
|
|
||||||
ServiceIdentityGenerator.generateToDisk(paths, notaryName)
|
val nodeNames = (0 until clusterSize).map { notaryName.copy(organisation = "${notaryName.organisation}-$it") }
|
||||||
|
val notaryIdentity = createNetworkParameters(
|
||||||
|
nodeNames,
|
||||||
|
notaryName,
|
||||||
|
validating = true,
|
||||||
|
serviceId = NotaryService.constructId(validating = true, raft = true))
|
||||||
|
|
||||||
val clusterAddress = portAllocation.nextHostAndPort()
|
val clusterAddress = portAllocation.nextHostAndPort()
|
||||||
|
|
||||||
// Start the first node that will bootstrap the cluster
|
// Start the first node that will bootstrap the cluster
|
||||||
val firstNotaryFuture = startNode(
|
val firstNotaryFuture = startNode(
|
||||||
providedName = nodeNames.first(),
|
providedName = nodeNames[0],
|
||||||
rpcUsers = rpcUsers,
|
rpcUsers = rpcUsers,
|
||||||
verifierType = verifierType,
|
verifierType = verifierType,
|
||||||
customOverrides = notaryConfig(clusterAddress) + mapOf(
|
customOverrides = notaryConfig(clusterAddress) + mapOf(
|
||||||
"database.serverNameTablePrefix" to if (nodeNames.isNotEmpty()) nodeNames.first().toString().replace(Regex("[^0-9A-Za-z]+"), "") else ""
|
"database.serverNameTablePrefix" to nodeNames[0].toString().replace(Regex("[^0-9A-Za-z]+"), "")
|
||||||
),
|
),
|
||||||
startInSameProcess = startInSameProcess
|
startInSameProcess = startInSameProcess
|
||||||
)
|
)
|
||||||
|
|
||||||
// All other nodes will join the cluster
|
// All other nodes will join the cluster
|
||||||
val restNotaryFutures = nodeNames.drop(1).map {
|
val restNotaryFutures = nodeNames.drop(1).map {
|
||||||
val nodeAddress = portAllocation.nextHostAndPort()
|
val nodeAddress = portAllocation.nextHostAndPort()
|
||||||
@ -816,13 +824,20 @@ class DriverDSL(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return firstNotaryFuture.flatMap { firstNotary ->
|
return firstNotaryFuture.flatMap { firstNotary ->
|
||||||
val notaryParty = firstNotary.nodeInfo.legalIdentities[1] // TODO For now the second identity is notary identity.
|
restNotaryFutures.transpose().map { restNotaries -> Pair(notaryIdentity, listOf(firstNotary) + restNotaries) }
|
||||||
restNotaryFutures.transpose().map { restNotaries ->
|
|
||||||
Pair(notaryParty, listOf(firstNotary) + restNotaries)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createNetworkParameters(notaryNodeNames: List<CordaX500Name>, notaryName: CordaX500Name, validating: Boolean, serviceId: String): Party {
|
||||||
|
check(networkParameters == null) { "Notaries must be started first" }
|
||||||
|
val identity = ServiceIdentityGenerator.generateToDisk(
|
||||||
|
notaryNodeNames.map { baseDirectory(it) },
|
||||||
|
notaryName,
|
||||||
|
serviceId)
|
||||||
|
networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(identity, validating))))
|
||||||
|
return identity
|
||||||
|
}
|
||||||
|
|
||||||
private fun queryWebserver(handle: NodeHandle, process: Process): WebserverHandle {
|
private fun queryWebserver(handle: NodeHandle, process: Process): WebserverHandle {
|
||||||
val protocol = if (handle.configuration.useHTTPS) "https://" else "http://"
|
val protocol = if (handle.configuration.useHTTPS) "https://" else "http://"
|
||||||
val url = URL("$protocol${handle.webAddress}/api/status")
|
val url = URL("$protocol${handle.webAddress}/api/status")
|
||||||
@ -854,9 +869,8 @@ class DriverDSL(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun baseDirectory(nodeName: CordaX500Name): Path {
|
fun baseDirectory(nodeName: CordaX500Name): Path {
|
||||||
val nodeDirectoryName = String(nodeName.organisation.filter { !it.isWhitespace() }.toCharArray())
|
val nodeDirectoryName = nodeName.organisation.filter { !it.isWhitespace() }
|
||||||
return driverDirectory / nodeDirectoryName
|
return driverDirectory / nodeDirectoryName
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun baseDirectory(nodeName: String): Path = baseDirectory(CordaX500Name.parse(nodeName))
|
override fun baseDirectory(nodeName: String): Path = baseDirectory(CordaX500Name.parse(nodeName))
|
||||||
@ -887,7 +901,7 @@ class DriverDSL(
|
|||||||
private fun allNodesConnected(rpc: CordaRPCOps): CordaFuture<Int> {
|
private fun allNodesConnected(rpc: CordaRPCOps): CordaFuture<Int> {
|
||||||
val (snapshot, updates) = rpc.networkMapFeed()
|
val (snapshot, updates) = rpc.networkMapFeed()
|
||||||
val counterObservable = nodeCountObservable(snapshot.size, updates)
|
val counterObservable = nodeCountObservable(snapshot.size, updates)
|
||||||
countObservables.put(rpc.nodeInfo().legalIdentities.first().name, counterObservable)
|
countObservables[rpc.nodeInfo().legalIdentities[0].name] = counterObservable
|
||||||
/* TODO: this might not always be the exact number of nodes one has to wait for,
|
/* TODO: this might not always be the exact number of nodes one has to wait for,
|
||||||
* for example in the following sequence
|
* for example in the following sequence
|
||||||
* 1 start 3 nodes in order, A, B, C.
|
* 1 start 3 nodes in order, A, B, C.
|
||||||
@ -897,7 +911,7 @@ class DriverDSL(
|
|||||||
val requiredNodes = countObservables.size
|
val requiredNodes = countObservables.size
|
||||||
|
|
||||||
// This is an observable which yield the minimum number of nodes in each node network map.
|
// This is an observable which yield the minimum number of nodes in each node network map.
|
||||||
val smallestSeenNetworkMapSize = Observable.combineLatest(countObservables.values.toList()) { args : Array<Any> ->
|
val smallestSeenNetworkMapSize = Observable.combineLatest(countObservables.values.toList()) { args: Array<Any> ->
|
||||||
args.map { it as Int }.min() ?: 0
|
args.map { it as Int }.min() ?: 0
|
||||||
}
|
}
|
||||||
val future = smallestSeenNetworkMapSize.filter { it >= requiredNodes }.toFuture()
|
val future = smallestSeenNetworkMapSize.filter { it >= requiredNodes }.toFuture()
|
||||||
@ -905,15 +919,23 @@ class DriverDSL(
|
|||||||
return future
|
return future
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startNodeInternal(config: Config, webAddress: NetworkHostAndPort, startInProcess: Boolean?, maximumHeapSize: String): CordaFuture<NodeHandle> {
|
private fun startNodeInternal(config: Config,
|
||||||
val nodeConfiguration = config.parseAsNodeConfiguration()
|
webAddress: NetworkHostAndPort,
|
||||||
nodeInfoFilesCopier.addConfig(nodeConfiguration.baseDirectory)
|
startInProcess: Boolean?,
|
||||||
|
maximumHeapSize: String): CordaFuture<NodeHandle> {
|
||||||
|
val configuration = config.parseAsNodeConfiguration()
|
||||||
|
val baseDirectory = configuration.baseDirectory.createDirectories()
|
||||||
|
if (networkParameters == null) {
|
||||||
|
networkParameters = NetworkParametersCopier(testNetworkParameters(emptyList()))
|
||||||
|
}
|
||||||
|
networkParameters!!.install(baseDirectory)
|
||||||
|
nodeInfoFilesCopier.addConfig(baseDirectory)
|
||||||
val onNodeExit: () -> Unit = {
|
val onNodeExit: () -> Unit = {
|
||||||
nodeInfoFilesCopier.removeConfig(nodeConfiguration.baseDirectory)
|
nodeInfoFilesCopier.removeConfig(baseDirectory)
|
||||||
countObservables.remove(nodeConfiguration.myLegalName)
|
countObservables.remove(configuration.myLegalName)
|
||||||
}
|
}
|
||||||
if (startInProcess ?: startNodesInProcess) {
|
if (startInProcess ?: startNodesInProcess) {
|
||||||
val nodeAndThreadFuture = startInProcessNode(executorService, nodeConfiguration, config, cordappPackages)
|
val nodeAndThreadFuture = startInProcessNode(executorService, configuration, config, cordappPackages)
|
||||||
shutdownManager.registerShutdown(
|
shutdownManager.registerShutdown(
|
||||||
nodeAndThreadFuture.map { (node, thread) ->
|
nodeAndThreadFuture.map { (node, thread) ->
|
||||||
{
|
{
|
||||||
@ -923,21 +945,21 @@ class DriverDSL(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
return nodeAndThreadFuture.flatMap { (node, thread) ->
|
return nodeAndThreadFuture.flatMap { (node, thread) ->
|
||||||
establishRpc(nodeConfiguration, openFuture()).flatMap { rpc ->
|
establishRpc(configuration, openFuture()).flatMap { rpc ->
|
||||||
allNodesConnected(rpc).map {
|
allNodesConnected(rpc).map {
|
||||||
NodeHandle.InProcess(rpc.nodeInfo(), rpc, nodeConfiguration, webAddress, node, thread, onNodeExit)
|
NodeHandle.InProcess(rpc.nodeInfo(), rpc, configuration, webAddress, node, thread, onNodeExit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
||||||
val processFuture = startOutOfProcessNode(executorService, nodeConfiguration, config, quasarJarPath, debugPort, systemProperties, cordappPackages, maximumHeapSize)
|
val processFuture = startOutOfProcessNode(executorService, configuration, config, quasarJarPath, debugPort, systemProperties, cordappPackages, maximumHeapSize)
|
||||||
registerProcess(processFuture)
|
registerProcess(processFuture)
|
||||||
return processFuture.flatMap { process ->
|
return processFuture.flatMap { process ->
|
||||||
val processDeathFuture = poll(executorService, "process death") {
|
val processDeathFuture = poll(executorService, "process death") {
|
||||||
if (process.isAlive) null else process
|
if (process.isAlive) null else process
|
||||||
}
|
}
|
||||||
establishRpc(nodeConfiguration, processDeathFuture).flatMap { rpc ->
|
establishRpc(configuration, processDeathFuture).flatMap { rpc ->
|
||||||
// Call waitUntilNetworkReady in background in case RPC is failing over:
|
// Call waitUntilNetworkReady in background in case RPC is failing over:
|
||||||
val forked = executorService.fork {
|
val forked = executorService.fork {
|
||||||
allNodesConnected(rpc)
|
allNodesConnected(rpc)
|
||||||
@ -945,11 +967,11 @@ class DriverDSL(
|
|||||||
val networkMapFuture = forked.flatMap { it }
|
val networkMapFuture = forked.flatMap { it }
|
||||||
firstOf(processDeathFuture, networkMapFuture) {
|
firstOf(processDeathFuture, networkMapFuture) {
|
||||||
if (it == processDeathFuture) {
|
if (it == processDeathFuture) {
|
||||||
throw ListenProcessDeathException(nodeConfiguration.p2pAddress, process)
|
throw ListenProcessDeathException(configuration.p2pAddress, process)
|
||||||
}
|
}
|
||||||
processDeathFuture.cancel(false)
|
processDeathFuture.cancel(false)
|
||||||
log.info("Node handle is ready. NodeInfo: ${rpc.nodeInfo()}, WebAddress: ${webAddress}")
|
log.info("Node handle is ready. NodeInfo: ${rpc.nodeInfo()}, WebAddress: $webAddress")
|
||||||
NodeHandle.OutOfProcess(rpc.nodeInfo(), rpc, nodeConfiguration, webAddress, debugPort, process,
|
NodeHandle.OutOfProcess(rpc.nodeInfo(), rpc, configuration, webAddress, debugPort, process,
|
||||||
onNodeExit)
|
onNodeExit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -985,12 +1007,19 @@ class DriverDSL(
|
|||||||
// Write node.conf
|
// Write node.conf
|
||||||
writeConfig(nodeConf.baseDirectory, "node.conf", config)
|
writeConfig(nodeConf.baseDirectory, "node.conf", config)
|
||||||
// TODO pass the version in?
|
// TODO pass the version in?
|
||||||
val node = Node(nodeConf, MOCK_VERSION_INFO, initialiseSerialization = false, cordappLoader = CordappLoader.createDefaultWithTestPackages(nodeConf, cordappPackages)).start()
|
val node = Node(
|
||||||
|
nodeConf,
|
||||||
|
MOCK_VERSION_INFO,
|
||||||
|
initialiseSerialization = false,
|
||||||
|
cordappLoader = CordappLoader.createDefaultWithTestPackages(nodeConf, cordappPackages))
|
||||||
|
.start()
|
||||||
val nodeThread = thread(name = nodeConf.myLegalName.organisation) {
|
val nodeThread = thread(name = nodeConf.myLegalName.organisation) {
|
||||||
node.internals.run()
|
node.internals.run()
|
||||||
}
|
}
|
||||||
node to nodeThread
|
node to nodeThread
|
||||||
}.flatMap { nodeAndThread -> addressMustBeBoundFuture(executorService, nodeConf.p2pAddress).map { nodeAndThread } }
|
}.flatMap {
|
||||||
|
nodeAndThread -> addressMustBeBoundFuture(executorService, nodeConf.p2pAddress).map { nodeAndThread }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startOutOfProcessNode(
|
private fun startOutOfProcessNode(
|
||||||
@ -1004,7 +1033,7 @@ class DriverDSL(
|
|||||||
maximumHeapSize: String
|
maximumHeapSize: String
|
||||||
): CordaFuture<Process> {
|
): CordaFuture<Process> {
|
||||||
val processFuture = executorService.fork {
|
val processFuture = executorService.fork {
|
||||||
log.info("Starting out-of-process Node ${nodeConf.myLegalName.organisation}, debug port is " + debugPort ?: "not enabled")
|
log.info("Starting out-of-process Node ${nodeConf.myLegalName.organisation}, debug port is " + (debugPort ?: "not enabled"))
|
||||||
// Write node.conf
|
// Write node.conf
|
||||||
writeConfig(nodeConf.baseDirectory, "node.conf", config)
|
writeConfig(nodeConf.baseDirectory, "node.conf", config)
|
||||||
|
|
||||||
@ -1015,7 +1044,13 @@ class DriverDSL(
|
|||||||
"java.io.tmpdir" to System.getProperty("java.io.tmpdir") // Inherit from parent process
|
"java.io.tmpdir" to System.getProperty("java.io.tmpdir") // Inherit from parent process
|
||||||
)
|
)
|
||||||
// See experimental/quasar-hook/README.md for how to generate.
|
// See experimental/quasar-hook/README.md for how to generate.
|
||||||
val excludePattern = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**)"
|
val excludePattern = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;" +
|
||||||
|
"com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;" +
|
||||||
|
"com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;" +
|
||||||
|
"io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;" +
|
||||||
|
"org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;" +
|
||||||
|
"org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;" +
|
||||||
|
"org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**)"
|
||||||
val extraJvmArguments = systemProperties.removeResolvedClasspath().map { "-D${it.key}=${it.value}" } +
|
val extraJvmArguments = systemProperties.removeResolvedClasspath().map { "-D${it.key}=${it.value}" } +
|
||||||
"-javaagent:$quasarJarPath=$excludePattern"
|
"-javaagent:$quasarJarPath=$excludePattern"
|
||||||
val loggingLevel = if (debugPort == null) "INFO" else "DEBUG"
|
val loggingLevel = if (debugPort == null) "INFO" else "DEBUG"
|
||||||
@ -1081,7 +1116,7 @@ class DriverDSL(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun writeConfig(path: Path, filename: String, config: Config) {
|
fun writeConfig(path: Path, filename: String, config: Config) {
|
||||||
path.toFile().mkdirs()
|
val configString = config.root().render(ConfigRenderOptions.defaults())
|
||||||
File("$path/$filename").writeText(config.root().render(ConfigRenderOptions.defaults()))
|
configString.byteInputStream().copyTo(path / filename, REPLACE_EXISTING)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,11 +16,14 @@ import net.corda.node.services.config.parseAsNodeConfiguration
|
|||||||
import net.corda.node.services.config.plus
|
import net.corda.node.services.config.plus
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
|
import net.corda.testing.common.internal.NetworkParametersCopier
|
||||||
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.driver.addressMustNotBeBoundFuture
|
import net.corda.testing.driver.addressMustNotBeBoundFuture
|
||||||
import net.corda.testing.getFreeLocalPorts
|
import net.corda.testing.getFreeLocalPorts
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import org.apache.logging.log4j.Level
|
import org.apache.logging.log4j.Level
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -40,6 +43,7 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
|
|||||||
@JvmField
|
@JvmField
|
||||||
val tempFolder = TemporaryFolder()
|
val tempFolder = TemporaryFolder()
|
||||||
|
|
||||||
|
private lateinit var defaultNetworkParameters: NetworkParametersCopier
|
||||||
private val nodes = mutableListOf<StartedNode<Node>>()
|
private val nodes = mutableListOf<StartedNode<Node>>()
|
||||||
private val nodeInfos = mutableListOf<NodeInfo>()
|
private val nodeInfos = mutableListOf<NodeInfo>()
|
||||||
|
|
||||||
@ -47,6 +51,11 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
|
|||||||
System.setProperty("consoleLogLevel", Level.DEBUG.name().toLowerCase())
|
System.setProperty("consoleLogLevel", Level.DEBUG.name().toLowerCase())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun init() {
|
||||||
|
defaultNetworkParameters = NetworkParametersCopier(testNetworkParameters(emptyList()))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops the network map node and all the nodes started by [startNode]. This is called automatically after each test
|
* Stops the network map node and all the nodes started by [startNode]. This is called automatically after each test
|
||||||
* but can also be called manually within a test.
|
* but can also be called manually within a test.
|
||||||
@ -86,6 +95,7 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
|
|||||||
)
|
)
|
||||||
|
|
||||||
val parsedConfig = config.parseAsNodeConfiguration()
|
val parsedConfig = config.parseAsNodeConfiguration()
|
||||||
|
defaultNetworkParameters.install(baseDirectory)
|
||||||
val node = Node(
|
val node = Node(
|
||||||
parsedConfig,
|
parsedConfig,
|
||||||
MockServices.MOCK_VERSION_INFO.copy(platformVersion = platformVersion),
|
MockServices.MOCK_VERSION_INFO.copy(platformVersion = platformVersion),
|
||||||
|
@ -15,6 +15,7 @@ fun CordformDefinition.clean() {
|
|||||||
/**
|
/**
|
||||||
* Creates and starts all nodes required for the demo.
|
* Creates and starts all nodes required for the demo.
|
||||||
*/
|
*/
|
||||||
|
// TODO add notaries to cordform!
|
||||||
fun CordformDefinition.runNodes() = driver(
|
fun CordformDefinition.runNodes() = driver(
|
||||||
isDebug = true,
|
isDebug = true,
|
||||||
driverDirectory = driverDirectory,
|
driverDirectory = driverDirectory,
|
||||||
|
@ -18,7 +18,10 @@ import java.math.BigInteger
|
|||||||
/**
|
/**
|
||||||
* Network map cache with no backing map service.
|
* Network map cache with no backing map service.
|
||||||
*/
|
*/
|
||||||
class MockNetworkMapCache(database: CordaPersistence, configuration: NodeConfiguration) : PersistentNetworkMapCache(database, configuration) {
|
class MockNetworkMapCache(
|
||||||
|
database: CordaPersistence,
|
||||||
|
configuration: NodeConfiguration
|
||||||
|
) : PersistentNetworkMapCache(database, configuration, emptyList()) {
|
||||||
private companion object {
|
private companion object {
|
||||||
val BANK_C = getTestPartyAndCertificate(CordaX500Name(organisation = "Bank C", locality = "London", country = "GB"), entropyToKeyPair(BigInteger.valueOf(1000)).public)
|
val BANK_C = getTestPartyAndCertificate(CordaX500Name(organisation = "Bank C", locality = "London", country = "GB"), entropyToKeyPair(BigInteger.valueOf(1000)).public)
|
||||||
val BANK_D = getTestPartyAndCertificate(CordaX500Name(organisation = "Bank D", locality = "London", country = "GB"), entropyToKeyPair(BigInteger.valueOf(2000)).public)
|
val BANK_D = getTestPartyAndCertificate(CordaX500Name(organisation = "Bank D", locality = "London", country = "GB"), entropyToKeyPair(BigInteger.valueOf(2000)).public)
|
||||||
|
@ -7,13 +7,13 @@ import com.nhaarman.mockito_kotlin.whenever
|
|||||||
import net.corda.core.crypto.entropyToKeyPair
|
import net.corda.core.crypto.entropyToKeyPair
|
||||||
import net.corda.core.crypto.random63BitValue
|
import net.corda.core.crypto.random63BitValue
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
|
||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.createDirectory
|
import net.corda.core.internal.createDirectory
|
||||||
import net.corda.core.internal.uncheckedCast
|
import net.corda.core.internal.uncheckedCast
|
||||||
import net.corda.core.messaging.MessageRecipients
|
import net.corda.core.messaging.MessageRecipients
|
||||||
import net.corda.core.messaging.RPCOps
|
import net.corda.core.messaging.RPCOps
|
||||||
import net.corda.core.messaging.SingleMessageRecipient
|
import net.corda.core.messaging.SingleMessageRecipient
|
||||||
|
import net.corda.core.node.NotaryInfo
|
||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.node.services.KeyManagementService
|
import net.corda.core.node.services.KeyManagementService
|
||||||
import net.corda.core.serialization.SerializationWhitelist
|
import net.corda.core.serialization.SerializationWhitelist
|
||||||
@ -34,14 +34,17 @@ import net.corda.node.services.transactions.BFTSMaRt
|
|||||||
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
||||||
import net.corda.node.utilities.AffinityExecutor
|
import net.corda.node.utilities.AffinityExecutor
|
||||||
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
|
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
|
||||||
import net.corda.nodeapi.internal.ServiceInfo
|
import net.corda.node.utilities.ServiceIdentityGenerator
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
|
import net.corda.testing.common.internal.NetworkParametersCopier
|
||||||
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.initialiseTestSerialization
|
import net.corda.testing.initialiseTestSerialization
|
||||||
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import net.corda.testing.testNodeConfiguration
|
import net.corda.testing.testNodeConfiguration
|
||||||
import org.apache.activemq.artemis.utils.ReusableLatch
|
import org.apache.activemq.artemis.utils.ReusableLatch
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
|
import sun.plugin.dom.DOMObjectFactory.createNode
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -72,8 +75,6 @@ data class MockNetworkParameters(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param notaryIdentity a set of service entries to use in place of the node's default service entries,
|
|
||||||
* for example where a node's service is part of a cluster.
|
|
||||||
* @param entropyRoot the initial entropy value to use when generating keys. Defaults to an (insecure) random value,
|
* @param entropyRoot the initial entropy value to use when generating keys. Defaults to an (insecure) random value,
|
||||||
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
|
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
|
||||||
* @param configOverrides add/override behaviour of the [NodeConfiguration] mock object.
|
* @param configOverrides add/override behaviour of the [NodeConfiguration] mock object.
|
||||||
@ -82,12 +83,10 @@ data class MockNetworkParameters(
|
|||||||
data class MockNodeParameters(
|
data class MockNodeParameters(
|
||||||
val forcedID: Int? = null,
|
val forcedID: Int? = null,
|
||||||
val legalName: CordaX500Name? = null,
|
val legalName: CordaX500Name? = null,
|
||||||
val notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
|
|
||||||
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||||
val configOverrides: (NodeConfiguration) -> Any? = {}) {
|
val configOverrides: (NodeConfiguration) -> Any? = {}) {
|
||||||
fun setForcedID(forcedID: Int?) = copy(forcedID = forcedID)
|
fun setForcedID(forcedID: Int?) = copy(forcedID = forcedID)
|
||||||
fun setLegalName(legalName: CordaX500Name?) = copy(legalName = legalName)
|
fun setLegalName(legalName: CordaX500Name?) = copy(legalName = legalName)
|
||||||
fun setNotaryIdentity(notaryIdentity: Pair<ServiceInfo, KeyPair>?) = copy(notaryIdentity = notaryIdentity)
|
|
||||||
fun setEntropyRoot(entropyRoot: BigInteger) = copy(entropyRoot = entropyRoot)
|
fun setEntropyRoot(entropyRoot: BigInteger) = copy(entropyRoot = entropyRoot)
|
||||||
fun setConfigOverrides(configOverrides: (NodeConfiguration) -> Any?) = copy(configOverrides = configOverrides)
|
fun setConfigOverrides(configOverrides: (NodeConfiguration) -> Any?) = copy(configOverrides = configOverrides)
|
||||||
}
|
}
|
||||||
@ -96,8 +95,8 @@ data class MockNodeArgs(
|
|||||||
val config: NodeConfiguration,
|
val config: NodeConfiguration,
|
||||||
val network: MockNetwork,
|
val network: MockNetwork,
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val notaryIdentity: Pair<ServiceInfo, KeyPair>?,
|
val entropyRoot: BigInteger
|
||||||
val entropyRoot: BigInteger)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mock node brings up a suite of in-memory services in a fast manner suitable for unit testing.
|
* A mock node brings up a suite of in-memory services in a fast manner suitable for unit testing.
|
||||||
@ -129,10 +128,13 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
|||||||
val messagingNetwork = InMemoryMessagingNetwork(networkSendManuallyPumped, servicePeerAllocationStrategy, busyLatch)
|
val messagingNetwork = InMemoryMessagingNetwork(networkSendManuallyPumped, servicePeerAllocationStrategy, busyLatch)
|
||||||
// A unique identifier for this network to segregate databases with the same nodeID but different networks.
|
// A unique identifier for this network to segregate databases with the same nodeID but different networks.
|
||||||
private val networkId = random63BitValue()
|
private val networkId = random63BitValue()
|
||||||
private val _nodes = mutableListOf<MockNode>()
|
private lateinit var networkParameters: NetworkParametersCopier
|
||||||
|
private var notaryInfos: MutableList<NotaryInfo>? = ArrayList()
|
||||||
|
private val _nodes = ArrayList<MockNode>()
|
||||||
|
private val serializationEnv = initialiseTestSerialization(initialiseSerialization)
|
||||||
|
|
||||||
/** A read only view of the current set of executing nodes. */
|
/** A read only view of the current set of executing nodes. */
|
||||||
val nodes: List<MockNode> get() = _nodes
|
val nodes: List<MockNode> get() = _nodes
|
||||||
private val serializationEnv = initialiseTestSerialization(initialiseSerialization)
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
filesystem.getPath("/nodes").createDirectory()
|
filesystem.getPath("/nodes").createDirectory()
|
||||||
@ -152,11 +154,11 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean {
|
override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean {
|
||||||
if (!isShutdown) {
|
return if (!isShutdown) {
|
||||||
flush()
|
flush()
|
||||||
return true
|
true
|
||||||
} else {
|
} else {
|
||||||
return super.awaitTermination(timeout, unit)
|
super.awaitTermination(timeout, unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,44 +168,69 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
|||||||
TestClock(),
|
TestClock(),
|
||||||
MOCK_VERSION_INFO,
|
MOCK_VERSION_INFO,
|
||||||
CordappLoader.createDefaultWithTestPackages(args.config, args.network.cordappPackages),
|
CordappLoader.createDefaultWithTestPackages(args.config, args.network.cordappPackages),
|
||||||
args.network.busyLatch) {
|
args.network.busyLatch
|
||||||
|
) {
|
||||||
val mockNet = args.network
|
val mockNet = args.network
|
||||||
val id = args.id
|
val id = args.id
|
||||||
internal val notaryIdentity = args.notaryIdentity
|
private val entropyRoot = args.entropyRoot
|
||||||
val entropyRoot = args.entropyRoot
|
|
||||||
var counter = entropyRoot
|
var counter = entropyRoot
|
||||||
|
|
||||||
override val log: Logger = loggerFor<MockNode>()
|
override val log: Logger = loggerFor<MockNode>()
|
||||||
|
|
||||||
override val serverThread: AffinityExecutor =
|
override val serverThread: AffinityExecutor =
|
||||||
if (mockNet.threadPerNode)
|
if (mockNet.threadPerNode) {
|
||||||
ServiceAffinityExecutor("Mock node $id thread", 1)
|
ServiceAffinityExecutor("Mock node $id thread", 1)
|
||||||
else {
|
} else {
|
||||||
mockNet.sharedUserCount.incrementAndGet()
|
mockNet.sharedUserCount.incrementAndGet()
|
||||||
mockNet.sharedServerThread
|
mockNet.sharedServerThread
|
||||||
}
|
}
|
||||||
|
|
||||||
override val started: StartedNode<MockNode>? get() = uncheckedCast(super.started)
|
override val started: StartedNode<MockNode>? get() = uncheckedCast(super.started)
|
||||||
override fun start(): StartedNode<MockNode> = uncheckedCast(super.start())
|
|
||||||
|
override fun start(): StartedNode<MockNode> {
|
||||||
|
installNetworkParameters()
|
||||||
|
val started: StartedNode<MockNode> = uncheckedCast(super.start())
|
||||||
|
advertiseNodeToNetwork(started)
|
||||||
|
return started
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun installNetworkParameters() {
|
||||||
|
mockNet.notaryInfos?.let {
|
||||||
|
mockNet.networkParameters = NetworkParametersCopier(testNetworkParameters(it))
|
||||||
|
}
|
||||||
|
mockNet.notaryInfos = null
|
||||||
|
mockNet.networkParameters.install(configuration.baseDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun advertiseNodeToNetwork(newNode: StartedNode<MockNode>) {
|
||||||
|
mockNet.nodes
|
||||||
|
.mapNotNull { it.started }
|
||||||
|
.forEach { existingNode ->
|
||||||
|
newNode.services.networkMapCache.addNode(existingNode.info)
|
||||||
|
existingNode.services.networkMapCache.addNode(newNode.info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We only need to override the messaging service here, as currently everything that hits disk does so
|
// We only need to override the messaging service here, as currently everything that hits disk does so
|
||||||
// through the java.nio API which we are already mocking via Jimfs.
|
// through the java.nio API which we are already mocking via Jimfs.
|
||||||
override fun makeMessagingService(legalIdentity: PartyAndCertificate): MessagingService {
|
override fun makeMessagingService(): MessagingService {
|
||||||
require(id >= 0) { "Node ID must be zero or positive, was passed: " + id }
|
require(id >= 0) { "Node ID must be zero or positive, was passed: " + id }
|
||||||
return mockNet.messagingNetwork.createNodeWithID(
|
return mockNet.messagingNetwork.createNodeWithID(
|
||||||
!mockNet.threadPerNode,
|
!mockNet.threadPerNode,
|
||||||
id,
|
id,
|
||||||
serverThread,
|
serverThread,
|
||||||
getNotaryIdentity(),
|
myNotaryIdentity,
|
||||||
myLegalName,
|
myLegalName,
|
||||||
database)
|
database
|
||||||
.start()
|
).start().getOrThrow()
|
||||||
.getOrThrow()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setMessagingServiceSpy(messagingServiceSpy: MessagingServiceSpy) {
|
fun setMessagingServiceSpy(messagingServiceSpy: MessagingServiceSpy) {
|
||||||
network = messagingServiceSpy
|
network = messagingServiceSpy
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun makeKeyManagementService(identityService: IdentityService): KeyManagementService {
|
override fun makeKeyManagementService(identityService: IdentityService, keyPairs: Set<KeyPair>): KeyManagementService {
|
||||||
return E2ETestKeyManagementService(identityService, partyKeys + (notaryIdentity?.let { setOf(it.second) } ?: emptySet()))
|
return E2ETestKeyManagementService(identityService, keyPairs)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun startMessagingService(rpcOps: RPCOps) {
|
override fun startMessagingService(rpcOps: RPCOps) {
|
||||||
@ -224,7 +251,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
|||||||
|
|
||||||
override fun makeTransactionVerifierService() = InMemoryTransactionVerifierService(1)
|
override fun makeTransactionVerifierService() = InMemoryTransactionVerifierService(1)
|
||||||
|
|
||||||
override fun myAddresses() = emptyList<NetworkHostAndPort>()
|
override fun myAddresses(): List<NetworkHostAndPort> = emptyList()
|
||||||
|
|
||||||
// Allow unit tests to modify the serialization whitelist list before the node start,
|
// Allow unit tests to modify the serialization whitelist list before the node start,
|
||||||
// so they don't have to ServiceLoad test whitelists into all unit tests.
|
// so they don't have to ServiceLoad test whitelists into all unit tests.
|
||||||
@ -269,17 +296,21 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createUnstartedNode(parameters: MockNodeParameters = MockNodeParameters()) = createUnstartedNode(parameters, defaultFactory)
|
fun createUnstartedNode(parameters: MockNodeParameters = MockNodeParameters()): MockNode {
|
||||||
|
return createUnstartedNode(parameters, defaultFactory)
|
||||||
|
}
|
||||||
|
|
||||||
fun <N : MockNode> createUnstartedNode(parameters: MockNodeParameters = MockNodeParameters(), nodeFactory: (MockNodeArgs) -> N): N {
|
fun <N : MockNode> createUnstartedNode(parameters: MockNodeParameters = MockNodeParameters(), nodeFactory: (MockNodeArgs) -> N): N {
|
||||||
return createNodeImpl(parameters, nodeFactory, false)
|
return createNodeImpl(parameters, nodeFactory, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedNode<MockNode> = createNode(parameters, defaultFactory)
|
fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedNode<MockNode> {
|
||||||
|
return createNode(parameters, defaultFactory)
|
||||||
|
}
|
||||||
|
|
||||||
/** Like the other [createNode] but takes a [nodeFactory] and propagates its [MockNode] subtype. */
|
/** Like the other [createNode] but takes a [nodeFactory] and propagates its [MockNode] subtype. */
|
||||||
fun <N : MockNode> createNode(parameters: MockNodeParameters = MockNodeParameters(), nodeFactory: (MockNodeArgs) -> N): StartedNode<N> {
|
fun <N : MockNode> createNode(parameters: MockNodeParameters = MockNodeParameters(), nodeFactory: (MockNodeArgs) -> N): StartedNode<N> {
|
||||||
val node: StartedNode<N> = uncheckedCast(createNodeImpl(parameters, nodeFactory, true).started)!!
|
return uncheckedCast(createNodeImpl(parameters, nodeFactory, true).started)!!
|
||||||
ensureAllNetworkMapCachesHaveAllNodeInfos()
|
|
||||||
return node
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <N : MockNode> createNodeImpl(parameters: MockNodeParameters, nodeFactory: (MockNodeArgs) -> N, start: Boolean): N {
|
private fun <N : MockNode> createNodeImpl(parameters: MockNodeParameters, nodeFactory: (MockNodeArgs) -> N, start: Boolean): N {
|
||||||
@ -290,13 +321,24 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
|||||||
doReturn(makeTestDataSourceProperties("node_${id}_net_$networkId")).whenever(it).dataSourceProperties
|
doReturn(makeTestDataSourceProperties("node_${id}_net_$networkId")).whenever(it).dataSourceProperties
|
||||||
parameters.configOverrides(it)
|
parameters.configOverrides(it)
|
||||||
}
|
}
|
||||||
return nodeFactory(MockNodeArgs(config, this, id, parameters.notaryIdentity, parameters.entropyRoot)).apply {
|
|
||||||
if (start) {
|
val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot))
|
||||||
start()
|
_nodes += node
|
||||||
ensureAllNetworkMapCachesHaveAllNodeInfos()
|
|
||||||
|
config.notary?.let { notaryConfig ->
|
||||||
|
val notaryInfos = notaryInfos ?: throw IllegalStateException("Cannot add notaries once nodes have started")
|
||||||
|
if (!notaryConfig.isClusterConfig) {
|
||||||
|
// Create the node's main identity, which will also double as its notary identity
|
||||||
|
val identity = ServiceIdentityGenerator.generateToDisk(listOf(config.baseDirectory), config.myLegalName, "identity")
|
||||||
|
notaryInfos += NotaryInfo(identity, notaryConfig.validating)
|
||||||
}
|
}
|
||||||
_nodes.add(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (start) {
|
||||||
|
node.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
fun baseDirectory(nodeId: Int): Path = filesystem.getPath("/nodes/$nodeId")
|
fun baseDirectory(nodeId: Int): Path = filesystem.getPath("/nodes/$nodeId")
|
||||||
@ -309,7 +351,6 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
|||||||
*/
|
*/
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun runNetwork(rounds: Int = -1) {
|
fun runNetwork(rounds: Int = -1) {
|
||||||
ensureAllNetworkMapCachesHaveAllNodeInfos()
|
|
||||||
check(!networkSendManuallyPumped)
|
check(!networkSendManuallyPumped)
|
||||||
fun pumpAll() = messagingNetwork.endpoints.map { it.pumpReceive(false) }
|
fun pumpAll() = messagingNetwork.endpoints.map { it.pumpReceive(false) }
|
||||||
|
|
||||||
@ -324,23 +365,26 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun createNotaryNode(parameters: MockNodeParameters = MockNodeParameters(legalName = DUMMY_NOTARY.name), validating: Boolean = true): StartedNode<MockNode> {
|
fun createNotaryNode(parameters: MockNodeParameters = MockNodeParameters(legalName = DUMMY_NOTARY.name),
|
||||||
|
validating: Boolean = true): StartedNode<MockNode> {
|
||||||
return createNotaryNode(parameters, validating, defaultFactory)
|
return createNotaryNode(parameters, validating, defaultFactory)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <N : MockNode> createNotaryNode(parameters: MockNodeParameters = MockNodeParameters(legalName = DUMMY_NOTARY.name),
|
fun <N : MockNode> createNotaryNode(parameters: MockNodeParameters = MockNodeParameters(legalName = DUMMY_NOTARY.name),
|
||||||
validating: Boolean = true,
|
validating: Boolean = true,
|
||||||
nodeFactory: (MockNodeArgs) -> N): StartedNode<N> {
|
nodeFactory: (MockNodeArgs) -> N): StartedNode<N> {
|
||||||
return createNode(parameters.copy(configOverrides = {
|
return createNode(
|
||||||
doReturn(NotaryConfig(validating)).whenever(it).notary
|
parameters.copy(configOverrides = {
|
||||||
parameters.configOverrides(it)
|
doReturn(NotaryConfig(validating)).whenever(it).notary
|
||||||
}), nodeFactory)
|
parameters.configOverrides(it)
|
||||||
|
}),
|
||||||
|
nodeFactory
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun createPartyNode(legalName: CordaX500Name? = null,
|
fun createPartyNode(legalName: CordaX500Name? = null): StartedNode<MockNode> {
|
||||||
notaryIdentity: Pair<ServiceInfo, KeyPair>? = null): StartedNode<MockNode> {
|
return createNode(MockNodeParameters(legalName = legalName))
|
||||||
return createNode(MockNodeParameters(legalName = legalName, notaryIdentity = notaryIdentity))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused") // This is used from the network visualiser tool.
|
@Suppress("unused") // This is used from the network visualiser tool.
|
||||||
@ -355,21 +399,9 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ensureAllNetworkMapCachesHaveAllNodeInfos() {
|
|
||||||
val infos = nodes.mapNotNull { it.started?.info }
|
|
||||||
nodes.filter { it.hasDBConnection() }
|
|
||||||
.mapNotNull { it.started?.services?.networkMapCache }
|
|
||||||
.forEach {
|
|
||||||
for (nodeInfo in infos) {
|
|
||||||
it.addNode(nodeInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun startNodes() {
|
fun startNodes() {
|
||||||
require(nodes.isNotEmpty())
|
require(nodes.isNotEmpty())
|
||||||
nodes.forEach { it.started ?: it.start() }
|
nodes.forEach { it.started ?: it.start() }
|
||||||
ensureAllNetworkMapCachesHaveAllNodeInfos()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopNodes() {
|
fun stopNodes() {
|
||||||
|
@ -2,10 +2,14 @@ package net.corda.smoketesting
|
|||||||
|
|
||||||
import net.corda.client.rpc.CordaRPCClient
|
import net.corda.client.rpc.CordaRPCClient
|
||||||
import net.corda.client.rpc.CordaRPCConnection
|
import net.corda.client.rpc.CordaRPCConnection
|
||||||
|
import net.corda.client.rpc.internal.KryoClientSerializationScheme
|
||||||
|
import net.corda.core.internal.copyTo
|
||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
|
import net.corda.testing.common.internal.NetworkParametersCopier
|
||||||
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
@ -45,9 +49,17 @@ class NodeProcess(
|
|||||||
private val buildDirectory: Path = Paths.get("build"),
|
private val buildDirectory: Path = Paths.get("build"),
|
||||||
private val cordaJar: Path = Paths.get(this::class.java.getResource("/corda.jar").toURI())
|
private val cordaJar: Path = Paths.get(this::class.java.getResource("/corda.jar").toURI())
|
||||||
) {
|
) {
|
||||||
companion object {
|
private companion object {
|
||||||
val javaPath: Path = Paths.get(System.getProperty("java.home"), "bin", "java")
|
val javaPath: Path = Paths.get(System.getProperty("java.home"), "bin", "java")
|
||||||
val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(systemDefault())
|
val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(systemDefault())
|
||||||
|
val defaultNetworkParameters = run {
|
||||||
|
// TODO withTestSerialization is in test-utils, which we don't have access to
|
||||||
|
KryoClientSerializationScheme.initialiseSerialization()
|
||||||
|
// There are no notaries in the network parameters for smoke test nodes. If this is required then we would
|
||||||
|
// need to introduce the concept of a "network" which predefines the notaries, like the driver and MockNetwork
|
||||||
|
NetworkParametersCopier(testNetworkParameters(emptyList()))
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
try {
|
try {
|
||||||
Class.forName("net.corda.node.Corda")
|
Class.forName("net.corda.node.Corda")
|
||||||
@ -66,8 +78,8 @@ class NodeProcess(
|
|||||||
val nodeDir = baseDirectory(config).createDirectories()
|
val nodeDir = baseDirectory(config).createDirectories()
|
||||||
log.info("Node directory: {}", nodeDir)
|
log.info("Node directory: {}", nodeDir)
|
||||||
|
|
||||||
val confFile = nodeDir.resolve("node.conf").toFile()
|
config.toText().byteInputStream().copyTo(nodeDir / "node.conf")
|
||||||
confFile.writeText(config.toText())
|
defaultNetworkParameters.install(nodeDir)
|
||||||
|
|
||||||
val process = startNode(nodeDir)
|
val process = startNode(nodeDir)
|
||||||
val client = CordaRPCClient(NetworkHostAndPort("localhost", config.rpcPort))
|
val client = CordaRPCClient(NetworkHostAndPort("localhost", config.rpcPort))
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
apply plugin: 'net.corda.plugins.publish-utils'
|
apply plugin: 'net.corda.plugins.publish-utils'
|
||||||
apply plugin: 'com.jfrog.artifactory'
|
apply plugin: 'com.jfrog.artifactory'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile project(':core')
|
||||||
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
baseName 'corda-test-common'
|
baseName 'corda-test-common'
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package net.corda.testing.common.internal
|
||||||
|
|
||||||
|
import net.corda.core.crypto.SignedData
|
||||||
|
import net.corda.core.crypto.entropyToKeyPair
|
||||||
|
import net.corda.core.crypto.sign
|
||||||
|
import net.corda.core.internal.copyTo
|
||||||
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.core.node.NetworkParameters
|
||||||
|
import net.corda.core.serialization.serialize
|
||||||
|
import java.math.BigInteger
|
||||||
|
import java.nio.file.FileAlreadyExistsException
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
class NetworkParametersCopier(networkParameters: NetworkParameters) {
|
||||||
|
private companion object {
|
||||||
|
val DUMMY_MAP_KEY = entropyToKeyPair(BigInteger.valueOf(123))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val serializedNetworkParameters = networkParameters.let {
|
||||||
|
val serialize = it.serialize()
|
||||||
|
val signature = DUMMY_MAP_KEY.sign(serialize)
|
||||||
|
SignedData(serialize, signature).serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun install(dir: Path) {
|
||||||
|
try {
|
||||||
|
serializedNetworkParameters.open().copyTo(dir / "network-parameters")
|
||||||
|
} catch (e: FileAlreadyExistsException) {
|
||||||
|
// Leave the file untouched if it already exists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package net.corda.testing.common.internal
|
||||||
|
|
||||||
|
import net.corda.core.node.NetworkParameters
|
||||||
|
import net.corda.core.node.NotaryInfo
|
||||||
|
import net.corda.core.utilities.days
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
fun testNetworkParameters(notaries: List<NotaryInfo>): NetworkParameters {
|
||||||
|
return NetworkParameters(
|
||||||
|
minimumPlatformVersion = 1,
|
||||||
|
notaries = notaries,
|
||||||
|
modifiedTime = Instant.now(),
|
||||||
|
eventHorizon = 10000.days,
|
||||||
|
maxMessageSize = 40000,
|
||||||
|
maxTransactionSize = 40000,
|
||||||
|
epoch = 1
|
||||||
|
)
|
||||||
|
}
|
@ -8,7 +8,6 @@ description 'Testing utilities for Corda'
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':test-common')
|
compile project(':test-common')
|
||||||
compile project(':core')
|
|
||||||
compile project(':node')
|
compile project(':node')
|
||||||
compile project(':client:mock')
|
compile project(':client:mock')
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ val DUMMY_NOTARY_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(20))
|
|||||||
/** Dummy notary identity for tests and simulations */
|
/** Dummy notary identity for tests and simulations */
|
||||||
val DUMMY_NOTARY_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(DUMMY_NOTARY)
|
val DUMMY_NOTARY_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(DUMMY_NOTARY)
|
||||||
val DUMMY_NOTARY: Party get() = Party(CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"), DUMMY_NOTARY_KEY.public)
|
val DUMMY_NOTARY: Party get() = Party(CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"), DUMMY_NOTARY_KEY.public)
|
||||||
val DUMMY_NOTARY_SERVICE_NAME: CordaX500Name = DUMMY_NOTARY.name.copy(commonName = "corda.notary.validating")
|
|
||||||
|
|
||||||
val DUMMY_BANK_A_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(40)) }
|
val DUMMY_BANK_A_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(40)) }
|
||||||
/** Dummy bank identity for tests and simulations */
|
/** Dummy bank identity for tests and simulations */
|
||||||
|
@ -36,7 +36,6 @@ import net.corda.explorer.model.CordaView
|
|||||||
import net.corda.finance.utils.CityDatabase
|
import net.corda.finance.utils.CityDatabase
|
||||||
import net.corda.finance.utils.ScreenCoordinate
|
import net.corda.finance.utils.ScreenCoordinate
|
||||||
import net.corda.finance.utils.WorldMapLocation
|
import net.corda.finance.utils.WorldMapLocation
|
||||||
import net.corda.nodeapi.internal.ServiceType
|
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
|
||||||
class Network : CordaView() {
|
class Network : CordaView() {
|
||||||
@ -45,6 +44,7 @@ class Network : CordaView() {
|
|||||||
// Inject data.
|
// Inject data.
|
||||||
private val myIdentity by observableValue(NetworkIdentityModel::myIdentity)
|
private val myIdentity by observableValue(NetworkIdentityModel::myIdentity)
|
||||||
private val notaries by observableList(NetworkIdentityModel::notaryNodes)
|
private val notaries by observableList(NetworkIdentityModel::notaryNodes)
|
||||||
|
private val notaryIdentities by observableList(NetworkIdentityModel::notaries)
|
||||||
private val peers by observableList(NetworkIdentityModel::parties)
|
private val peers by observableList(NetworkIdentityModel::parties)
|
||||||
private val transactions by observableList(TransactionDataModel::partiallyResolvedTransactions)
|
private val transactions by observableList(TransactionDataModel::partiallyResolvedTransactions)
|
||||||
var centralPeer: String? = null
|
var centralPeer: String? = null
|
||||||
@ -103,7 +103,7 @@ class Network : CordaView() {
|
|||||||
hgap = 5.0
|
hgap = 5.0
|
||||||
vgap = 5.0
|
vgap = 5.0
|
||||||
for (identity in identities) {
|
for (identity in identities) {
|
||||||
val isNotary = identity.name.commonName?.let { ServiceType.parse(it).isNotary() } == true
|
val isNotary = identity.party in notaryIdentities
|
||||||
row("${if (isNotary) "Notary " else ""}Public Key :") {
|
row("${if (isNotary) "Notary " else ""}Public Key :") {
|
||||||
copyableLabel(SimpleObjectProperty(identity.owningKey.toBase58String()))
|
copyableLabel(SimpleObjectProperty(identity.owningKey.toBase58String()))
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ val dummyNotarisationTest = LoadTest<NotariseCommand, Unit>(
|
|||||||
val issuerServices = MockServices(DUMMY_CASH_ISSUER_KEY)
|
val issuerServices = MockServices(DUMMY_CASH_ISSUER_KEY)
|
||||||
val generateTx = Generator.pickOne(simpleNodes).flatMap { node ->
|
val generateTx = Generator.pickOne(simpleNodes).flatMap { node ->
|
||||||
Generator.int().map {
|
Generator.int().map {
|
||||||
val issueBuilder = DummyContract.generateInitial(it, notary.info.legalIdentities[1], DUMMY_CASH_ISSUER) // TODO notary choice
|
val issueBuilder = DummyContract.generateInitial(it, notary.info.legalIdentities[0], DUMMY_CASH_ISSUER) // TODO notary choice
|
||||||
val issueTx = issuerServices.signInitialTransaction(issueBuilder)
|
val issueTx = issuerServices.signInitialTransaction(issueBuilder)
|
||||||
val asset = issueTx.tx.outRef<DummyContract.SingleOwnerState>(0)
|
val asset = issueTx.tx.outRef<DummyContract.SingleOwnerState>(0)
|
||||||
val moveBuilder = DummyContract.move(asset, DUMMY_CASH_ISSUER.party)
|
val moveBuilder = DummyContract.move(asset, DUMMY_CASH_ISSUER.party)
|
||||||
|
@ -6,6 +6,7 @@ import net.corda.core.concurrent.CordaFuture
|
|||||||
import net.corda.core.crypto.random63BitValue
|
import net.corda.core.crypto.random63BitValue
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.concurrent.*
|
import net.corda.core.internal.concurrent.*
|
||||||
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
@ -248,7 +249,7 @@ data class VerifierDriverDSL(
|
|||||||
val jdwpPort = if (driverDSL.isDebug) driverDSL.debugPortAllocation.nextPort() else null
|
val jdwpPort = if (driverDSL.isDebug) driverDSL.debugPortAllocation.nextPort() else null
|
||||||
val processFuture = driverDSL.executorService.fork {
|
val processFuture = driverDSL.executorService.fork {
|
||||||
val verifierName = CordaX500Name(organisation = "Verifier$id", locality = "London", country = "GB")
|
val verifierName = CordaX500Name(organisation = "Verifier$id", locality = "London", country = "GB")
|
||||||
val baseDirectory = driverDSL.driverDirectory / verifierName.organisation
|
val baseDirectory = (driverDSL.driverDirectory / verifierName.organisation).createDirectories()
|
||||||
val config = createConfiguration(baseDirectory, address)
|
val config = createConfiguration(baseDirectory, address)
|
||||||
val configFilename = "verifier.conf"
|
val configFilename = "verifier.conf"
|
||||||
writeConfig(baseDirectory, configFilename, config)
|
writeConfig(baseDirectory, configFilename, config)
|
||||||
|
@ -7,6 +7,7 @@ import net.corda.core.messaging.startFlow
|
|||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
import net.corda.finance.flows.CashIssueFlow
|
import net.corda.finance.flows.CashIssueFlow
|
||||||
import net.corda.finance.flows.CashPaymentFlow
|
import net.corda.finance.flows.CashPaymentFlow
|
||||||
@ -14,7 +15,6 @@ import net.corda.node.services.config.VerifierType
|
|||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
import net.corda.testing.DUMMY_NOTARY_SERVICE_NAME
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
@ -129,12 +129,12 @@ class VerifierTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `single verifier works with a node`() {
|
fun `single verifier works with a node`() {
|
||||||
verifierDriver(extraCordappPackagesToScan = listOf("net.corda.finance.contracts")) {
|
verifierDriver(extraCordappPackagesToScan = listOf("net.corda.finance.contracts")) {
|
||||||
val aliceFuture = startNode(providedName = ALICE.name)
|
val (notaryNode, aliceNode) = listOf(
|
||||||
val notaryFuture = startNotaryNode(DUMMY_NOTARY.name, verifierType = VerifierType.OutOfProcess)
|
startNotaryNode(DUMMY_NOTARY.name, verifierType = VerifierType.OutOfProcess),
|
||||||
val aliceNode = aliceFuture.get()
|
startNode(providedName = ALICE.name)
|
||||||
val notaryNode = notaryFuture.get()
|
).transpose().getOrThrow()
|
||||||
val alice = aliceNode.rpc.wellKnownPartyFromX500Name(ALICE_NAME)!!
|
val alice = aliceNode.rpc.wellKnownPartyFromX500Name(ALICE_NAME)!!
|
||||||
val notary = notaryNode.rpc.notaryPartyFromX500Name(DUMMY_NOTARY_SERVICE_NAME)!!
|
val notary = notaryNode.rpc.notaryPartyFromX500Name(DUMMY_NOTARY.name)!!
|
||||||
startVerifier(notaryNode)
|
startVerifier(notaryNode)
|
||||||
aliceNode.rpc.startFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), notary).returnValue.get()
|
aliceNode.rpc.startFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), notary).returnValue.get()
|
||||||
notaryNode.waitUntilNumberOfVerifiers(1)
|
notaryNode.waitUntilNumberOfVerifiers(1)
|
||||||
|
Reference in New Issue
Block a user