mirror of
https://github.com/corda/corda.git
synced 2025-06-18 07:08:15 +00:00
Merge remote-tracking branch 'open/master' into colljos-os-merge-rc01
This commit is contained in:
@ -288,11 +288,13 @@ class FlowStackSnapshotTest {
|
||||
|
||||
@Test
|
||||
fun `flowStackSnapshot object is serializable`() {
|
||||
val mockNet = MockNetwork(threadPerNode = true)
|
||||
val mockNet = MockNetwork(emptyList(), threadPerNode = true)
|
||||
val node = mockNet.createPartyNode()
|
||||
node.registerInitiatedFlow(DummyFlow::class.java)
|
||||
node.services.startFlow(FlowStackSnapshotSerializationTestingFlow()).resultFuture.get()
|
||||
val thrown = try {
|
||||
// Due to the [MockNetwork] implementation, the easiest way to trigger object serialization process is at
|
||||
// the network stopping stage.
|
||||
mockNet.stopNodes()
|
||||
null
|
||||
} catch (exception: Exception) {
|
||||
|
@ -45,8 +45,8 @@ import kotlin.concurrent.thread
|
||||
* a service is addressed.
|
||||
*/
|
||||
@ThreadSafe
|
||||
class InMemoryMessagingNetwork(
|
||||
val sendManuallyPumped: Boolean,
|
||||
class InMemoryMessagingNetwork internal constructor(
|
||||
private val sendManuallyPumped: Boolean,
|
||||
private val servicePeerAllocationStrategy: ServicePeerAllocationStrategy = InMemoryMessagingNetwork.ServicePeerAllocationStrategy.Random(),
|
||||
private val messagesInFlight: ReusableLatch = ReusableLatch()
|
||||
) : SingletonSerializeAsToken() {
|
||||
@ -94,7 +94,7 @@ class InMemoryMessagingNetwork(
|
||||
/**
|
||||
* Creates a node at the given address: useful if you want to recreate a node to simulate a restart.
|
||||
*
|
||||
* @param manuallyPumped if set to true, then you are expected to call the [InMemoryMessaging.pump] method on the [InMemoryMessaging]
|
||||
* @param manuallyPumped if set to true, then you are expected to call [InMemoryMessaging.pumpReceive]
|
||||
* in order to cause the delivery of a single message, which will occur on the thread of the caller. If set to false
|
||||
* then this class will set up a background thread to deliver messages asynchronously, if the handler specifies no
|
||||
* executor.
|
||||
@ -153,9 +153,6 @@ class InMemoryMessagingNetwork(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val everyoneOnline: AllPossibleRecipients = object : AllPossibleRecipients {}
|
||||
|
||||
fun stop() {
|
||||
val nodes = synchronized(this) {
|
||||
counter = -1
|
||||
@ -226,7 +223,7 @@ class InMemoryMessagingNetwork(
|
||||
return transfer
|
||||
}
|
||||
|
||||
fun pumpSendInternal(transfer: MessageTransfer) {
|
||||
private fun pumpSendInternal(transfer: MessageTransfer) {
|
||||
when (transfer.recipients) {
|
||||
is PeerHandle -> getQueueForPeerHandle(transfer.recipients).add(transfer)
|
||||
is ServiceHandle -> {
|
||||
@ -271,7 +268,8 @@ class InMemoryMessagingNetwork(
|
||||
private val peerHandle: PeerHandle,
|
||||
private val executor: AffinityExecutor,
|
||||
private val database: CordaPersistence) : SingletonSerializeAsToken(), MessagingService {
|
||||
inner class Handler(val topicSession: String, val callback: MessageHandler) : MessageHandlerRegistration
|
||||
privateinner class Handler(val topicSession: String,
|
||||
val callback: MessageHandler) : MessageHandlerRegistration
|
||||
|
||||
@Volatile
|
||||
private var running = true
|
||||
|
@ -1,39 +0,0 @@
|
||||
package net.corda.testing.node
|
||||
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.crypto.entropyToKeyPair
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.concurrent.doneFuture
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.services.NetworkMapCache
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.network.PersistentNetworkMapCache
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.testing.getTestPartyAndCertificate
|
||||
import rx.Observable
|
||||
import rx.subjects.PublishSubject
|
||||
import java.math.BigInteger
|
||||
|
||||
/**
|
||||
* Network map cache with no backing map service.
|
||||
*/
|
||||
class MockNetworkMapCache(database: CordaPersistence) : PersistentNetworkMapCache(database, emptyList()) {
|
||||
private companion object {
|
||||
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_C_ADDR = NetworkHostAndPort("bankC", 8080)
|
||||
val BANK_D_ADDR = NetworkHostAndPort("bankD", 8080)
|
||||
}
|
||||
|
||||
override val changed: Observable<NetworkMapCache.MapChange> = PublishSubject.create<NetworkMapCache.MapChange>()
|
||||
override val nodeReady: CordaFuture<Void?> get() = doneFuture(null)
|
||||
|
||||
init {
|
||||
val mockNodeA = NodeInfo(listOf(BANK_C_ADDR), listOf(BANK_C), 1, serial = 1L)
|
||||
val mockNodeB = NodeInfo(listOf(BANK_D_ADDR), listOf(BANK_D), 1, serial = 1L)
|
||||
addNode(mockNodeA)
|
||||
addNode(mockNodeB)
|
||||
}
|
||||
}
|
||||
|
@ -22,14 +22,14 @@ import net.corda.core.node.services.KeyManagementService
|
||||
import net.corda.core.serialization.SerializationWhitelist
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.AbstractNode
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.services.api.SchemaService
|
||||
import net.corda.node.services.config.BFTSMaRtConfiguration
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.NotaryConfig
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.node.services.api.SchemaService
|
||||
import net.corda.node.services.config.*
|
||||
import net.corda.node.services.keys.E2ETestKeyManagementService
|
||||
import net.corda.node.services.messaging.MessagingService
|
||||
import net.corda.node.services.transactions.BFTNonValidatingNotaryService
|
||||
@ -37,12 +37,15 @@ import net.corda.node.services.transactions.BFTSMaRt
|
||||
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
||||
import net.corda.node.utilities.AffinityExecutor
|
||||
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
||||
import net.corda.nodeapi.internal.NotaryInfo
|
||||
import net.corda.nodeapi.internal.NetworkParametersCopier
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.testThreadFactory
|
||||
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
@ -71,13 +74,13 @@ data class MockNetworkParameters(
|
||||
val servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = InMemoryMessagingNetwork.ServicePeerAllocationStrategy.Random(),
|
||||
val defaultFactory: (MockNodeArgs) -> MockNetwork.MockNode = MockNetwork::MockNode,
|
||||
val initialiseSerialization: Boolean = true,
|
||||
val cordappPackages: List<String> = emptyList()) {
|
||||
val notarySpecs: List<MockNetwork.NotarySpec> = listOf(MockNetwork.NotarySpec(DUMMY_NOTARY_NAME))) {
|
||||
fun setNetworkSendManuallyPumped(networkSendManuallyPumped: Boolean) = copy(networkSendManuallyPumped = networkSendManuallyPumped)
|
||||
fun setThreadPerNode(threadPerNode: Boolean) = copy(threadPerNode = threadPerNode)
|
||||
fun setServicePeerAllocationStrategy(servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy) = copy(servicePeerAllocationStrategy = servicePeerAllocationStrategy)
|
||||
fun setDefaultFactory(defaultFactory: (MockNodeArgs) -> MockNetwork.MockNode) = copy(defaultFactory = defaultFactory)
|
||||
fun setInitialiseSerialization(initialiseSerialization: Boolean) = copy(initialiseSerialization = initialiseSerialization)
|
||||
fun setCordappPackages(cordappPackages: List<String>) = copy(cordappPackages = cordappPackages)
|
||||
fun setNotarySpecs(notarySpecs: List<MockNetwork.NotarySpec>) = copy(notarySpecs = notarySpecs)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,7 +93,8 @@ data class MockNodeParameters(
|
||||
val forcedID: Int? = null,
|
||||
val legalName: CordaX500Name? = null,
|
||||
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||
val configOverrides: (NodeConfiguration) -> Any? = {}) {
|
||||
val configOverrides: (NodeConfiguration) -> Any? = {},
|
||||
val version: VersionInfo = MOCK_VERSION_INFO) {
|
||||
fun setForcedID(forcedID: Int?) = copy(forcedID = forcedID)
|
||||
fun setLegalName(legalName: CordaX500Name?) = copy(legalName = legalName)
|
||||
fun setEntropyRoot(entropyRoot: BigInteger) = copy(entropyRoot = entropyRoot)
|
||||
@ -101,7 +105,8 @@ data class MockNodeArgs(
|
||||
val config: NodeConfiguration,
|
||||
val network: MockNetwork,
|
||||
val id: Int,
|
||||
val entropyRoot: BigInteger
|
||||
val entropyRoot: BigInteger,
|
||||
val version: VersionInfo = MOCK_VERSION_INFO
|
||||
)
|
||||
|
||||
/**
|
||||
@ -120,16 +125,17 @@ data class MockNodeArgs(
|
||||
* By default a single notary node is automatically started, which forms part of the network parameters for all the nodes.
|
||||
* This node is available by calling [defaultNotaryNode].
|
||||
*/
|
||||
class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParameters(),
|
||||
class MockNetwork(private val cordappPackages: List<String>,
|
||||
defaultParameters: MockNetworkParameters = MockNetworkParameters(),
|
||||
private val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped,
|
||||
private val threadPerNode: Boolean = defaultParameters.threadPerNode,
|
||||
servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy,
|
||||
private val defaultFactory: (MockNodeArgs) -> MockNode = defaultParameters.defaultFactory,
|
||||
initialiseSerialization: Boolean = defaultParameters.initialiseSerialization,
|
||||
private val notarySpecs: List<NotarySpec> = listOf(NotarySpec(DUMMY_NOTARY_NAME)),
|
||||
private val cordappPackages: List<String> = defaultParameters.cordappPackages) {
|
||||
private val notarySpecs: List<NotarySpec> = defaultParameters.notarySpecs) {
|
||||
/** Helper constructor for creating a [MockNetwork] with custom parameters from Java. */
|
||||
constructor(parameters: MockNetworkParameters) : this(defaultParameters = parameters)
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: List<String>, parameters: MockNetworkParameters = MockNetworkParameters()) : this(cordappPackages, defaultParameters = parameters)
|
||||
|
||||
init {
|
||||
// Apache SSHD for whatever reason registers a SFTP FileSystemProvider - which gets loaded by JimFS.
|
||||
@ -147,9 +153,13 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
||||
private val networkId = random63BitValue()
|
||||
private val networkParameters: NetworkParametersCopier
|
||||
private val _nodes = mutableListOf<MockNode>()
|
||||
private val serializationEnv = setGlobalSerialization(initialiseSerialization)
|
||||
private val serializationEnv = try {
|
||||
setGlobalSerialization(initialiseSerialization)
|
||||
} catch (e: IllegalStateException) {
|
||||
throw IllegalStateException("Using more than one MockNetwork simultaneously is not supported.", e)
|
||||
}
|
||||
private val sharedUserCount = AtomicInteger(0)
|
||||
/** A read only view of the current set of executing nodes. */
|
||||
/** A read only view of the current set of nodes. */
|
||||
val nodes: List<MockNode> get() = _nodes
|
||||
|
||||
/**
|
||||
@ -220,12 +230,12 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
||||
}
|
||||
|
||||
private fun generateNotaryIdentities(): List<NotaryInfo> {
|
||||
return notarySpecs.mapIndexed { index, spec ->
|
||||
return notarySpecs.mapIndexed { index, (name, validating) ->
|
||||
val identity = ServiceIdentityGenerator.generateToDisk(
|
||||
dirs = listOf(baseDirectory(nextNodeId + index)),
|
||||
serviceName = spec.name,
|
||||
serviceName = name,
|
||||
serviceId = "identity")
|
||||
NotaryInfo(identity, spec.validating)
|
||||
NotaryInfo(identity, validating)
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,7 +250,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
||||
open class MockNode(args: MockNodeArgs) : AbstractNode(
|
||||
args.config,
|
||||
TestClock(Clock.systemUTC()),
|
||||
MOCK_VERSION_INFO,
|
||||
args.version,
|
||||
CordappLoader.createDefaultWithTestPackages(args.config, args.network.cordappPackages),
|
||||
args.network.busyLatch
|
||||
) {
|
||||
@ -385,14 +395,14 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
||||
|
||||
private fun <N : MockNode> createNodeImpl(parameters: MockNodeParameters, nodeFactory: (MockNodeArgs) -> N, start: Boolean): N {
|
||||
val id = parameters.forcedID ?: nextNodeId++
|
||||
val config = testNodeConfiguration(
|
||||
baseDirectory = baseDirectory(id).createDirectories(),
|
||||
myLegalName = parameters.legalName ?: CordaX500Name(organisation = "Mock Company $id", locality = "London", country = "GB")).also {
|
||||
val config = mockNodeConfiguration().also {
|
||||
doReturn(baseDirectory(id).createDirectories()).whenever(it).baseDirectory
|
||||
doReturn(parameters.legalName ?: CordaX500Name("Mock Company $id", "London", "GB")).whenever(it).myLegalName
|
||||
doReturn(makeTestDataSourceProperties("node_$id", "net_$networkId")).whenever(it).dataSourceProperties
|
||||
doReturn(makeTestDatabaseProperties("node_$id")).whenever(it).database
|
||||
parameters.configOverrides(it)
|
||||
}
|
||||
val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot))
|
||||
val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version))
|
||||
_nodes += node
|
||||
if (start) {
|
||||
node.start()
|
||||
@ -474,3 +484,24 @@ open class MessagingServiceSpy(val messagingService: MessagingService) : Messagi
|
||||
fun StartedNode<MockNetwork.MockNode>.setMessagingServiceSpy(messagingServiceSpy: MessagingServiceSpy) {
|
||||
internals.setMessagingServiceSpy(messagingServiceSpy)
|
||||
}
|
||||
|
||||
private fun mockNodeConfiguration(): NodeConfiguration {
|
||||
abstract class AbstractNodeConfiguration : NodeConfiguration
|
||||
return rigorousMock<AbstractNodeConfiguration>().also {
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn(emptyList<User>()).whenever(it).rpcUsers
|
||||
doReturn(null).whenever(it).notary
|
||||
doReturn(DatabaseConfig()).whenever(it).database
|
||||
doReturn("").whenever(it).emailAddress
|
||||
doReturn("").whenever(it).exportJMXto
|
||||
doReturn(true).whenever(it).devMode
|
||||
doReturn(null).whenever(it).compatibilityZoneURL
|
||||
doReturn(emptyList<CertChainPolicyConfig>()).whenever(it).certificateChainCheckPolicies
|
||||
doReturn(VerifierType.InMemory).whenever(it).verifierType
|
||||
doReturn(5).whenever(it).messageRedeliveryDelaySeconds
|
||||
doReturn(5.seconds.toMillis()).whenever(it).additionalNodeInfoPollingFrequencyMsec
|
||||
doReturn(null).whenever(it).devModeOptions
|
||||
doReturn(true).whenever(it).useAMQPBridges
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ import java.sql.Connection
|
||||
import java.time.Clock
|
||||
import java.util.*
|
||||
|
||||
fun makeTestIdentityService(identities: Iterable<PartyAndCertificate> = emptySet()) = InMemoryIdentityService(identities, DEV_TRUST_ROOT)
|
||||
fun makeTestIdentityService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities, DEV_TRUST_ROOT)
|
||||
/**
|
||||
* A singleton utility that only provides a mock identity, key and storage service. However, this is sufficient for
|
||||
* building chains of transactions and verifying them. It isn't sufficient for testing flows however.
|
||||
@ -68,8 +68,8 @@ open class MockServices private constructor(
|
||||
cordappLoader: CordappLoader,
|
||||
override val validatedTransactions: WritableTransactionStorage,
|
||||
override val identityService: IdentityServiceInternal,
|
||||
private val initialIdentityName: CordaX500Name,
|
||||
val keys: Array<out KeyPair>
|
||||
private val initialIdentity: TestIdentity,
|
||||
private val moreKeys: Array<out KeyPair>
|
||||
) : ServiceHub, StateLoader by validatedTransactions {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@ -144,22 +144,22 @@ open class MockServices private constructor(
|
||||
|
||||
/**
|
||||
* Makes database and mock services appropriate for unit tests.
|
||||
* @param keys a list of [KeyPair] instances to be used by [MockServices].
|
||||
* @param moreKeys a list of additional [KeyPair] instances to be used by [MockServices].
|
||||
* @param identityService an instance of [IdentityServiceInternal], see [makeTestIdentityService].
|
||||
* @param initialIdentityName the name of the first (typically sole) identity the services will represent.
|
||||
* @param initialIdentity the first (typically sole) identity the services will represent.
|
||||
* @return a pair where the first element is the instance of [CordaPersistence] and the second is [MockServices].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun makeTestDatabaseAndMockServices(keys: List<KeyPair>,
|
||||
fun makeTestDatabaseAndMockServices(cordappPackages: List<String>,
|
||||
identityService: IdentityServiceInternal,
|
||||
cordappPackages: List<String> = emptyList(),
|
||||
initialIdentityName: CordaX500Name): Pair<CordaPersistence, MockServices> {
|
||||
initialIdentity: TestIdentity,
|
||||
vararg moreKeys: KeyPair): Pair<CordaPersistence, MockServices> {
|
||||
val cordappLoader = CordappLoader.createWithTestPackages(cordappPackages)
|
||||
val dataSourceProps = makeTestDataSourceProperties(initialIdentityName.organisation)
|
||||
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas)
|
||||
val database = configureDatabase(dataSourceProps, makeTestDatabaseProperties(initialIdentityName.organisation), identityService, schemaService)
|
||||
val mockService = database.transaction {
|
||||
object : MockServices(cordappLoader, identityService, initialIdentityName, keys.toTypedArray()) {
|
||||
object : MockServices(cordappLoader, identityService, initialIdentity, moreKeys) {
|
||||
override val vaultService: VaultServiceInternal = makeVaultService(database.hibernateConfig, schemaService)
|
||||
|
||||
override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable<SignedTransaction>) {
|
||||
@ -175,13 +175,18 @@ open class MockServices private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private constructor(cordappLoader: CordappLoader, identityService: IdentityServiceInternal, initialIdentityName: CordaX500Name, keys: Array<out KeyPair>) : this(cordappLoader, MockTransactionStorage(), identityService, initialIdentityName, keys)
|
||||
constructor(cordappPackages: List<String>, identityService: IdentityServiceInternal, initialIdentityName: CordaX500Name, vararg keys: KeyPair) : this(CordappLoader.createWithTestPackages(cordappPackages), identityService, initialIdentityName, keys)
|
||||
constructor(cordappPackages: List<String>, identityService: IdentityServiceInternal, initialIdentity: TestIdentity, vararg moreKeys: KeyPair) : this(CordappLoader.createWithTestPackages(cordappPackages), identityService, initialIdentity.name, arrayOf(initialIdentity.key) + moreKeys)
|
||||
constructor(identityService: IdentityServiceInternal, initialIdentityName: CordaX500Name, vararg keys: KeyPair) : this(emptyList(), identityService, initialIdentityName, *keys)
|
||||
constructor(identityService: IdentityServiceInternal, initialIdentityName: CordaX500Name) : this(identityService, initialIdentityName, generateKeyPair())
|
||||
private constructor(cordappLoader: CordappLoader, identityService: IdentityServiceInternal, initialIdentity: TestIdentity, moreKeys: Array<out KeyPair>) : this(cordappLoader, MockTransactionStorage(), identityService, initialIdentity, moreKeys)
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: List<String>, identityService: IdentityServiceInternal = makeTestIdentityService(), initialIdentity: TestIdentity, vararg moreKeys: KeyPair) : this(CordappLoader.createWithTestPackages(cordappPackages), identityService, initialIdentity, moreKeys)
|
||||
|
||||
val key: KeyPair get() = keys.first()
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: List<String>, identityService: IdentityServiceInternal = makeTestIdentityService(), initialIdentityName: CordaX500Name, key: KeyPair, vararg moreKeys: KeyPair) : this(cordappPackages, identityService, TestIdentity(initialIdentityName, key), *moreKeys)
|
||||
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: List<String>, identityService: IdentityServiceInternal = makeTestIdentityService(), initialIdentityName: CordaX500Name) : this(cordappPackages, identityService, TestIdentity(initialIdentityName))
|
||||
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: List<String>, identityService: IdentityServiceInternal = makeTestIdentityService(), vararg moreKeys: KeyPair) : this(cordappPackages, identityService, TestIdentity.fresh("MockServices"), *moreKeys)
|
||||
|
||||
override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable<SignedTransaction>) {
|
||||
txs.forEach {
|
||||
@ -190,16 +195,14 @@ open class MockServices private constructor(
|
||||
}
|
||||
|
||||
final override val attachments = MockAttachmentStorage()
|
||||
override val keyManagementService: KeyManagementService by lazy { MockKeyManagementService(identityService, *keys) }
|
||||
|
||||
override val keyManagementService: KeyManagementService by lazy { MockKeyManagementService(identityService, *arrayOf(initialIdentity.keyPair) + moreKeys) }
|
||||
override val vaultService: VaultService get() = throw UnsupportedOperationException()
|
||||
override val contractUpgradeService: ContractUpgradeService get() = throw UnsupportedOperationException()
|
||||
override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException()
|
||||
override val clock: Clock get() = Clock.systemUTC()
|
||||
override val myInfo: NodeInfo
|
||||
get() {
|
||||
val identity = getTestPartyAndCertificate(initialIdentityName, key.public)
|
||||
return NodeInfo(emptyList(), listOf(identity), 1, serial = 1L)
|
||||
return NodeInfo(emptyList(), listOf(initialIdentity.identity), 1, serial = 1L)
|
||||
}
|
||||
override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2)
|
||||
val mockCordappProvider = MockCordappProvider(cordappLoader, attachments)
|
||||
|
@ -2,8 +2,6 @@
|
||||
|
||||
package net.corda.testing.node
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.context.Actor
|
||||
import net.corda.core.context.AuthServiceId
|
||||
import net.corda.core.context.InvocationContext
|
||||
@ -13,26 +11,34 @@ import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.FlowStateMachine
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.serialization.internal.effectiveSerializationEnv
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.services.api.StartedNodeServices
|
||||
import net.corda.node.services.config.*
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
import net.corda.testing.node.makeTestIdentityService
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
||||
import java.nio.file.Path
|
||||
import net.corda.testing.dsl.*
|
||||
|
||||
/**
|
||||
* Creates and tests a ledger built by the passed in dsl.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun ServiceHub.ledger(
|
||||
notary: Party,
|
||||
dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
|
||||
notary: Party = TestIdentity.fresh("ledger notary").party,
|
||||
script: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
|
||||
): LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> {
|
||||
return LedgerDSL(TestLedgerDSLInterpreter(this), notary).apply(dsl)
|
||||
val serializationExists = try {
|
||||
effectiveSerializationEnv
|
||||
true
|
||||
} catch (e: IllegalStateException) {
|
||||
false
|
||||
}
|
||||
return LedgerDSL(TestLedgerDSLInterpreter(this), notary).apply {
|
||||
if (serializationExists) {
|
||||
script()
|
||||
} else {
|
||||
SerializationEnvironmentRule.run("ledger") { script() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,37 +46,12 @@ fun ServiceHub.ledger(
|
||||
*
|
||||
* @see LedgerDSLInterpreter._transaction
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun ServiceHub.transaction(
|
||||
notary: Party,
|
||||
dsl: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail
|
||||
notary: Party = TestIdentity.fresh("transaction notary").party,
|
||||
script: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail
|
||||
) = ledger(notary) {
|
||||
dsl(TransactionDSL(TestTransactionDSLInterpreter(interpreter, TransactionBuilder(notary)), notary))
|
||||
}
|
||||
|
||||
fun testNodeConfiguration(
|
||||
baseDirectory: Path,
|
||||
myLegalName: CordaX500Name): NodeConfiguration {
|
||||
abstract class MockableNodeConfiguration : NodeConfiguration // Otherwise Mockito is defeated by val getters.
|
||||
return rigorousMock<MockableNodeConfiguration>().also {
|
||||
doReturn(baseDirectory).whenever(it).baseDirectory
|
||||
doReturn(myLegalName).whenever(it).myLegalName
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn(emptyList<User>()).whenever(it).rpcUsers
|
||||
doReturn(null).whenever(it).notary
|
||||
doReturn(makeTestDataSourceProperties(myLegalName.organisation)).whenever(it).dataSourceProperties
|
||||
doReturn(makeTestDatabaseProperties(myLegalName.organisation)).whenever(it).database
|
||||
doReturn("").whenever(it).emailAddress
|
||||
doReturn("").whenever(it).exportJMXto
|
||||
doReturn(true).whenever(it).devMode
|
||||
doReturn(null).whenever(it).compatibilityZoneURL
|
||||
doReturn(emptyList<CertChainPolicyConfig>()).whenever(it).certificateChainCheckPolicies
|
||||
doReturn(VerifierType.InMemory).whenever(it).verifierType
|
||||
doReturn(5).whenever(it).messageRedeliveryDelaySeconds
|
||||
doReturn(5.seconds.toMillis()).whenever(it).additionalNodeInfoPollingFrequencyMsec
|
||||
doReturn(null).whenever(it).devModeOptions
|
||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration //Enterprise only
|
||||
}
|
||||
TransactionDSL(TestTransactionDSLInterpreter(interpreter, TransactionBuilder(notary)), notary).script()
|
||||
}
|
||||
|
||||
fun testActor(owningLegalIdentity: CordaX500Name = CordaX500Name("Test Company Inc.", "London", "GB")) = Actor(Actor.Id("Only For Testing"), AuthServiceId("TEST"), owningLegalIdentity)
|
||||
|
@ -10,6 +10,7 @@ import net.corda.cordform.CordformContext
|
||||
import net.corda.cordform.CordformNode
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.concurrent.firstOf
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.ThreadBox
|
||||
import net.corda.core.internal.concurrent.*
|
||||
@ -34,7 +35,8 @@ import net.corda.node.services.transactions.RaftNonValidatingNotaryService
|
||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
||||
import net.corda.nodeapi.internal.*
|
||||
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
||||
import net.corda.nodeapi.internal.addShutdownHook
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
import net.corda.nodeapi.internal.config.toConfig
|
||||
@ -42,8 +44,13 @@ import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.crypto.addOrReplaceCertificate
|
||||
import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore
|
||||
import net.corda.nodeapi.internal.crypto.save
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier
|
||||
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.BOB_NAME
|
||||
import net.corda.testing.DUMMY_BANK_A_NAME
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.driver.*
|
||||
import net.corda.testing.node.ClusterSpec
|
||||
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
||||
@ -244,6 +251,7 @@ class DriverDSLImpl(
|
||||
}
|
||||
|
||||
internal fun startCordformNodes(cordforms: List<CordformNode>): CordaFuture<*> {
|
||||
check(compatibilityZone == null) { "Cordform nodes should be run without compatibilityZone configuration" }
|
||||
val clusterNodes = HashMultimap.create<ClusterType, CordaX500Name>()
|
||||
val notaryInfos = ArrayList<NotaryInfo>()
|
||||
|
||||
@ -353,7 +361,7 @@ class DriverDSLImpl(
|
||||
}
|
||||
val notaryInfos = generateNotaryIdentities()
|
||||
// The network parameters must be serialised before starting any of the nodes
|
||||
networkParameters = NetworkParametersCopier(testNetworkParameters(notaryInfos))
|
||||
if (compatibilityZone == null) networkParameters = NetworkParametersCopier(testNetworkParameters(notaryInfos))
|
||||
val nodeHandles = startNotaries()
|
||||
_notaries = notaryInfos.zip(nodeHandles) { (identity, validating), nodes -> NotaryHandle(identity, validating, nodes) }
|
||||
}
|
||||
@ -514,7 +522,7 @@ class DriverDSLImpl(
|
||||
val configuration = config.parseAsNodeConfiguration()
|
||||
val baseDirectory = configuration.baseDirectory.createDirectories()
|
||||
nodeInfoFilesCopier?.addConfig(baseDirectory)
|
||||
networkParameters!!.install(baseDirectory)
|
||||
networkParameters?.install(baseDirectory)
|
||||
val onNodeExit: () -> Unit = {
|
||||
nodeInfoFilesCopier?.removeConfig(baseDirectory)
|
||||
countObservables.remove(configuration.myLegalName)
|
||||
@ -889,7 +897,8 @@ fun <A> internalDriver(
|
||||
}
|
||||
|
||||
fun getTimestampAsDirectoryName(): String {
|
||||
return DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(ZoneOffset.UTC).format(Instant.now())
|
||||
// Add a random number in case 2 tests are started in the same instant.
|
||||
return DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(ZoneOffset.UTC).format(Instant.now()) + random63BitValue()
|
||||
}
|
||||
|
||||
fun writeConfig(path: Path, filename: String, config: Config) {
|
||||
|
@ -12,10 +12,10 @@ import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.services.config.*
|
||||
import net.corda.nodeapi.internal.NetworkParametersCopier
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.IntegrationTest
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.getFreeLocalPorts
|
||||
import net.corda.testing.internal.testThreadFactory
|
||||
|
@ -17,7 +17,6 @@ import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.internal.security.RPCSecurityManagerImpl
|
||||
import net.corda.node.services.messaging.ArtemisMessagingServer
|
||||
import net.corda.node.services.messaging.RPCServer
|
||||
import net.corda.node.services.messaging.RPCServerConfiguration
|
||||
import net.corda.nodeapi.ArtemisTcpTransport
|
||||
@ -25,6 +24,7 @@ import net.corda.nodeapi.ConnectionDirection
|
||||
import net.corda.nodeapi.RPCApi
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT
|
||||
import net.corda.testing.MAX_MESSAGE_SIZE
|
||||
import net.corda.testing.driver.JmxPolicy
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.node.NotarySpec
|
||||
@ -46,7 +46,7 @@ import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ
|
||||
import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl
|
||||
import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy
|
||||
import org.apache.activemq.artemis.core.settings.impl.AddressSettings
|
||||
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection
|
||||
import org.apache.activemq.artemis.spi.core.remoting.Connection
|
||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager3
|
||||
import java.lang.reflect.Method
|
||||
import java.nio.file.Path
|
||||
@ -133,11 +133,11 @@ fun <A> rpcDriver(
|
||||
private class SingleUserSecurityManager(val rpcUser: User) : ActiveMQSecurityManager3 {
|
||||
override fun validateUser(user: String?, password: String?) = isValid(user, password)
|
||||
override fun validateUserAndRole(user: String?, password: String?, roles: MutableSet<Role>?, checkType: CheckType?) = isValid(user, password)
|
||||
override fun validateUser(user: String?, password: String?, remotingConnection: RemotingConnection?): String? {
|
||||
override fun validateUser(user: String?, password: String?, connection: Connection?): String? {
|
||||
return validate(user, password)
|
||||
}
|
||||
|
||||
override fun validateUserAndRole(user: String?, password: String?, roles: MutableSet<Role>?, checkType: CheckType?, address: String?, connection: RemotingConnection?): String? {
|
||||
override fun validateUserAndRole(user: String?, password: String?, roles: MutableSet<Role>?, checkType: CheckType?, address: String?, connection: Connection?): String? {
|
||||
return validate(user, password)
|
||||
}
|
||||
|
||||
@ -226,8 +226,8 @@ data class RPCDriverDSL(
|
||||
fun <I : RPCOps> startInVmRpcServer(
|
||||
rpcUser: User = rpcTestUser,
|
||||
nodeLegalName: CordaX500Name = fakeNodeLegalName,
|
||||
maxFileSize: Int = ArtemisMessagingServer.MAX_FILE_SIZE,
|
||||
maxBufferedBytesPerClient: Long = 10L * ArtemisMessagingServer.MAX_FILE_SIZE,
|
||||
maxFileSize: Int = MAX_MESSAGE_SIZE,
|
||||
maxBufferedBytesPerClient: Long = 10L * MAX_MESSAGE_SIZE,
|
||||
configuration: RPCServerConfiguration = RPCServerConfiguration.default,
|
||||
ops: I
|
||||
): CordaFuture<RpcServerHandle> {
|
||||
@ -294,8 +294,8 @@ data class RPCDriverDSL(
|
||||
serverName: String = "driver-rpc-server-${random63BitValue()}",
|
||||
rpcUser: User = rpcTestUser,
|
||||
nodeLegalName: CordaX500Name = fakeNodeLegalName,
|
||||
maxFileSize: Int = ArtemisMessagingServer.MAX_FILE_SIZE,
|
||||
maxBufferedBytesPerClient: Long = 10L * ArtemisMessagingServer.MAX_FILE_SIZE,
|
||||
maxFileSize: Int = MAX_MESSAGE_SIZE,
|
||||
maxBufferedBytesPerClient: Long = 10L * MAX_MESSAGE_SIZE,
|
||||
configuration: RPCServerConfiguration = RPCServerConfiguration.default,
|
||||
customPort: NetworkHostAndPort? = null,
|
||||
ops: I
|
||||
@ -377,8 +377,8 @@ data class RPCDriverDSL(
|
||||
fun startRpcBroker(
|
||||
serverName: String = "driver-rpc-server-${random63BitValue()}",
|
||||
rpcUser: User = rpcTestUser,
|
||||
maxFileSize: Int = ArtemisMessagingServer.MAX_FILE_SIZE,
|
||||
maxBufferedBytesPerClient: Long = 10L * ArtemisMessagingServer.MAX_FILE_SIZE,
|
||||
maxFileSize: Int = MAX_MESSAGE_SIZE,
|
||||
maxBufferedBytesPerClient: Long = 10L * MAX_MESSAGE_SIZE,
|
||||
customPort: NetworkHostAndPort? = null
|
||||
): CordaFuture<RpcBrokerHandle> {
|
||||
val hostAndPort = customPort ?: driverDSL.portAllocation.nextHostAndPort()
|
||||
@ -401,8 +401,8 @@ data class RPCDriverDSL(
|
||||
|
||||
fun startInVmRpcBroker(
|
||||
rpcUser: User = rpcTestUser,
|
||||
maxFileSize: Int = ArtemisMessagingServer.MAX_FILE_SIZE,
|
||||
maxBufferedBytesPerClient: Long = 10L * ArtemisMessagingServer.MAX_FILE_SIZE
|
||||
maxFileSize: Int = MAX_MESSAGE_SIZE,
|
||||
maxBufferedBytesPerClient: Long = 10L * MAX_MESSAGE_SIZE
|
||||
): CordaFuture<RpcBrokerHandle> {
|
||||
return driverDSL.executorService.fork {
|
||||
val artemisConfig = createInVmRpcServerArtemisConfig(maxFileSize, maxBufferedBytesPerClient)
|
||||
@ -430,7 +430,7 @@ data class RPCDriverDSL(
|
||||
brokerHandle: RpcBrokerHandle
|
||||
): RpcServerHandle {
|
||||
val locator = ActiveMQClient.createServerLocatorWithoutHA(brokerHandle.clientTransportConfiguration).apply {
|
||||
minLargeMessageSize = ArtemisMessagingServer.MAX_FILE_SIZE
|
||||
minLargeMessageSize = MAX_MESSAGE_SIZE
|
||||
isUseGlobalPools = false
|
||||
}
|
||||
val rpcSecurityManager = RPCSecurityManagerImpl.fromUserList(users = listOf(rpcUser), id = AuthServiceId("TEST_SECURITY_MANAGER"))
|
||||
|
@ -1,20 +1,17 @@
|
||||
package net.corda.testing.node.network
|
||||
package net.corda.testing.node.internal.network
|
||||
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.hours
|
||||
import net.corda.nodeapi.internal.DigitalSignatureWithCert
|
||||
import net.corda.nodeapi.internal.NetworkMap
|
||||
import net.corda.nodeapi.internal.NetworkParameters
|
||||
import net.corda.nodeapi.internal.SignedNetworkMap
|
||||
import net.corda.nodeapi.internal.network.DigitalSignatureWithCert
|
||||
import net.corda.nodeapi.internal.network.NetworkMap
|
||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
||||
import net.corda.nodeapi.internal.*
|
||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
@ -39,9 +36,11 @@ import javax.ws.rs.core.Response.ok
|
||||
|
||||
class NetworkMapServer(cacheTimeout: Duration,
|
||||
hostAndPort: NetworkHostAndPort,
|
||||
root_ca: CertificateAndKeyPair = ROOT_CA, // Default to ROOT_CA for testing.
|
||||
vararg additionalServices: Any) : Closeable {
|
||||
companion object {
|
||||
val stubNetworkParameter = NetworkParameters(1, emptyList(), 1.hours, 10, 10, Instant.now(), 10)
|
||||
val stubNetworkParameter = NetworkParameters(1, emptyList(), 10485760, 40000, Instant.now(), 10)
|
||||
private val serializedParameters = stubNetworkParameter.serialize()
|
||||
|
||||
private fun networkMapKeyAndCert(rootCAKeyAndCert: CertificateAndKeyPair): CertificateAndKeyPair {
|
||||
val networkMapKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
@ -56,9 +55,7 @@ class NetworkMapServer(cacheTimeout: Duration,
|
||||
}
|
||||
|
||||
private val server: Server
|
||||
// Default to ROOT_CA for testing.
|
||||
// TODO: make this configurable?
|
||||
private val service = InMemoryNetworkMapService(cacheTimeout, networkMapKeyAndCert(ROOT_CA))
|
||||
private val service = InMemoryNetworkMapService(cacheTimeout, networkMapKeyAndCert(root_ca))
|
||||
|
||||
init {
|
||||
server = Server(InetSocketAddress(hostAndPort.host, hostAndPort.port)).apply {
|
||||
@ -99,13 +96,18 @@ class NetworkMapServer(cacheTimeout: Duration,
|
||||
|
||||
@Path("network-map")
|
||||
class InMemoryNetworkMapService(private val cacheTimeout: Duration, private val networkMapKeyAndCert: CertificateAndKeyPair) {
|
||||
private val nodeInfoMap = mutableMapOf<SecureHash, SignedData<NodeInfo>>()
|
||||
private val nodeInfoMap = mutableMapOf<SecureHash, SignedNodeInfo>()
|
||||
private val parametersHash = serializedParameters.hash
|
||||
private val signedParameters = SignedData(
|
||||
serializedParameters,
|
||||
DigitalSignature.WithKey(networkMapKeyAndCert.keyPair.public, Crypto.doSign(networkMapKeyAndCert.keyPair.private, serializedParameters.bytes))
|
||||
)
|
||||
|
||||
@POST
|
||||
@Path("publish")
|
||||
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
|
||||
fun publishNodeInfo(input: InputStream): Response {
|
||||
val registrationData = input.readBytes().deserialize<SignedData<NodeInfo>>()
|
||||
val registrationData = input.readBytes().deserialize<SignedNodeInfo>()
|
||||
val nodeInfo = registrationData.verified()
|
||||
val nodeInfoHash = nodeInfo.serialize().sha256()
|
||||
nodeInfoMap.put(nodeInfoHash, registrationData)
|
||||
@ -115,7 +117,7 @@ class NetworkMapServer(cacheTimeout: Duration,
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||
fun getNetworkMap(): Response {
|
||||
val networkMap = NetworkMap(nodeInfoMap.keys.map { it }, SecureHash.randomSHA256())
|
||||
val networkMap = NetworkMap(nodeInfoMap.keys.toList(), parametersHash)
|
||||
val serializedNetworkMap = networkMap.serialize()
|
||||
val signature = Crypto.doSign(networkMapKeyAndCert.keyPair.private, serializedNetworkMap.bytes)
|
||||
val signedNetworkMap = SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(networkMapKeyAndCert.certificate.cert, signature))
|
||||
@ -143,7 +145,7 @@ class NetworkMapServer(cacheTimeout: Duration,
|
||||
@Path("network-parameter/{var}")
|
||||
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||
fun getNetworkParameter(@PathParam("var") networkParameterHash: String): Response {
|
||||
return Response.ok(stubNetworkParameter.serialize().bytes).build()
|
||||
return Response.ok(signedParameters.serialize().bytes).build()
|
||||
}
|
||||
|
||||
@GET
|
@ -2,6 +2,8 @@ package net.corda.testing.node;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class MockNodeFactoryInJavaTest {
|
||||
private static class CustomNode extends MockNetwork.MockNode {
|
||||
@ -16,10 +18,10 @@ public class MockNodeFactoryInJavaTest {
|
||||
@SuppressWarnings("unused")
|
||||
private static void factoryIsEasyToPassInUsingJava() {
|
||||
//noinspection Convert2MethodRef
|
||||
new MockNetwork(new MockNetworkParameters().setDefaultFactory(args -> new CustomNode(args)));
|
||||
new MockNetwork(new MockNetworkParameters().setDefaultFactory(CustomNode::new));
|
||||
new MockNetwork(emptyList(), new MockNetworkParameters().setDefaultFactory(args -> new CustomNode(args)));
|
||||
new MockNetwork(emptyList(), new MockNetworkParameters().setDefaultFactory(CustomNode::new));
|
||||
//noinspection Convert2MethodRef
|
||||
new MockNetwork().createNode(new MockNodeParameters(), args -> new CustomNode(args));
|
||||
new MockNetwork().createNode(new MockNodeParameters(), CustomNode::new);
|
||||
new MockNetwork(emptyList()).createNode(new MockNodeParameters(), args -> new CustomNode(args));
|
||||
new MockNetwork(emptyList()).createNode(new MockNodeParameters(), CustomNode::new);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.nodeapi.internal.NetworkParametersCopier
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.common.internal.asContextEnv
|
||||
import java.nio.file.Path
|
||||
|
@ -1,18 +1,23 @@
|
||||
package net.corda.testing.common.internal
|
||||
|
||||
import net.corda.core.utilities.days
|
||||
import net.corda.nodeapi.internal.NetworkParameters
|
||||
import net.corda.nodeapi.internal.NotaryInfo
|
||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||
import java.time.Instant
|
||||
|
||||
fun testNetworkParameters(notaries: List<NotaryInfo>): NetworkParameters {
|
||||
fun testNetworkParameters(
|
||||
notaries: List<NotaryInfo>,
|
||||
minimumPlatformVersion: Int = 1,
|
||||
modifiedTime: Instant = Instant.now(),
|
||||
maxMessageSize: Int = 10485760,
|
||||
maxTransactionSize: Int = 40000,
|
||||
epoch: Int = 1
|
||||
): NetworkParameters {
|
||||
return NetworkParameters(
|
||||
minimumPlatformVersion = 1,
|
||||
minimumPlatformVersion = minimumPlatformVersion,
|
||||
notaries = notaries,
|
||||
modifiedTime = Instant.now(),
|
||||
eventHorizon = 10000.days,
|
||||
maxMessageSize = 40000,
|
||||
maxTransactionSize = 40000,
|
||||
epoch = 1
|
||||
modifiedTime = modifiedTime,
|
||||
maxMessageSize = maxMessageSize,
|
||||
maxTransactionSize = maxTransactionSize,
|
||||
epoch = epoch
|
||||
)
|
||||
}
|
@ -3,11 +3,9 @@
|
||||
|
||||
package net.corda.testing
|
||||
|
||||
import net.corda.core.contracts.PartyAndReference
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.entropyToKeyPair
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
@ -15,25 +13,20 @@ import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.x500Name
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.services.config.configureDevKeyAndTrustStores
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.serialization.amqp.AMQP_ENABLED
|
||||
import org.bouncycastle.asn1.x509.GeneralName
|
||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||
import org.bouncycastle.asn1.x509.NameConstraints
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.internal.stubbing.answers.ThrowsException
|
||||
import java.lang.reflect.Modifier
|
||||
import java.math.BigInteger
|
||||
import java.nio.file.Files
|
||||
import java.security.KeyPair
|
||||
import java.security.PublicKey
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
/**
|
||||
@ -81,7 +74,7 @@ fun freePort(): Int = freePortCounter.getAndAccumulate(0) { prev, _ -> 30000 + (
|
||||
*/
|
||||
fun getFreeLocalPorts(hostName: String, numberToAlloc: Int): List<NetworkHostAndPort> {
|
||||
val freePort = freePortCounter.getAndAccumulate(0) { prev, _ -> 30000 + (prev - 30000 + numberToAlloc) % 10000 }
|
||||
return (freePort..freePort + numberToAlloc - 1).map { NetworkHostAndPort(hostName, it) }
|
||||
return (0 until numberToAlloc).map { NetworkHostAndPort(hostName, freePort + it) }
|
||||
}
|
||||
|
||||
fun configureTestSSL(legalName: CordaX500Name): SSLConfiguration = object : SSLConfiguration {
|
||||
@ -93,18 +86,29 @@ fun configureTestSSL(legalName: CordaX500Name): SSLConfiguration = object : SSLC
|
||||
configureDevKeyAndTrustStores(legalName)
|
||||
}
|
||||
}
|
||||
|
||||
fun getTestPartyAndCertificate(party: Party): PartyAndCertificate {
|
||||
val trustRoot: X509CertificateHolder = DEV_TRUST_ROOT
|
||||
val intermediate: CertificateAndKeyPair = DEV_CA
|
||||
|
||||
val nodeCaName = party.name.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN)
|
||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, party.name.x500Name))), arrayOf())
|
||||
val issuerKeyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||
val issuerCertificate = X509Utilities.createCertificate(CertificateType.NODE_CA, intermediate.certificate, intermediate.keyPair, nodeCaName, issuerKeyPair.public,
|
||||
nameConstraints = nameConstraints)
|
||||
val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val nodeCaCert = X509Utilities.createCertificate(
|
||||
CertificateType.NODE_CA,
|
||||
intermediate.certificate,
|
||||
intermediate.keyPair,
|
||||
nodeCaName,
|
||||
nodeCaKeyPair.public,
|
||||
nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, party.name.x500Name))), arrayOf()))
|
||||
|
||||
val certHolder = X509Utilities.createCertificate(CertificateType.WELL_KNOWN_IDENTITY, issuerCertificate, issuerKeyPair, party.name, party.owningKey)
|
||||
val pathElements = listOf(certHolder, issuerCertificate, intermediate.certificate, trustRoot)
|
||||
val identityCert = X509Utilities.createCertificate(
|
||||
CertificateType.WELL_KNOWN_IDENTITY,
|
||||
nodeCaCert,
|
||||
nodeCaKeyPair,
|
||||
party.name,
|
||||
party.owningKey)
|
||||
|
||||
val pathElements = listOf(identityCert, nodeCaCert, intermediate.certificate, trustRoot)
|
||||
val certPath = X509CertificateFactory().generateCertPath(pathElements.map(X509CertificateHolder::cert))
|
||||
return PartyAndCertificate(certPath)
|
||||
}
|
||||
@ -116,26 +120,29 @@ fun getTestPartyAndCertificate(name: CordaX500Name, publicKey: PublicKey): Party
|
||||
return getTestPartyAndCertificate(Party(name, publicKey))
|
||||
}
|
||||
|
||||
class TestIdentity @JvmOverloads constructor(val name: CordaX500Name, entropy: Long? = null) {
|
||||
val key = if (entropy != null) entropyToKeyPair(BigInteger.valueOf(entropy)) else generateKeyPair()
|
||||
val pubkey get() = key.public!!
|
||||
val party = Party(name, pubkey)
|
||||
val identity by lazy { getTestPartyAndCertificate(party) } // Often not needed.
|
||||
fun ref(vararg bytes: Byte) = party.ref(*bytes)
|
||||
}
|
||||
class TestIdentity(val name: CordaX500Name, val keyPair: KeyPair) {
|
||||
companion object {
|
||||
/**
|
||||
* Creates an identity that won't equal any other. This is mostly useful as a throwaway for test helpers.
|
||||
* @param organisation the organisation part of the new identity's name.
|
||||
*/
|
||||
fun fresh(organisation: String): TestIdentity {
|
||||
val keyPair = generateKeyPair()
|
||||
val name = CordaX500Name(organisation, keyPair.public.toStringShort(), CordaX500Name.unspecifiedCountry)
|
||||
return TestIdentity(name, keyPair)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
inline fun <reified T : Any> T.kryoSpecific(reason: String, function: () -> Unit) = if (!AMQP_ENABLED) {
|
||||
function()
|
||||
} else {
|
||||
loggerFor<T>().info("Ignoring Kryo specific test, reason: $reason")
|
||||
}
|
||||
/** Creates an identity with a deterministic [keyPair] i.e. same [entropy] same keyPair .*/
|
||||
constructor(name: CordaX500Name, entropy: Long) : this(name, entropyToKeyPair(BigInteger.valueOf(entropy)))
|
||||
|
||||
@Suppress("unused")
|
||||
inline fun <reified T : Any> T.amqpSpecific(reason: String, function: () -> Unit) = if (AMQP_ENABLED) {
|
||||
function()
|
||||
} else {
|
||||
loggerFor<T>().info("Ignoring AMQP specific test, reason: $reason")
|
||||
/** Creates an identity with the given name and a fresh keyPair. */
|
||||
constructor(name: CordaX500Name) : this(name, generateKeyPair())
|
||||
|
||||
val publicKey: PublicKey get() = keyPair.public
|
||||
val party: Party = Party(name, publicKey)
|
||||
val identity: PartyAndCertificate by lazy { getTestPartyAndCertificate(party) } // Often not needed.
|
||||
fun ref(vararg bytes: Byte): PartyAndReference = party.ref(*bytes)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,25 +161,3 @@ fun NodeInfo.singleIdentityAndCert(): PartyAndCertificate = legalIdentitiesAndCe
|
||||
* Extract a single identity from the node info. Throws an error if the node has multiple identities.
|
||||
*/
|
||||
fun NodeInfo.singleIdentity(): Party = singleIdentityAndCert().party
|
||||
|
||||
/**
|
||||
* A method on a mock was called, but no behaviour was previously specified for that method.
|
||||
* You can use [com.nhaarman.mockito_kotlin.doReturn] or similar to specify behaviour, see Mockito documentation for details.
|
||||
*/
|
||||
class UndefinedMockBehaviorException(message: String) : RuntimeException(message)
|
||||
|
||||
inline fun <reified T : Any> rigorousMock() = rigorousMock(T::class.java)
|
||||
/**
|
||||
* Create a Mockito mock that has [UndefinedMockBehaviorException] as the default behaviour of all abstract methods,
|
||||
* and [org.mockito.invocation.InvocationOnMock.callRealMethod] as the default for all concrete methods.
|
||||
* @param T the type to mock. Note if you want concrete methods of a Kotlin interface to be invoked,
|
||||
* it won't work unless you mock a (trivial) abstract implementation of that interface instead.
|
||||
*/
|
||||
fun <T> rigorousMock(clazz: Class<T>): T = mock(clazz) {
|
||||
if (Modifier.isAbstract(it.method.modifiers)) {
|
||||
// Use ThrowsException to hack the stack trace, and lazily so we can customise the message:
|
||||
ThrowsException(UndefinedMockBehaviorException("Please specify what should happen when '${it.method}' is called, or don't call it. Args: ${Arrays.toString(it.arguments)}")).answer(it)
|
||||
} else {
|
||||
it.callRealMethod()
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
package net.corda.testing
|
||||
|
||||
import java.time.Duration
|
||||
|
||||
/**
|
||||
* Ideas borrowed from "io.kotlintest" with some improvements made
|
||||
* This is meant for use from Kotlin code use only mainly due to it's inline/reified nature
|
||||
*/
|
||||
inline fun <reified E : Throwable, R> eventually(duration: Duration, f: () -> R): R {
|
||||
val end = System.nanoTime() + duration.toNanos()
|
||||
var times = 0
|
||||
while (System.nanoTime() < end) {
|
||||
try {
|
||||
return f()
|
||||
} catch (e: Throwable) {
|
||||
when (e) {
|
||||
is E -> {
|
||||
}// ignore and continue
|
||||
else -> throw e // unexpected exception type - rethrow
|
||||
}
|
||||
}
|
||||
times++
|
||||
}
|
||||
throw AssertionError("Test failed after $duration; attempted $times times")
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
package net.corda.testing
|
||||
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import kotlin.reflect.KCallable
|
||||
import kotlin.reflect.jvm.reflect
|
||||
|
||||
/**
|
||||
* These functions may be used to run measurements of a function where the parameters are chosen from corresponding
|
||||
* [Iterable]s in a lexical manner. An example use case would be benchmarking the speed of a certain function call using
|
||||
* different combinations of parameters.
|
||||
*/
|
||||
|
||||
fun <A : Any, R> measure(a: Iterable<A>, f: (A) -> R) =
|
||||
measure(listOf(a), f.reflect()!!) { f(uncheckedCast(it[0])) }
|
||||
|
||||
fun <A : Any, B : Any, R> measure(a: Iterable<A>, b: Iterable<B>, f: (A, B) -> R) =
|
||||
measure(listOf(a, b), f.reflect()!!) { f(uncheckedCast(it[0]), uncheckedCast(it[1])) }
|
||||
|
||||
fun <A : Any, B : Any, C : Any, R> measure(a: Iterable<A>, b: Iterable<B>, c: Iterable<C>, f: (A, B, C) -> R) =
|
||||
measure(listOf(a, b, c), f.reflect()!!) { f(uncheckedCast(it[0]), uncheckedCast(it[1]), uncheckedCast(it[2])) }
|
||||
|
||||
fun <A : Any, B : Any, C : Any, D : Any, R> measure(a: Iterable<A>, b: Iterable<B>, c: Iterable<C>, d: Iterable<D>, f: (A, B, C, D) -> R) =
|
||||
measure(listOf(a, b, c, d), f.reflect()!!) { f(uncheckedCast(it[0]), uncheckedCast(it[1]), uncheckedCast(it[2]), uncheckedCast(it[3])) }
|
||||
|
||||
private fun <R> measure(paramIterables: List<Iterable<Any?>>, kCallable: KCallable<R>, call: (Array<Any?>) -> R): Iterable<MeasureResult<R>> {
|
||||
val kParameters = kCallable.parameters
|
||||
return iterateLexical(paramIterables).map { params ->
|
||||
MeasureResult(
|
||||
// For example an underscore param in a lambda does not have a name:
|
||||
parameters = params.mapIndexed { index, param -> Pair(kParameters[index].name, param) },
|
||||
result = call(params.toTypedArray())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class MeasureResult<out R>(
|
||||
val parameters: List<Pair<String?, Any?>>,
|
||||
val result: R
|
||||
)
|
||||
|
||||
fun <A> iterateLexical(iterables: List<Iterable<A>>): Iterable<List<A>> {
|
||||
val result = ArrayList<List<A>>()
|
||||
fun iterateLexicalHelper(index: Int, list: List<A>) {
|
||||
if (index < iterables.size) {
|
||||
iterables[index].forEach {
|
||||
iterateLexicalHelper(index + 1, list + it)
|
||||
}
|
||||
} else {
|
||||
result.add(list)
|
||||
}
|
||||
}
|
||||
iterateLexicalHelper(0, emptyList())
|
||||
return result
|
||||
}
|
@ -9,6 +9,7 @@ import net.corda.nodeapi.internal.serialization.*
|
||||
import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme
|
||||
import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme
|
||||
import net.corda.testing.common.internal.asContextEnv
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.testThreadFactory
|
||||
import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnector
|
||||
import org.junit.rules.TestRule
|
||||
@ -33,19 +34,30 @@ class SerializationEnvironmentRule(private val inheritable: Boolean = false) : T
|
||||
}.whenever(it).execute(any())
|
||||
}
|
||||
}
|
||||
|
||||
/** Do not call, instead use [SerializationEnvironmentRule] as a [org.junit.Rule]. */
|
||||
fun <T> run(taskLabel: String, task: (SerializationEnvironment) -> T): T {
|
||||
return SerializationEnvironmentRule().apply { init(taskLabel) }.runTask(task)
|
||||
}
|
||||
}
|
||||
|
||||
lateinit var env: SerializationEnvironment
|
||||
override fun apply(base: Statement, description: Description): Statement {
|
||||
env = createTestSerializationEnv(description.toString())
|
||||
init(description.toString())
|
||||
return object : Statement() {
|
||||
override fun evaluate() {
|
||||
try {
|
||||
env.asContextEnv(inheritable) { base.evaluate() }
|
||||
} finally {
|
||||
inVMExecutors.remove(env)
|
||||
}
|
||||
}
|
||||
override fun evaluate() = runTask { base.evaluate() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun init(envLabel: String) {
|
||||
env = createTestSerializationEnv(envLabel)
|
||||
}
|
||||
|
||||
private fun <T> runTask(task: (SerializationEnvironment) -> T): T {
|
||||
try {
|
||||
return env.asContextEnv(inheritable, task)
|
||||
} finally {
|
||||
inVMExecutors.remove(env)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,25 +67,6 @@ interface GlobalSerializationEnvironment : SerializationEnvironment {
|
||||
fun unset()
|
||||
}
|
||||
|
||||
/** @param inheritable whether new threads inherit the environment, use sparingly. */
|
||||
fun <T> withTestSerialization(inheritable: Boolean = false, callable: (SerializationEnvironment) -> T): T {
|
||||
return createTestSerializationEnv("<context>").asContextEnv(inheritable, callable)
|
||||
}
|
||||
|
||||
/**
|
||||
* For example your test class uses [SerializationEnvironmentRule] but you want to turn it off for one method.
|
||||
* Use sparingly, ideally a test class shouldn't mix serializers init mechanisms.
|
||||
*/
|
||||
fun <T> withoutTestSerialization(callable: () -> T): T {
|
||||
val (property, env) = listOf(_contextSerializationEnv, _inheritableContextSerializationEnv).map { Pair(it, it.get()) }.single { it.second != null }
|
||||
property.set(null)
|
||||
try {
|
||||
return callable()
|
||||
} finally {
|
||||
property.set(env)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should only be used by Driver and MockNode.
|
||||
* @param armed true to install, false to do nothing and return a dummy env.
|
||||
@ -95,22 +88,21 @@ fun setGlobalSerialization(armed: Boolean): GlobalSerializationEnvironment {
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTestSerializationEnv(label: String) = object : SerializationEnvironmentImpl(
|
||||
SerializationFactoryImpl().apply {
|
||||
registerScheme(KryoClientSerializationScheme())
|
||||
registerScheme(KryoServerSerializationScheme())
|
||||
registerScheme(AMQPClientSerializationScheme(emptyList()))
|
||||
registerScheme(AMQPServerSerializationScheme(emptyList()))
|
||||
},
|
||||
AMQP_P2P_CONTEXT,
|
||||
KRYO_RPC_SERVER_CONTEXT,
|
||||
KRYO_RPC_CLIENT_CONTEXT,
|
||||
AMQP_STORAGE_CONTEXT,
|
||||
KRYO_CHECKPOINT_CONTEXT) {
|
||||
override fun toString() = "testSerializationEnv($label)"
|
||||
private fun createTestSerializationEnv(label: String): SerializationEnvironmentImpl {
|
||||
val factory = SerializationFactoryImpl().apply {
|
||||
registerScheme(KryoClientSerializationScheme())
|
||||
registerScheme(KryoServerSerializationScheme())
|
||||
registerScheme(AMQPClientSerializationScheme(emptyList()))
|
||||
registerScheme(AMQPServerSerializationScheme(emptyList()))
|
||||
}
|
||||
return object : SerializationEnvironmentImpl(
|
||||
factory,
|
||||
AMQP_P2P_CONTEXT,
|
||||
KRYO_RPC_SERVER_CONTEXT,
|
||||
KRYO_RPC_CLIENT_CONTEXT,
|
||||
AMQP_STORAGE_CONTEXT,
|
||||
KRYO_CHECKPOINT_CONTEXT
|
||||
) {
|
||||
override fun toString() = "testSerializationEnv($label)"
|
||||
}
|
||||
}
|
||||
|
||||
private const val AMQP_ENABLE_PROP_NAME = "net.corda.testing.amqp.enable"
|
||||
|
||||
// TODO: Remove usages of this function when we fully switched to AMQP
|
||||
private fun isAmqpEnabled(): Boolean = java.lang.Boolean.getBoolean(AMQP_ENABLE_PROP_NAME)
|
||||
|
@ -13,14 +13,23 @@ import java.security.PublicKey
|
||||
import java.time.Instant
|
||||
|
||||
// A dummy time at which we will be pretending test transactions are created.
|
||||
val TEST_TX_TIME: Instant get() = Instant.parse("2015-04-17T12:00:00.00Z")
|
||||
@JvmField
|
||||
val TEST_TX_TIME = Instant.parse("2015-04-17T12:00:00.00Z")
|
||||
@JvmField
|
||||
val DUMMY_NOTARY_NAME = CordaX500Name("Notary Service", "Zurich", "CH")
|
||||
@JvmField
|
||||
val DUMMY_BANK_A_NAME = CordaX500Name("Bank A", "London", "GB")
|
||||
@JvmField
|
||||
val DUMMY_BANK_B_NAME = CordaX500Name("Bank B", "New York", "US")
|
||||
@JvmField
|
||||
val DUMMY_BANK_C_NAME = CordaX500Name("Bank C", "Tokyo", "JP")
|
||||
@JvmField
|
||||
val BOC_NAME = CordaX500Name("BankOfCorda", "London", "GB")
|
||||
@JvmField
|
||||
val ALICE_NAME = CordaX500Name("Alice Corp", "Madrid", "ES")
|
||||
@JvmField
|
||||
val BOB_NAME = CordaX500Name("Bob Plc", "Rome", "IT")
|
||||
@JvmField
|
||||
val CHARLIE_NAME = CordaX500Name("Charlie Ltd", "Athens", "GR")
|
||||
val DEV_CA: CertificateAndKeyPair by lazy {
|
||||
// TODO: Should be identity scheme
|
||||
@ -42,3 +51,6 @@ val DEV_TRUST_ROOT: X509CertificateHolder by lazy {
|
||||
fun dummyCommand(vararg signers: PublicKey = arrayOf(generateKeyPair().public)) = Command<TypeOnlyCommandData>(DummyCommandData, signers.toList())
|
||||
|
||||
object DummyCommandData : TypeOnlyCommandData()
|
||||
|
||||
/** Maximum artemis message size. 10 MiB maximum allowed file size for attachments, including message headers. */
|
||||
const val MAX_MESSAGE_SIZE: Int = 10485760
|
||||
|
@ -1,10 +0,0 @@
|
||||
package net.corda.testing
|
||||
|
||||
import org.junit.Rule
|
||||
|
||||
@Deprecated("Instead of extending this class, use SerializationEnvironmentRule in the same way.")
|
||||
abstract class TestDependencyInjectionBase {
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package net.corda.testing
|
||||
|
||||
import net.corda.testing.TestTimestamp.Companion.timestamp
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* [timestamp] holds a formatted (UTC) timestamp that's set the first time it is queried. This is used to
|
||||
* provide a uniform timestamp for tests.
|
||||
*/
|
||||
class TestTimestamp {
|
||||
companion object {
|
||||
val timestamp: String = {
|
||||
val tz = TimeZone.getTimeZone("UTC")
|
||||
val df = SimpleDateFormat("yyyyMMddHHmmss")
|
||||
df.timeZone = tz
|
||||
df.format(Date())
|
||||
}()
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.corda.testing
|
||||
package net.corda.testing.dsl
|
||||
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.StateAndRef
|
@ -1,4 +1,4 @@
|
||||
package net.corda.testing
|
||||
package net.corda.testing.dsl
|
||||
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.cordapp.CordappProvider
|
||||
@ -12,9 +12,9 @@ import net.corda.core.node.ServicesForResolution
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.services.MockAttachmentStorage
|
||||
import net.corda.testing.services.MockCordappProvider
|
||||
import net.corda.testing.dummyCommand
|
||||
import java.io.InputStream
|
||||
import java.security.PublicKey
|
||||
import java.util.*
|
@ -1,4 +1,4 @@
|
||||
package net.corda.testing
|
||||
package net.corda.testing.dsl
|
||||
|
||||
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
|
||||
import net.corda.core.contracts.Attachment
|
@ -0,0 +1,19 @@
|
||||
package net.corda.testing.internal
|
||||
|
||||
import net.corda.core.serialization.internal._contextSerializationEnv
|
||||
import net.corda.core.serialization.internal._inheritableContextSerializationEnv
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
|
||||
/**
|
||||
* For example your test class uses [SerializationEnvironmentRule] but you want to turn it off for one method.
|
||||
* Use sparingly, ideally a test class shouldn't mix serializers init mechanisms.
|
||||
*/
|
||||
fun <T> withoutTestSerialization(callable: () -> T): T { // TODO: Delete this, see CORDA-858.
|
||||
val (property, env) = listOf(_contextSerializationEnv, _inheritableContextSerializationEnv).map { Pair(it, it.get()) }.single { it.second != null }
|
||||
property.set(null)
|
||||
try {
|
||||
return callable()
|
||||
} finally {
|
||||
property.set(env)
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package net.corda.testing.internal
|
||||
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.nodeapi.internal.serialization.amqp.AMQP_ENABLED
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.internal.stubbing.answers.ThrowsException
|
||||
import java.lang.reflect.Modifier
|
||||
import java.util.*
|
||||
|
||||
@Suppress("unused")
|
||||
inline fun <reified T : Any> T.kryoSpecific(reason: String, function: () -> Unit) = if (!AMQP_ENABLED) {
|
||||
function()
|
||||
} else {
|
||||
loggerFor<T>().info("Ignoring Kryo specific test, reason: $reason")
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
inline fun <reified T : Any> T.amqpSpecific(reason: String, function: () -> Unit) = if (AMQP_ENABLED) {
|
||||
function()
|
||||
} else {
|
||||
loggerFor<T>().info("Ignoring AMQP specific test, reason: $reason")
|
||||
}
|
||||
|
||||
/**
|
||||
* A method on a mock was called, but no behaviour was previously specified for that method.
|
||||
* You can use [com.nhaarman.mockito_kotlin.doReturn] or similar to specify behaviour, see Mockito documentation for details.
|
||||
*/
|
||||
class UndefinedMockBehaviorException(message: String) : RuntimeException(message)
|
||||
|
||||
inline fun <reified T : Any> rigorousMock() = rigorousMock(T::class.java)
|
||||
/**
|
||||
* Create a Mockito mock that has [UndefinedMockBehaviorException] as the default behaviour of all abstract methods,
|
||||
* and [org.mockito.invocation.InvocationOnMock.callRealMethod] as the default for all concrete methods.
|
||||
* @param T the type to mock. Note if you want concrete methods of a Kotlin interface to be invoked,
|
||||
* it won't work unless you mock a (trivial) abstract implementation of that interface instead.
|
||||
*/
|
||||
fun <T> rigorousMock(clazz: Class<T>): T = Mockito.mock(clazz) {
|
||||
if (Modifier.isAbstract(it.method.modifiers)) {
|
||||
// Use ThrowsException to hack the stack trace, and lazily so we can customise the message:
|
||||
ThrowsException(UndefinedMockBehaviorException("Please specify what should happen when '${it.method}' is called, or don't call it. Args: ${Arrays.toString(it.arguments)}")).answer(it)
|
||||
} else {
|
||||
it.callRealMethod()
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.corda.testing
|
||||
package net.corda.testing.internal
|
||||
|
||||
import net.corda.core.internal.packageName
|
||||
import org.apache.logging.log4j.Level
|
@ -0,0 +1,55 @@
|
||||
package net.corda.testing.internal
|
||||
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||
import net.corda.testing.getTestPartyAndCertificate
|
||||
import java.security.PrivateKey
|
||||
|
||||
class TestNodeInfoBuilder {
|
||||
private val identitiesAndPrivateKeys = ArrayList<Pair<PartyAndCertificate, PrivateKey>>()
|
||||
|
||||
fun addIdentity(name: CordaX500Name): Pair<PartyAndCertificate, PrivateKey> {
|
||||
val identityKeyPair = Crypto.generateKeyPair()
|
||||
val identity = getTestPartyAndCertificate(name, identityKeyPair.public)
|
||||
return Pair(identity, identityKeyPair.private).also {
|
||||
identitiesAndPrivateKeys += it
|
||||
}
|
||||
}
|
||||
|
||||
fun build(serial: Long = 1): NodeInfo {
|
||||
return NodeInfo(
|
||||
listOf(NetworkHostAndPort("my.${identitiesAndPrivateKeys[0].first.party.name.organisation}.com", 1234)),
|
||||
identitiesAndPrivateKeys.map { it.first },
|
||||
1,
|
||||
serial
|
||||
)
|
||||
}
|
||||
|
||||
fun buildWithSigned(serial: Long = 1): Pair<NodeInfo, SignedNodeInfo> {
|
||||
val nodeInfo = build(serial)
|
||||
val privateKeys = identitiesAndPrivateKeys.map { it.second }
|
||||
return Pair(nodeInfo, nodeInfo.signWith(privateKeys))
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
identitiesAndPrivateKeys.clear()
|
||||
}
|
||||
}
|
||||
|
||||
fun createNodeInfoAndSigned(vararg names: CordaX500Name, serial: Long = 1): Pair<NodeInfo, SignedNodeInfo> {
|
||||
val nodeInfoBuilder = TestNodeInfoBuilder()
|
||||
names.forEach { nodeInfoBuilder.addIdentity(it) }
|
||||
return nodeInfoBuilder.buildWithSigned(serial)
|
||||
}
|
||||
|
||||
fun NodeInfo.signWith(keys: List<PrivateKey>): SignedNodeInfo {
|
||||
val serialized = serialize()
|
||||
val signatures = keys.map { it.sign(serialized.bytes) }
|
||||
return SignedNodeInfo(serialized, signatures)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.corda.testing.contracts
|
||||
package net.corda.testing.internal.vault
|
||||
|
||||
import net.corda.core.contracts.Contract
|
||||
import net.corda.core.contracts.UniqueIdentifier
|
||||
@ -10,9 +10,8 @@ import net.corda.core.schemas.QueryableState
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.finance.contracts.DealState
|
||||
import net.corda.testing.schemas.DummyDealStateSchemaV1
|
||||
|
||||
val DUMMY_DEAL_PROGRAM_ID = "net.corda.testing.contracts.DummyDealContract"
|
||||
val DUMMY_DEAL_PROGRAM_ID = "net.corda.testing.internal.vault.DummyDealContract"
|
||||
|
||||
class DummyDealContract : Contract {
|
||||
override fun verify(tx: LedgerTransaction) {}
|
@ -1,4 +1,4 @@
|
||||
package net.corda.testing.schemas
|
||||
package net.corda.testing.internal.vault
|
||||
|
||||
import net.corda.core.contracts.UniqueIdentifier
|
||||
import net.corda.core.identity.AbstractParty
|
@ -1,4 +1,4 @@
|
||||
package net.corda.testing.contracts
|
||||
package net.corda.testing.internal.vault
|
||||
|
||||
import net.corda.core.contracts.Contract
|
||||
import net.corda.core.contracts.LinearState
|
||||
@ -10,12 +10,10 @@ import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.schemas.PersistentState
|
||||
import net.corda.core.schemas.QueryableState
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.testing.schemas.DummyLinearStateSchemaV1
|
||||
import net.corda.testing.schemas.DummyLinearStateSchemaV2
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset.UTC
|
||||
|
||||
const val DUMMY_LINEAR_CONTRACT_PROGRAM_ID = "net.corda.testing.contracts.DummyLinearContract"
|
||||
const val DUMMY_LINEAR_CONTRACT_PROGRAM_ID = "net.corda.testing.internal.vault.DummyLinearContract"
|
||||
|
||||
class DummyLinearContract : Contract {
|
||||
override fun verify(tx: LedgerTransaction) {
|
@ -1,4 +1,4 @@
|
||||
package net.corda.testing.schemas
|
||||
package net.corda.testing.internal.vault
|
||||
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.identity.AbstractParty
|
@ -1,4 +1,4 @@
|
||||
package net.corda.testing.schemas
|
||||
package net.corda.testing.internal.vault
|
||||
|
||||
import net.corda.core.contracts.UniqueIdentifier
|
||||
import net.corda.core.identity.AbstractParty
|
@ -1,6 +1,4 @@
|
||||
@file:JvmName("VaultFiller")
|
||||
|
||||
package net.corda.testing.contracts
|
||||
package net.corda.testing.internal.vault
|
||||
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.Crypto
|
||||
@ -80,7 +78,7 @@ class VaultFiller @JvmOverloads constructor(
|
||||
addCommand(dummyCommand())
|
||||
}
|
||||
val stx = issuerServices.signInitialTransaction(dummyIssue)
|
||||
return@map services.addSignature(stx, defaultNotary.pubkey)
|
||||
return@map services.addSignature(stx, defaultNotary.publicKey)
|
||||
}
|
||||
services.recordTransactions(transactions)
|
||||
// Get all the StateAndRefs of all the generated transactions.
|
||||
@ -101,7 +99,7 @@ class VaultFiller @JvmOverloads constructor(
|
||||
linearTimestamp: Instant = now()): Vault<LinearState> {
|
||||
val myKey: PublicKey = services.myInfo.chooseIdentity().owningKey
|
||||
val me = AnonymousParty(myKey)
|
||||
val issuerKey = defaultNotary.key
|
||||
val issuerKey = defaultNotary.keyPair
|
||||
val signatureMetadata = SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(issuerKey.public).schemeNumberID)
|
||||
val transactions: List<SignedTransaction> = (1..numberToCreate).map {
|
||||
// Issue a Linear state
|
@ -1,47 +0,0 @@
|
||||
package net.corda.testing.messaging
|
||||
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.ArtemisTcpTransport
|
||||
import net.corda.nodeapi.ConnectionDirection
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.testing.configureTestSSL
|
||||
import org.apache.activemq.artemis.api.core.client.*
|
||||
|
||||
/**
|
||||
* As the name suggests this is a simple client for connecting to MQ brokers.
|
||||
*/
|
||||
class SimpleMQClient(val target: NetworkHostAndPort,
|
||||
private val config: SSLConfiguration? = configureTestSSL(DEFAULT_MQ_LEGAL_NAME)) {
|
||||
companion object {
|
||||
val DEFAULT_MQ_LEGAL_NAME = CordaX500Name(organisation = "SimpleMQClient", locality = "London", country = "GB")
|
||||
}
|
||||
|
||||
lateinit var sessionFactory: ClientSessionFactory
|
||||
lateinit var session: ClientSession
|
||||
lateinit var producer: ClientProducer
|
||||
|
||||
fun start(username: String? = null, password: String? = null, enableSSL: Boolean = true) {
|
||||
val tcpTransport = ArtemisTcpTransport.tcpTransport(ConnectionDirection.Outbound(), target, config, enableSSL = enableSSL)
|
||||
val locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply {
|
||||
isBlockOnNonDurableSend = true
|
||||
threadPoolMaxSize = 1
|
||||
isUseGlobalPools = nodeSerializationEnv != null
|
||||
}
|
||||
sessionFactory = locator.createSessionFactory()
|
||||
session = sessionFactory.createSession(username, password, false, true, true, locator.isPreAcknowledge, locator.ackBatchSize)
|
||||
session.start()
|
||||
producer = session.createProducer()
|
||||
}
|
||||
|
||||
fun createMessage(): ClientMessage = session.createMessage(false)
|
||||
|
||||
fun stop() {
|
||||
try {
|
||||
sessionFactory.close()
|
||||
} catch (e: Exception) {
|
||||
// sessionFactory might not have initialised.
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package net.corda.testing
|
||||
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotEquals
|
||||
|
||||
class TestIdentityTests {
|
||||
@Test
|
||||
fun `entropy works`() {
|
||||
val a = TestIdentity(ALICE_NAME, 123)
|
||||
val b = TestIdentity(BOB_NAME, 123)
|
||||
assertEquals(a.publicKey, b.publicKey)
|
||||
assertEquals(a.keyPair.private, b.keyPair.private)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fresh works`() {
|
||||
val x = TestIdentity.fresh("xx")
|
||||
val y = TestIdentity.fresh("yy")
|
||||
// The param is called organisation so we'd better use it as such:
|
||||
assertEquals("xx", x.name.organisation)
|
||||
assertEquals("yy", y.name.organisation)
|
||||
// A fresh identity shouldn't be equal to anything by accident:
|
||||
assertNotEquals(x.name, y.name)
|
||||
assertNotEquals(x.publicKey, y.publicKey)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user