mirror of
https://github.com/corda/corda.git
synced 2025-01-13 16:30:25 +00:00
Fixing stuff
This commit is contained in:
parent
f3f06976d0
commit
107fcf82e3
27
build.gradle
27
build.gradle
@ -162,33 +162,6 @@ allprojects {
|
||||
tasks.withType(Test) {
|
||||
// Prevent the project from creating temporary files outside of the build directory.
|
||||
systemProperties['java.io.tmpdir'] = buildDir
|
||||
|
||||
// Ensures that "net.corda.testing.amqp.enable" is passed correctly from Gradle command line
|
||||
// down to JVM executing unit test. It looks like we are running unit tests in the forked mode
|
||||
// and all the "-D" parameters passed to Gradle not making it to unit test level
|
||||
// TODO: Remove once we fully switched to AMQP
|
||||
final AMQP_ENABLE_PROP_NAME = "net.corda.testing.amqp.enable"
|
||||
systemProperty(AMQP_ENABLE_PROP_NAME, System.getProperty(AMQP_ENABLE_PROP_NAME))
|
||||
|
||||
// relational database provider to be used by node
|
||||
final DATABASE_PROVIDER = "databaseProvider"
|
||||
final DATASOURCE_URL = "dataSourceProperties.dataSource.url"
|
||||
final DATASOURCE_CLASSNAME = "dataSourceProperties.dataSourceClassName"
|
||||
final DATASOURCE_USER = "dataSourceProperties.dataSource.user"
|
||||
final DATASOURCE_PASSWORD = "dataSourceProperties.dataSource.password"
|
||||
|
||||
// integration testing database configuration (to be used in conjunction with a DATABASE_PROVIDER)
|
||||
final TEST_DB_ADMIN_USER = "test.db.admin.user"
|
||||
final TEST_DB_ADMIN_PASSWORD = "test.db.admin.password"
|
||||
final TEST_DB_SCRIPT_DIR = "test.db.script.dir"
|
||||
|
||||
[DATABASE_PROVIDER,DATASOURCE_URL, DATASOURCE_CLASSNAME, DATASOURCE_USER, DATASOURCE_PASSWORD,
|
||||
TEST_DB_ADMIN_USER, TEST_DB_ADMIN_PASSWORD, TEST_DB_SCRIPT_DIR].forEach {
|
||||
def property = System.getProperty(it)
|
||||
if (property != null) {
|
||||
systemProperty(it, property)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group 'com.r3.corda.enterprise'
|
||||
|
@ -63,8 +63,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
public void setUp() throws ExecutionException, InterruptedException {
|
||||
node = startNode(ALICE_NAME, 1, singletonList(rpcUser));
|
||||
client = new CordaRPCClient(requireNonNull(node.getInternals().getConfiguration().getRpcAddress()));
|
||||
}
|
||||
|
@ -19,13 +19,11 @@ import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
|
||||
@Ignore
|
||||
class IdentitySyncFlowTests {
|
||||
private lateinit var mockNet: MockNetwork
|
||||
|
||||
|
@ -6,11 +6,9 @@ import net.corda.testing.*
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import org.junit.Before
|
||||
import net.corda.testing.node.startFlow
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import kotlin.test.*
|
||||
|
||||
@Ignore
|
||||
class SwapIdentitiesFlowTests {
|
||||
private lateinit var mockNet: MockNetwork
|
||||
|
||||
|
@ -16,13 +16,21 @@ import java.security.PublicKey
|
||||
abstract class NotaryService : SingletonSerializeAsToken() {
|
||||
companion object {
|
||||
const val ID_PREFIX = "corda.notary."
|
||||
fun constructId(validating: Boolean, raft: Boolean = false, bft: Boolean = false, custom: Boolean = false): String {
|
||||
require(Booleans.countTrue(raft, bft, custom) <= 1) { "At most one of raft, bft or custom may be true" }
|
||||
@JvmOverloads
|
||||
fun constructId(
|
||||
validating: Boolean,
|
||||
raft: Boolean = false,
|
||||
bft: Boolean = false,
|
||||
custom: Boolean = false,
|
||||
mysql: Boolean = false
|
||||
): String {
|
||||
require(Booleans.countTrue(raft, bft, custom, mysql) <= 1) { "At most one of raft, bft, mysql or custom may be true" }
|
||||
return StringBuffer(ID_PREFIX).apply {
|
||||
append(if (validating) "validating" else "simple")
|
||||
if (raft) append(".raft")
|
||||
if (bft) append(".bft")
|
||||
if (custom) append(".custom")
|
||||
if (mysql) append(".mysql")
|
||||
}.toString()
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ from the previous milestone release.
|
||||
|
||||
UNRELEASED
|
||||
----------
|
||||
|
||||
* The network map service concept has been re-designed. More information can be found in :doc:`network-map`.
|
||||
|
||||
* The previous design was never intended to be final but was rather a quick implementation in the earliest days of the
|
||||
|
@ -15,7 +15,9 @@ import net.corda.core.utilities.days
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.*
|
||||
import net.corda.finance.contracts.asset.CASH
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.contracts.asset.STATE
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.dsl.EnforceVerifyOrFail
|
||||
|
@ -1,9 +1,15 @@
|
||||
package net.corda.finance.contracts.asset
|
||||
|
||||
import com.nhaarman.mockito_kotlin.*
|
||||
import com.nhaarman.mockito_kotlin.argThat
|
||||
import com.nhaarman.mockito_kotlin.doNothing
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.*
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.services.VaultService
|
||||
import net.corda.core.node.services.queryBy
|
||||
@ -20,10 +26,10 @@ import net.corda.node.services.vault.NodeVaultService
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.contracts.DummyState
|
||||
import net.corda.testing.internal.LogHelper
|
||||
import net.corda.testing.dsl.EnforceVerifyOrFail
|
||||
import net.corda.testing.dsl.TransactionDSL
|
||||
import net.corda.testing.dsl.TransactionDSLInterpreter
|
||||
import net.corda.testing.internal.LogHelper
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.vault.VaultFiller
|
||||
import net.corda.testing.node.MockServices
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.r3.corda.networkmanage.doorman
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
@ -8,22 +9,29 @@ import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.minutes
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.network.NetworkMapClient
|
||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
@ -48,16 +56,11 @@ class DoormanIntegrationTest {
|
||||
|
||||
//Start doorman server
|
||||
val doorman = startDoorman(intermediateCertAndKey, rootCertAndKey.certificate)
|
||||
|
||||
val doormanHostAndPort = doorman.hostAndPort
|
||||
// Start Corda network registration.
|
||||
val config = testNodeConfiguration(
|
||||
baseDirectory = tempFolder.root.toPath(),
|
||||
myLegalName = ALICE_NAME).also {
|
||||
val doormanHostAndPort = doorman.hostAndPort
|
||||
whenever(it.compatibilityZoneURL).thenReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}"))
|
||||
whenever(it.emailAddress).thenReturn("iTest@R3.com")
|
||||
val config = createConfig().also {
|
||||
doReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}")).whenever(it).compatibilityZoneURL
|
||||
}
|
||||
|
||||
config.trustStoreFile.parent.createDirectories()
|
||||
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate.cert)
|
||||
@ -94,6 +97,7 @@ class DoormanIntegrationTest {
|
||||
doorman.close()
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
fun `nodeInfo is published to the network map`() {
|
||||
// Given
|
||||
@ -105,13 +109,9 @@ class DoormanIntegrationTest {
|
||||
val doormanHostAndPort = doorman.hostAndPort
|
||||
|
||||
// Start Corda network registration.
|
||||
val config = testNodeConfiguration(
|
||||
baseDirectory = tempFolder.root.toPath(),
|
||||
myLegalName = ALICE_NAME).also {
|
||||
whenever(it.compatibilityZoneURL).thenReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}"))
|
||||
whenever(it.emailAddress).thenReturn("iTest@R3.com")
|
||||
val config = createConfig().also {
|
||||
doReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}")).whenever(it).compatibilityZoneURL
|
||||
}
|
||||
|
||||
config.trustStoreFile.parent.createDirectories()
|
||||
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCertAndKey.certificate.cert)
|
||||
@ -128,7 +128,8 @@ class DoormanIntegrationTest {
|
||||
val nodeInfoBytes = nodeInfo.serialize()
|
||||
|
||||
// When
|
||||
networkMapClient.publish(SignedData(nodeInfoBytes, keyPair.sign(nodeInfoBytes)))
|
||||
val signedNodeInfo = SignedNodeInfo(nodeInfoBytes, listOf(keyPair.sign(nodeInfoBytes)))
|
||||
networkMapClient.publish(signedNodeInfo)
|
||||
|
||||
// Then
|
||||
val networkMapNodeInfo = networkMapClient.getNodeInfo(nodeInfoBytes.hash)
|
||||
@ -137,8 +138,24 @@ class DoormanIntegrationTest {
|
||||
|
||||
doorman.close()
|
||||
}
|
||||
|
||||
fun createConfig(): NodeConfiguration {
|
||||
return rigorousMock<NodeConfiguration>().also {
|
||||
doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
|
||||
doReturn(ALICE_NAME).whenever(it).myLegalName
|
||||
doReturn(it.baseDirectory / "certificates").whenever(it).certificatesDirectory
|
||||
doReturn(it.certificatesDirectory / "truststore.jks").whenever(it).trustStoreFile
|
||||
doReturn(it.certificatesDirectory / "nodekeystore.jks").whenever(it).nodeKeystore
|
||||
doReturn(it.certificatesDirectory / "sslkeystore.jks").whenever(it).sslKeystore
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
// doReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}")).whenever(it).compatibilityZoneURL
|
||||
doReturn("iTest@R3.com").whenever(it).emailAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun createDoormanIntermediateCertificateAndKeyPair(rootCertificateAndKeyPair: CertificateAndKeyPair): CertificateAndKeyPair {
|
||||
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCertificateAndKeyPair.certificate, rootCertificateAndKeyPair.keyPair,
|
||||
|
@ -1,9 +1,6 @@
|
||||
package com.r3.corda.networkmanage.hsm
|
||||
|
||||
import com.nhaarman.mockito_kotlin.any
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.verify
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import com.nhaarman.mockito_kotlin.*
|
||||
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||
@ -17,9 +14,11 @@ import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
@ -28,7 +27,7 @@ import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.BOB_NAME
|
||||
import net.corda.testing.CHARLIE_NAME
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.node.testNodeConfiguration
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||
import org.h2.tools.Server
|
||||
@ -101,14 +100,12 @@ class SigningServiceIntegrationTest {
|
||||
|
||||
NetworkManagementServer().use { server ->
|
||||
server.start(NetworkHostAndPort(HOST, 0), database, networkMapServiceParameter = null, doormanServiceParameter = DoormanConfig(approveAll = true, approveInterval = 2.seconds.toMillis(), jiraConfig = null), updateNetworkParameters = null)
|
||||
val doormanHostAndPort = server.hostAndPort
|
||||
// Start Corda network registration.
|
||||
val config = testNodeConfiguration(
|
||||
baseDirectory = tempFolder.root.toPath(),
|
||||
myLegalName = ALICE_NAME).also {
|
||||
val doormanHostAndPort = server.hostAndPort
|
||||
whenever(it.compatibilityZoneURL).thenReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}"))
|
||||
val config = createConfig().also {
|
||||
doReturn(ALICE_NAME).whenever(it).myLegalName
|
||||
doReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}")).whenever(it).compatibilityZoneURL
|
||||
}
|
||||
|
||||
val signingServiceStorage = DBSignedCertificateRequestStorage(configureDatabase(makeTestDataSourceProperties()))
|
||||
|
||||
val hsmSigner = givenSignerSigningAllRequests(signingServiceStorage)
|
||||
@ -149,6 +146,7 @@ class SigningServiceIntegrationTest {
|
||||
* The split is done due to the limited console support while executing tests and inability to capture user's input there.
|
||||
*
|
||||
*/
|
||||
@Ignore
|
||||
@Test
|
||||
fun `DEMO - Create CSR and poll`() {
|
||||
//Start doorman server
|
||||
@ -162,23 +160,41 @@ class SigningServiceIntegrationTest {
|
||||
}
|
||||
|
||||
// Start Corda network registration.
|
||||
(1..3).map {
|
||||
(1..3).map { num ->
|
||||
thread(start = true) {
|
||||
val config = testNodeConfiguration(
|
||||
baseDirectory = tempFolder.root.toPath(),
|
||||
myLegalName = when (it) {
|
||||
1 -> ALICE_NAME
|
||||
2 -> BOB_NAME
|
||||
3 -> CHARLIE_NAME
|
||||
else -> throw IllegalArgumentException("Unrecognised option")
|
||||
}).also {
|
||||
whenever(it.compatibilityZoneURL).thenReturn(URL("http://$HOST:${server.hostAndPort.port}"))
|
||||
// Start Corda network registration.
|
||||
val config = createConfig().also {
|
||||
doReturn(when (num) {
|
||||
1 -> ALICE_NAME
|
||||
2 -> BOB_NAME
|
||||
3 -> CHARLIE_NAME
|
||||
else -> throw IllegalArgumentException("Unrecognised option")
|
||||
}).whenever(it).myLegalName
|
||||
doReturn(URL("http://$HOST:${server.hostAndPort.port}")).whenever(it).compatibilityZoneURL
|
||||
}
|
||||
config.trustStoreFile.parent.createDirectories()
|
||||
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert.cert)
|
||||
it.save(config.trustStoreFile, config.trustStorePassword)
|
||||
}
|
||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
||||
}
|
||||
}.map { it.join() }
|
||||
}
|
||||
}
|
||||
|
||||
fun createConfig(): NodeConfiguration {
|
||||
return rigorousMock<NodeConfiguration>().also {
|
||||
doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
|
||||
doReturn(it.baseDirectory / "certificates").whenever(it).certificatesDirectory
|
||||
doReturn(it.certificatesDirectory / "truststore.jks").whenever(it).trustStoreFile
|
||||
doReturn(it.certificatesDirectory / "nodekeystore.jks").whenever(it).nodeKeystore
|
||||
doReturn(it.certificatesDirectory / "sslkeystore.jks").whenever(it).sslKeystore
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn("iTest@R3.com").whenever(it).emailAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeTestDataSourceProperties(): Properties {
|
||||
|
@ -15,7 +15,7 @@ class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private
|
||||
val nodeInfoHashes = networkMapStorage.getNodeInfoHashes(CertificateStatus.VALID)
|
||||
val networkParameters = networkMapStorage.getLatestNetworkParameters()
|
||||
val networkMap = NetworkMap(nodeInfoHashes, networkParameters.serialize().hash)
|
||||
if (networkMap != currentSignedNetworkMap?.verified()) {
|
||||
if (networkMap != currentSignedNetworkMap?.verified(null)) {
|
||||
val digitalSignature = signer.sign(networkMap.serialize().bytes)
|
||||
val signedHashedNetworkMap = SignedNetworkMap(networkMap.serialize(), digitalSignature)
|
||||
networkMapStorage.saveNetworkMap(signedHashedNetworkMap)
|
||||
|
@ -5,6 +5,7 @@ import com.typesafe.config.ConfigParseOptions
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.utilities.days
|
||||
import net.corda.core.utilities.parsePublicKeyBase58
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||
|
@ -17,7 +17,6 @@ class NetworkParametersConfigurationTest {
|
||||
fun `reads an existing file`() {
|
||||
val networkParameters = parseNetworkParametersFrom(validOverrideNetworkConfigPath)
|
||||
assertThat(networkParameters.minimumPlatformVersion).isEqualTo(1)
|
||||
assertThat(networkParameters.eventHorizon).isEqualTo(100.days)
|
||||
val notaries = networkParameters.notaries
|
||||
assertThat(notaries).hasSize(2)
|
||||
assertThat(notaries[0].validating).isTrue()
|
||||
|
@ -7,8 +7,8 @@ import com.r3.corda.networkmanage.common.persistence.CertificateStatus
|
||||
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.utilities.seconds
|
||||
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 net.corda.testing.SerializationEnvironmentRule
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||
import org.junit.Rule
|
||||
@ -62,7 +62,6 @@ abstract class TestBase {
|
||||
return NetworkParameters(
|
||||
minimumPlatformVersion = minimumPlatformVersion,
|
||||
notaries = notaries,
|
||||
eventHorizon = eventHorizon,
|
||||
maxMessageSize = maxMessageSize,
|
||||
maxTransactionSize = maxTransactionSize,
|
||||
modifiedTime = modifiedTime,
|
||||
|
@ -81,7 +81,7 @@ class DBNetworkMapStorageTest : TestBase() {
|
||||
val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
||||
|
||||
assertEquals(signedNetworkMap.signature, persistedSignedNetworkMap?.signature)
|
||||
assertEquals(signedNetworkMap.verified(), persistedSignedNetworkMap?.verified())
|
||||
assertEquals(signedNetworkMap.verified(rootCACert.cert), persistedSignedNetworkMap?.verified(rootCACert.cert))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -11,10 +11,10 @@ import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.cert
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.nodeapi.internal.NetworkMap
|
||||
import net.corda.nodeapi.internal.SignedNetworkMap
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.network.NetworkMap
|
||||
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
@ -58,7 +58,7 @@ class NetworkMapSignerTest : TestBase() {
|
||||
verify(networkMapStorage).getLatestNetworkParameters()
|
||||
argumentCaptor<SignedNetworkMap>().apply {
|
||||
verify(networkMapStorage).saveNetworkMap(capture())
|
||||
val networkMap = firstValue.verified()
|
||||
val networkMap = firstValue.verified(rootCACert.cert)
|
||||
assertEquals(networkMapParameters.serialize().hash, networkMap.networkParameterHash)
|
||||
assertEquals(signedNodeInfoHashes.size, networkMap.nodeInfoHashes.size)
|
||||
assertTrue(networkMap.nodeInfoHashes.containsAll(signedNodeInfoHashes))
|
||||
@ -104,7 +104,7 @@ class NetworkMapSignerTest : TestBase() {
|
||||
verify(networkMapStorage).getLatestNetworkParameters()
|
||||
argumentCaptor<SignedNetworkMap>().apply {
|
||||
verify(networkMapStorage).saveNetworkMap(capture())
|
||||
val networkMap = firstValue.verified()
|
||||
val networkMap = firstValue.verified(rootCACert.cert)
|
||||
assertEquals(networkMapParameters.serialize().hash, networkMap.networkParameterHash)
|
||||
}
|
||||
}
|
||||
|
@ -19,10 +19,10 @@ import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.nodeapi.internal.NetworkMap
|
||||
import net.corda.nodeapi.internal.SignedNetworkMap
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.network.NetworkMap
|
||||
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.junit.Rule
|
||||
@ -82,7 +82,7 @@ class NodeInfoWebServiceTest {
|
||||
val conn = URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}").openConnection() as HttpURLConnection
|
||||
val signedNetworkMap = conn.inputStream.readBytes().deserialize<SignedNetworkMap>()
|
||||
verify(networkMapStorage, times(1)).getCurrentNetworkMap()
|
||||
assertEquals(signedNetworkMap.verified(), networkMap)
|
||||
assertEquals(signedNetworkMap.verified(rootCACert.cert), networkMap)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,10 +68,12 @@ class SignedNetworkMap(val raw: SerializedBytes<NetworkMap>, val signature: Digi
|
||||
* @throws SignatureException if the signature is invalid.
|
||||
*/
|
||||
@Throws(SignatureException::class, CertPathValidatorException::class)
|
||||
fun verified(trustedRoot: X509Certificate): NetworkMap {
|
||||
fun verified(trustedRoot: X509Certificate?): NetworkMap {
|
||||
signature.by.publicKey.verify(raw.bytes, signature)
|
||||
// Assume network map cert is under the default trust root.
|
||||
X509Utilities.validateCertificateChain(trustedRoot, signature.by, trustedRoot)
|
||||
if (trustedRoot != null) {
|
||||
X509Utilities.validateCertificateChain(trustedRoot, signature.by, trustedRoot)
|
||||
}
|
||||
return raw.deserialize()
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.node.services.statemachine.DataSessionMessage
|
||||
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
|
||||
import net.corda.testing.internal.kryoSpecific
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.internal.kryoSpecific
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Rule
|
||||
|
@ -127,6 +127,8 @@ dependencies {
|
||||
compile "org.postgresql:postgresql:$postgresql_version"
|
||||
//For Azure SQL and SQL Server support in persistence
|
||||
compile 'com.microsoft.sqlserver:mssql-jdbc:6.2.1.jre8'
|
||||
// For the MySQLUniquenessProvider
|
||||
compile group: 'mysql', name: 'mysql-connector-java', version: '6.0.6'
|
||||
|
||||
// SQL connection pooling library
|
||||
compile "com.zaxxer:HikariCP:2.5.1"
|
||||
|
@ -24,9 +24,9 @@ import net.corda.testing.*
|
||||
import net.corda.testing.driver.DriverDSL
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.withoutTestSerialization
|
||||
import net.corda.testing.services.MockAttachmentStorage
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Rule
|
||||
|
@ -25,11 +25,9 @@ import net.corda.node.services.config.NotaryConfig
|
||||
import net.corda.node.services.transactions.minClusterSize
|
||||
import net.corda.node.services.transactions.minCorrectReplicas
|
||||
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.testing.IntegrationTest
|
||||
import net.corda.testing.IntegrationTestSchemas
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.dummyCommand
|
||||
@ -39,19 +37,12 @@ import net.corda.testing.node.MockNodeParameters
|
||||
import net.corda.testing.node.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
import java.nio.file.Paths
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class BFTNotaryServiceTests : IntegrationTest() {
|
||||
companion object {
|
||||
@ClassRule @JvmField
|
||||
val databaseSchemas = IntegrationTestSchemas("node_0", "node_1", "node_2", "node_3", "node_4", "node_5",
|
||||
"node_6", "node_7", "node_8", "node_9")
|
||||
}
|
||||
|
||||
class BFTNotaryServiceTests {
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private lateinit var notary: Party
|
||||
private lateinit var node: StartedNode<MockNode>
|
||||
|
@ -0,0 +1,154 @@
|
||||
package net.corda.node.services
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.flows.NotaryError
|
||||
import net.corda.core.flows.NotaryException
|
||||
import net.corda.core.flows.NotaryFlow
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.services.config.NotaryConfig
|
||||
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
import net.corda.testing.node.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
import java.math.BigInteger
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class MySQLNotaryServiceTests : IntegrationTest() {
|
||||
companion object {
|
||||
val notaryName = CordaX500Name("MySQL Notary Service", "Zurich", "CH")
|
||||
@ClassRule
|
||||
@JvmField
|
||||
val databaseSchemas = IntegrationTestSchemas("node_0", DUMMY_NOTARY_NAME.toDatabaseSchemaName())
|
||||
}
|
||||
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private lateinit var node: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var notaryParty: Party
|
||||
private lateinit var notaryNode: StartedNode<MockNetwork.MockNode>
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts"))
|
||||
notaryParty = ServiceIdentityGenerator.generateToDisk(
|
||||
listOf(mockNet.baseDirectory(mockNet.nextNodeId)),
|
||||
notaryName,
|
||||
"identity"
|
||||
)
|
||||
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notaryParty, false))))
|
||||
val notaryNodeUnstarted = createNotaryNode()
|
||||
val nodeUnstarted = mockNet.createUnstartedNode()
|
||||
|
||||
val startedNodes = listOf(notaryNodeUnstarted, nodeUnstarted).map { n ->
|
||||
networkParameters.install(mockNet.baseDirectory(n.id))
|
||||
n.start()
|
||||
}
|
||||
notaryNode = startedNodes.first()
|
||||
node = startedNodes.last()
|
||||
}
|
||||
|
||||
@After
|
||||
fun stopNodes() {
|
||||
mockNet.stopNodes()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `detect double spend`() {
|
||||
val inputState = issueState(node, notaryParty)
|
||||
|
||||
val firstTxBuilder = TransactionBuilder(notaryParty)
|
||||
.addInputState(inputState)
|
||||
.addCommand(dummyCommand(node.services.myInfo.chooseIdentity().owningKey))
|
||||
val firstSpendTx = node.services.signInitialTransaction(firstTxBuilder)
|
||||
|
||||
val firstSpend = node.services.startFlow(NotaryFlow.Client(firstSpendTx))
|
||||
mockNet.runNetwork()
|
||||
|
||||
firstSpend.resultFuture.getOrThrow()
|
||||
|
||||
val secondSpendBuilder = TransactionBuilder(notaryParty).withItems(inputState).run {
|
||||
val dummyState = DummyContract.SingleOwnerState(0, node.info.chooseIdentity())
|
||||
addOutputState(dummyState, DummyContract.PROGRAM_ID)
|
||||
addCommand(dummyCommand(node.services.myInfo.chooseIdentity().owningKey))
|
||||
this
|
||||
}
|
||||
val secondSpendTx = node.services.signInitialTransaction(secondSpendBuilder)
|
||||
val secondSpend = node.services.startFlow(NotaryFlow.Client(secondSpendTx))
|
||||
|
||||
mockNet.runNetwork()
|
||||
|
||||
val ex = assertFailsWith(NotaryException::class) { secondSpend.resultFuture.getOrThrow() }
|
||||
val error = ex.error as NotaryError.Conflict
|
||||
assertEquals(error.txId, secondSpendTx.id)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `notarisations are idempotent`() {
|
||||
val inputState = issueState(node, notaryParty)
|
||||
|
||||
val txBuilder = TransactionBuilder(notaryParty)
|
||||
.addInputState(inputState)
|
||||
.addCommand(dummyCommand(node.services.myInfo.chooseIdentity().owningKey))
|
||||
val spendTx = node.services.signInitialTransaction(txBuilder)
|
||||
|
||||
val notarise = node.services.startFlow(NotaryFlow.Client(spendTx))
|
||||
mockNet.runNetwork()
|
||||
val signature = notarise.resultFuture.get().single()
|
||||
|
||||
val notariseRetry = node.services.startFlow(NotaryFlow.Client(spendTx))
|
||||
mockNet.runNetwork()
|
||||
val signatureRetry = notariseRetry.resultFuture.get().single()
|
||||
|
||||
fun checkSignature(signature: TransactionSignature) {
|
||||
signature.verify(spendTx.id)
|
||||
assertEquals(notaryParty.owningKey, signature.by)
|
||||
}
|
||||
|
||||
checkSignature(signature)
|
||||
checkSignature(signatureRetry)
|
||||
}
|
||||
|
||||
private fun createNotaryNode(): MockNetwork.MockNode {
|
||||
val dataStoreProperties = makeTestDataSourceProperties().apply {
|
||||
setProperty("autoCommit", "false")
|
||||
}
|
||||
return mockNet.createUnstartedNode(
|
||||
MockNodeParameters(
|
||||
legalName = notaryName,
|
||||
entropyRoot = BigInteger.valueOf(60L),
|
||||
configOverrides = {
|
||||
val notaryConfig = NotaryConfig(validating = false, mysql = dataStoreProperties)
|
||||
doReturn(notaryConfig).whenever(it).notary
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun issueState(node: StartedNode<*>, notary: Party): StateAndRef<*> {
|
||||
return node.database.transaction {
|
||||
val builder = DummyContract.generateInitial(Random().nextInt(), notary, node.info.chooseIdentity().ref(0))
|
||||
val stx = node.services.signInitialTransaction(builder)
|
||||
node.services.recordTransactions(stx)
|
||||
StateAndRef(builder.outputStates().first(), StateRef(stx.id, 0))
|
||||
}
|
||||
}
|
||||
}
|
@ -11,24 +11,21 @@ import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
|
||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.BOB_NAME
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.internal.CompatibilityZoneParams
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.node.internal.CompatibilityZoneParams
|
||||
import net.corda.testing.node.internal.internalDriver
|
||||
import net.corda.testing.node.internal.network.NetworkMapServer
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.*
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.net.URL
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class NetworkMapTest : IntegrationTest() {
|
||||
companion object {
|
||||
@ClassRule
|
||||
@JvmField
|
||||
val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName(), BOB_NAME.toDatabaseSchemaName(),
|
||||
DUMMY_NOTARY_NAME.toDatabaseSchemaName())
|
||||
}
|
||||
class NetworkMapTest {
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule(true)
|
||||
|
@ -15,10 +15,9 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
||||
import net.corda.testing.IntegrationTest
|
||||
import net.corda.testing.IntegrationTestSchemas
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.node.internal.CompatibilityZoneParams
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.node.internal.CompatibilityZoneParams
|
||||
import net.corda.testing.node.internal.internalDriver
|
||||
import net.corda.testing.node.internal.network.NetworkMapServer
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
@ -27,7 +26,6 @@ import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.io.ByteArrayOutputStream
|
||||
@ -42,11 +40,7 @@ import javax.ws.rs.*
|
||||
import javax.ws.rs.core.MediaType
|
||||
import javax.ws.rs.core.Response
|
||||
|
||||
class NodeRegistrationTest : IntegrationTest() {
|
||||
companion object {
|
||||
@ClassRule @JvmField
|
||||
val databaseSchemas = IntegrationTestSchemas("Alice")
|
||||
}
|
||||
class NodeRegistrationTest {
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule(true)
|
||||
|
@ -697,14 +697,19 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
private fun makeCoreNotaryService(notaryConfig: NotaryConfig, database: CordaPersistence): NotaryService {
|
||||
val notaryKey = myNotaryIdentity?.owningKey ?: throw IllegalArgumentException("No notary identity initialized when creating a notary service")
|
||||
return notaryConfig.run {
|
||||
if (raft != null) {
|
||||
val uniquenessProvider = RaftUniquenessProvider(configuration, database, services.monitoringService.metrics, raft)
|
||||
(if (validating) ::RaftValidatingNotaryService else ::RaftNonValidatingNotaryService)(services, notaryKey, uniquenessProvider)
|
||||
} else if (bftSMaRt != null) {
|
||||
if (validating) throw IllegalArgumentException("Validating BFTSMaRt notary not supported")
|
||||
BFTNonValidatingNotaryService(services, notaryKey, bftSMaRt, makeBFTCluster(notaryKey, bftSMaRt))
|
||||
} else {
|
||||
(if (validating) ::ValidatingNotaryService else ::SimpleNotaryService)(services, notaryKey)
|
||||
when {
|
||||
raft != null -> {
|
||||
val uniquenessProvider = RaftUniquenessProvider(configuration, database, services.monitoringService.metrics, raft)
|
||||
(if (validating) ::RaftValidatingNotaryService else ::RaftNonValidatingNotaryService)(services, notaryKey, uniquenessProvider)
|
||||
}
|
||||
bftSMaRt != null -> {
|
||||
if (validating) throw IllegalArgumentException("Validating BFTSMaRt notary not supported")
|
||||
BFTNonValidatingNotaryService(services, notaryKey, bftSMaRt, makeBFTCluster(notaryKey, bftSMaRt))
|
||||
}
|
||||
mysql != null -> {
|
||||
(if (validating) ::MySQLValidatingNotaryService else ::MySQLNonValidatingNotaryService)(services, notaryKey, mysql, configuration.devMode)
|
||||
}
|
||||
else -> (if (validating) ::ValidatingNotaryService else ::SimpleNotaryService)(services, notaryKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -754,7 +759,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
Pair("identity", myLegalName)
|
||||
} else {
|
||||
val notaryId = notaryConfig.run {
|
||||
NotaryService.constructId(validating, raft != null, bftSMaRt != null, custom)
|
||||
NotaryService.constructId(validating, raft != null, bftSMaRt != null, custom, mysql != null)
|
||||
}
|
||||
// The node is part of a distributed notary whose identity must already be generated beforehand.
|
||||
Pair(notaryId, null)
|
||||
|
@ -56,11 +56,12 @@ fun NodeConfiguration.shouldCheckCheckpoints(): Boolean {
|
||||
data class NotaryConfig(val validating: Boolean,
|
||||
val raft: RaftConfig? = null,
|
||||
val bftSMaRt: BFTSMaRtConfiguration? = null,
|
||||
val custom: Boolean = false
|
||||
val custom: Boolean = false,
|
||||
val mysql: Properties? = null
|
||||
) {
|
||||
init {
|
||||
require(raft == null || bftSMaRt == null || !custom) {
|
||||
"raft, bftSMaRt, and custom configs cannot be specified together"
|
||||
require(raft == null || bftSMaRt == null || !custom || mysql == null) {
|
||||
"raft, bftSMaRt, custom, and mysql configs cannot be specified together"
|
||||
}
|
||||
}
|
||||
val isClusterConfig: Boolean get() = raft != null || bftSMaRt != null
|
||||
|
@ -0,0 +1,52 @@
|
||||
package net.corda.node.services.transactions
|
||||
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.node.services.TimeWindowChecker
|
||||
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
import java.security.PublicKey
|
||||
import java.util.*
|
||||
|
||||
/** Notary service backed by a replicated MySQL database. */
|
||||
abstract class MySQLNotaryService(
|
||||
final override val services: ServiceHubInternal,
|
||||
override val notaryIdentityKey: PublicKey,
|
||||
dataSourceProperties: Properties,
|
||||
/** Database table will be automatically created in dev mode */
|
||||
val devMode: Boolean) : TrustedAuthorityNotaryService() {
|
||||
|
||||
override val timeWindowChecker = TimeWindowChecker(services.clock)
|
||||
override val uniquenessProvider = MySQLUniquenessProvider(
|
||||
services.monitoringService.metrics,
|
||||
dataSourceProperties
|
||||
)
|
||||
|
||||
override fun start() {
|
||||
if (devMode) uniquenessProvider.createTable()
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
uniquenessProvider.stop()
|
||||
}
|
||||
}
|
||||
|
||||
class MySQLNonValidatingNotaryService(services: ServiceHubInternal,
|
||||
notaryIdentityKey: PublicKey,
|
||||
dataSourceProperties: Properties,
|
||||
devMode: Boolean = false) : MySQLNotaryService(services, notaryIdentityKey, dataSourceProperties, devMode) {
|
||||
companion object {
|
||||
val id = constructId(validating = false, mysql = true)
|
||||
}
|
||||
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = NonValidatingNotaryFlow(otherPartySession, this)
|
||||
}
|
||||
|
||||
class MySQLValidatingNotaryService(services: ServiceHubInternal,
|
||||
notaryIdentityKey: PublicKey,
|
||||
dataSourceProperties: Properties,
|
||||
devMode: Boolean = false) : MySQLNotaryService(services, notaryIdentityKey, dataSourceProperties, devMode) {
|
||||
companion object {
|
||||
val id = constructId(validating = true, mysql = true)
|
||||
}
|
||||
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = ValidatingNotaryFlow(otherPartySession, this)
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
package net.corda.node.services.transactions
|
||||
|
||||
import com.codahale.metrics.MetricRegistry
|
||||
import com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException
|
||||
import com.zaxxer.hikari.HikariConfig
|
||||
import com.zaxxer.hikari.HikariDataSource
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.services.UniquenessException
|
||||
import net.corda.core.node.services.UniquenessProvider
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import java.security.PublicKey
|
||||
import java.sql.BatchUpdateException
|
||||
import java.sql.Connection
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Uniqueness provider backed by a MySQL database. It is intended to be used with a multi-master synchronously replicated
|
||||
* variant of MySQL, such as Percona XtraDB Cluster, or MariaDB Galera Cluster.
|
||||
*
|
||||
* Note that no ORM is used since we want to retain full control over table schema and be able to experiment with optimisations.
|
||||
*/
|
||||
class MySQLUniquenessProvider(
|
||||
metrics: MetricRegistry,
|
||||
dataSourceProperties: Properties
|
||||
) : UniquenessProvider, SingletonSerializeAsToken() {
|
||||
companion object {
|
||||
private val log = loggerFor<MySQLUniquenessProvider>()
|
||||
|
||||
// TODO: optimize table schema for InnoDB
|
||||
private val createTableStatement =
|
||||
"CREATE TABLE IF NOT EXISTS committed_states (" +
|
||||
"issue_tx_id BINARY(32) NOT NULL," +
|
||||
"issue_tx_output_id INT NOT NULL," +
|
||||
"consuming_tx_id BINARY(32) NOT NULL," +
|
||||
"consuming_tx_input_id INT UNSIGNED NOT NULL," +
|
||||
"consuming_party_name TEXT NOT NULL," +
|
||||
// TODO: do we need to store the key? X500 name should be sufficient
|
||||
"consuming_party_key BLOB NOT NULL," +
|
||||
"commit_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP," +
|
||||
"CONSTRAINT id PRIMARY KEY (issue_tx_id, issue_tx_output_id)" +
|
||||
")"
|
||||
private val insertStatement = "INSERT INTO committed_states (issue_tx_id, issue_tx_output_id, consuming_tx_id, consuming_tx_input_id, consuming_party_name, consuming_party_key) VALUES (?, ?, ?, ?, ?, ?)"
|
||||
private val findStatement = "SELECT consuming_tx_id, consuming_tx_input_id, consuming_party_name, consuming_party_key FROM committed_states WHERE issue_tx_id = ? AND issue_tx_output_id = ?"
|
||||
}
|
||||
|
||||
private val metricPrefix = MySQLUniquenessProvider::class.simpleName
|
||||
/** Transaction commit duration and rate metric timer */
|
||||
private val commitTimer = metrics.timer("$metricPrefix.Commit")
|
||||
/**
|
||||
* When writing to multiple masters with Galera, transaction rollbacks may happen due to high write contention.
|
||||
* This is a useful heath metric.
|
||||
*/
|
||||
private val rollbackCounter = metrics.counter("$metricPrefix.Rollback")
|
||||
/** Track double spend attempts. Note that this will also include notarisation retries. */
|
||||
private val conflictCounter = metrics.counter("$metricPrefix.Conflicts")
|
||||
|
||||
val dataSource = HikariDataSource(HikariConfig(dataSourceProperties))
|
||||
|
||||
private val connection: Connection
|
||||
get() = dataSource.connection
|
||||
|
||||
fun createTable() {
|
||||
log.debug("Attempting to create DB table if it does not yet exist: $createTableStatement")
|
||||
connection.use {
|
||||
it.createStatement().execute(createTableStatement)
|
||||
it.commit()
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
dataSource.close()
|
||||
}
|
||||
|
||||
override fun commit(states: List<StateRef>, txId: SecureHash, callerIdentity: Party) {
|
||||
val timer = commitTimer.time()
|
||||
try {
|
||||
retryTransaction(CommitAll(states, txId, callerIdentity))
|
||||
} catch (e: BatchUpdateException) {
|
||||
log.info("Unable to commit input states, finding conflicts", e)
|
||||
conflictCounter.inc()
|
||||
retryTransaction(FindConflicts(states))
|
||||
} finally {
|
||||
timer.stop()
|
||||
}
|
||||
}
|
||||
|
||||
private fun retryTransaction(tx: RetryableTransaction) {
|
||||
connection.use {
|
||||
while (true) {
|
||||
try {
|
||||
tx.run(it)
|
||||
} catch (e: Exception) {
|
||||
it.rollback()
|
||||
if (e is MySQLTransactionRollbackException) {
|
||||
log.warn("Rollback exception occurred, retrying", e)
|
||||
rollbackCounter.inc()
|
||||
continue
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
it.commit()
|
||||
}
|
||||
}
|
||||
|
||||
interface RetryableTransaction {
|
||||
fun run(conn: Connection)
|
||||
}
|
||||
|
||||
private class CommitAll(val states: List<StateRef>, val txId: SecureHash, val callerIdentity: Party) : RetryableTransaction {
|
||||
override fun run(conn: Connection) {
|
||||
conn.prepareStatement(insertStatement).apply {
|
||||
states.forEachIndexed { index, stateRef ->
|
||||
// StateRef
|
||||
setBytes(1, stateRef.txhash.bytes)
|
||||
setInt(2, stateRef.index)
|
||||
// Consuming transaction
|
||||
setBytes(3, txId.bytes)
|
||||
setInt(4, index)
|
||||
setString(5, callerIdentity.name.toString())
|
||||
setBytes(6, callerIdentity.owningKey.serialize().bytes)
|
||||
|
||||
addBatch()
|
||||
clearParameters()
|
||||
}
|
||||
executeBatch()
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FindConflicts(val states: List<StateRef>) : RetryableTransaction {
|
||||
override fun run(conn: Connection) {
|
||||
val conflicts = mutableMapOf<StateRef, UniquenessProvider.ConsumingTx>()
|
||||
states.forEach {
|
||||
val st = conn.prepareStatement(findStatement).apply {
|
||||
setBytes(1, it.txhash.bytes)
|
||||
setInt(2, it.index)
|
||||
}
|
||||
val result = st.executeQuery()
|
||||
|
||||
if (result.next()) {
|
||||
val consumingTxId = SecureHash.SHA256(result.getBytes(1))
|
||||
val inputIndex = result.getInt(2)
|
||||
val partyName = CordaX500Name.parse(result.getString(3))
|
||||
val partyKey: PublicKey = result.getBytes(4).deserialize()
|
||||
conflicts[it] = UniquenessProvider.ConsumingTx(consumingTxId, inputIndex, Party(partyName, partyKey))
|
||||
}
|
||||
}
|
||||
conn.commit()
|
||||
if (conflicts.isNotEmpty()) throw UniquenessException(UniquenessProvider.Conflict(conflicts))
|
||||
}
|
||||
}
|
||||
}
|
@ -33,5 +33,4 @@ enterpriseConfiguration = {
|
||||
waitInterval = 40000
|
||||
}
|
||||
}
|
||||
|
||||
useAMQPBridges = true
|
||||
useAMQPBridges = true
|
||||
|
@ -46,10 +46,8 @@ import java.util.stream.StreamSupport;
|
||||
import static net.corda.core.node.services.vault.QueryCriteriaUtils.DEFAULT_PAGE_NUM;
|
||||
import static net.corda.core.node.services.vault.QueryCriteriaUtils.MAX_PAGE_SIZE;
|
||||
import static net.corda.core.utilities.ByteArrays.toHexString;
|
||||
import static net.corda.testing.TestConstants.*;
|
||||
import static net.corda.testing.internal.InternalTestUtilsKt.rigorousMock;
|
||||
import static net.corda.testing.TestConstants.BOC_NAME;
|
||||
import static net.corda.testing.TestConstants.CHARLIE_NAME;
|
||||
import static net.corda.testing.TestConstants.DUMMY_NOTARY_NAME;
|
||||
import static net.corda.testing.node.MockServices.makeTestDatabaseAndMockServices;
|
||||
import static net.corda.testing.node.MockServicesKt.makeTestIdentityService;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@ -74,7 +72,8 @@ public class VaultQueryJavaTests {
|
||||
"net.corda.testing.internal.vault",
|
||||
"net.corda.finance.contracts.asset",
|
||||
CashSchemaV1.class.getPackage().getName(),
|
||||
DummyLinearStateSchemaV1.class.getPackage().getName());
|
||||
DummyLinearStateSchemaV1.class.getPackage().getName()
|
||||
);
|
||||
IdentityServiceInternal identitySvc = makeTestIdentityService(MEGA_CORP.getIdentity(), DUMMY_CASH_ISSUER_INFO.getIdentity(), DUMMY_NOTARY.getIdentity());
|
||||
Pair<CordaPersistence, MockServices> databaseAndServices = makeTestDatabaseAndMockServices(
|
||||
cordappPackages,
|
||||
|
@ -13,11 +13,12 @@ import net.corda.node.services.statemachine.FlowStart
|
||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.internal.LogHelper
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
import net.corda.testing.TestIdentity
|
||||
import net.corda.testing.internal.LogHelper
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
|
@ -24,26 +24,26 @@ import net.corda.finance.POUNDS
|
||||
import net.corda.finance.SWISS_FRANCS
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.contracts.asset.DummyFungibleContract
|
||||
import net.corda.finance.schemas.CashSchemaV1
|
||||
import net.corda.finance.sampleschemas.SampleCashSchemaV2
|
||||
import net.corda.finance.sampleschemas.SampleCashSchemaV3
|
||||
import net.corda.finance.schemas.CashSchemaV1
|
||||
import net.corda.finance.utils.sumCash
|
||||
import net.corda.node.internal.configureDatabase
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.node.services.schema.HibernateObserver
|
||||
import net.corda.node.services.schema.NodeSchemaService
|
||||
import net.corda.node.services.vault.VaultSchemaV1
|
||||
import net.corda.node.internal.configureDatabase
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.vault.DummyDealStateSchemaV1
|
||||
import net.corda.testing.internal.vault.DummyLinearStateSchemaV1
|
||||
import net.corda.testing.internal.vault.DummyLinearStateSchemaV2
|
||||
import net.corda.testing.internal.vault.VaultFiller
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
import net.corda.testing.internal.vault.DummyLinearStateSchemaV1
|
||||
import net.corda.testing.internal.vault.DummyLinearStateSchemaV2
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.hibernate.SessionFactory
|
||||
|
@ -1,6 +1,9 @@
|
||||
package net.corda.node.services.vault
|
||||
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.InsufficientBalanceException
|
||||
import net.corda.core.contracts.LinearState
|
||||
import net.corda.core.contracts.UniqueIdentifier
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
@ -42,7 +45,7 @@ import kotlin.test.fail
|
||||
class VaultWithCashTest {
|
||||
private companion object {
|
||||
private val cordappPackages = listOf(
|
||||
"net.corda.testing.internal.vault", "net.corda.finance.contracts.asset", CashSchemaV1::class.packageName, DummyLinearStateSchemaV1::class.packageName)
|
||||
"net.corda.testing.contracts", "net.corda.finance.contracts.asset", CashSchemaV1::class.packageName, DummyLinearStateSchemaV1::class.packageName)
|
||||
val BOB = TestIdentity(BOB_NAME, 80).party
|
||||
val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10)
|
||||
val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1)
|
||||
|
@ -4,18 +4,17 @@ import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import com.r3.corda.enterprise.perftestcordapp.DOLLARS
|
||||
import com.r3.corda.enterprise.perftestcordapp.`issued by`
|
||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.*
|
||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.CASH
|
||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash
|
||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.DUMMY_CASH_ISSUER_KEY
|
||||
import com.r3.corda.enterprise.perftestcordapp.contracts.asset.STATE
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.services.Vault
|
||||
import net.corda.core.node.services.VaultService
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.days
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.dsl.EnforceVerifyOrFail
|
||||
@ -24,16 +23,12 @@ import net.corda.testing.dsl.TransactionDSLInterpreter
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.ledger
|
||||
import net.corda.testing.node.makeTestIdentityService
|
||||
import net.corda.testing.node.transaction
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
// TODO: The generate functions aren't tested by these tests: add them.
|
||||
|
||||
@ -47,8 +42,7 @@ interface CommercialPaperTestTemplate {
|
||||
|
||||
private val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB"))
|
||||
private val MEGA_CORP get() = megaCorp.party
|
||||
private val MEGA_CORP_IDENTITY get() = megaCorp.identity
|
||||
private val MEGA_CORP_PUBKEY get() = megaCorp.publicKey
|
||||
private val MEGA_CORP_PUBKEY get() = megaCorp.keyPair.public
|
||||
|
||||
|
||||
class KotlinCommercialPaperTest : CommercialPaperTestTemplate {
|
||||
@ -85,21 +79,14 @@ class CommercialPaperTestsGeneric {
|
||||
@Parameterized.Parameters @JvmStatic
|
||||
fun data() = listOf(KotlinCommercialPaperTest(), KotlinCommercialPaperLegacyTest())
|
||||
|
||||
private val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10)
|
||||
private val DUMMY_CASH_ISSUER_IDENTITY get() = dummyCashIssuer.identity
|
||||
private val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1)
|
||||
private val alice = TestIdentity(ALICE_NAME, 70)
|
||||
private val BIG_CORP_KEY = generateKeyPair()
|
||||
private val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20)
|
||||
private val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB"))
|
||||
private val ALICE get() = alice.party
|
||||
private val ALICE_KEY get() = alice.keyPair
|
||||
private val ALICE_PUBKEY get() = alice.publicKey
|
||||
private val ALICE_PUBKEY get() = alice.keyPair.public
|
||||
private val DUMMY_NOTARY get() = dummyNotary.party
|
||||
private val DUMMY_NOTARY_IDENTITY get() = dummyNotary.identity
|
||||
private val MINI_CORP get() = miniCorp.party
|
||||
private val MINI_CORP_IDENTITY get() = miniCorp.identity
|
||||
private val MINI_CORP_PUBKEY get() = miniCorp.publicKey
|
||||
private val MINI_CORP_PUBKEY get() = miniCorp.keyPair.public
|
||||
|
||||
}
|
||||
|
||||
@ -159,8 +146,10 @@ class CommercialPaperTestsGeneric {
|
||||
output(Cash.PROGRAM_ID, "Alice's profit", aliceGetsBack.STATE ownedBy ALICE)
|
||||
output(Cash.PROGRAM_ID, "Change", (someProfits - aliceGetsBack).STATE ownedBy MEGA_CORP)
|
||||
}
|
||||
|
||||
command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
|
||||
command(ALICE_PUBKEY, thisTest.getRedeemCommand(DUMMY_NOTARY))
|
||||
|
||||
tweak {
|
||||
outputs(700.DOLLARS `issued by` issuer)
|
||||
timeWindow(TEST_TX_TIME + 8.days)
|
||||
@ -252,96 +241,7 @@ class CommercialPaperTestsGeneric {
|
||||
private lateinit var alicesVault: Vault<ContractState>
|
||||
|
||||
private val notaryServices = MockServices(emptyList(), rigorousMock(), MEGA_CORP.name, dummyNotary.keyPair)
|
||||
private val issuerServices = MockServices(listOf("net.corda.finance.contracts", "net.corda.finance.schemas"), rigorousMock(), MEGA_CORP.name, dummyCashIssuer.keyPair)
|
||||
private val issuerServices = MockServices(emptyList(), rigorousMock(), MEGA_CORP.name, DUMMY_CASH_ISSUER_KEY)
|
||||
|
||||
private lateinit var moveTX: SignedTransaction
|
||||
|
||||
@Test
|
||||
fun `issue move and then redeem`() {
|
||||
val aliceDatabaseAndServices = MockServices.makeTestDatabaseAndMockServices(
|
||||
listOf("net.corda.finance.contracts"),
|
||||
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY),
|
||||
TestIdentity(MEGA_CORP.name, ALICE_KEY))
|
||||
val databaseAlice = aliceDatabaseAndServices.first
|
||||
aliceServices = aliceDatabaseAndServices.second
|
||||
aliceVaultService = aliceServices.vaultService
|
||||
|
||||
databaseAlice.transaction {
|
||||
alicesVault = aliceServices.fillWithSomeTestCash(9000.DOLLARS, issuerServices, issuedBy = DUMMY_CASH_ISSUER, outputNotary = DUMMY_NOTARY)
|
||||
aliceVaultService = aliceServices.vaultService
|
||||
}
|
||||
|
||||
val bigCorpDatabaseAndServices = MockServices.makeTestDatabaseAndMockServices(
|
||||
listOf("net.corda.finance.contracts"),
|
||||
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY),
|
||||
TestIdentity(MEGA_CORP.name, BIG_CORP_KEY))
|
||||
val databaseBigCorp = bigCorpDatabaseAndServices.first
|
||||
bigCorpServices = bigCorpDatabaseAndServices.second
|
||||
bigCorpVaultService = bigCorpServices.vaultService
|
||||
|
||||
databaseBigCorp.transaction {
|
||||
bigCorpVault = bigCorpServices.fillWithSomeTestCash(13000.DOLLARS, issuerServices, issuedBy = DUMMY_CASH_ISSUER, outputNotary = DUMMY_NOTARY)
|
||||
bigCorpVaultService = bigCorpServices.vaultService
|
||||
}
|
||||
|
||||
// Propagate the cash transactions to each side.
|
||||
aliceServices.recordTransactions(bigCorpVault.states.map { bigCorpServices.validatedTransactions.getTransaction(it.ref.txhash)!! })
|
||||
bigCorpServices.recordTransactions(alicesVault.states.map { aliceServices.validatedTransactions.getTransaction(it.ref.txhash)!! })
|
||||
|
||||
// BigCorp™ issues $10,000 of commercial paper, to mature in 30 days, owned initially by itself.
|
||||
val faceValue = 10000.DOLLARS `issued by` DUMMY_CASH_ISSUER
|
||||
val issuance = bigCorpServices.myInfo.chooseIdentity().ref(1)
|
||||
val issueBuilder = CommercialPaper().generateIssue(issuance, faceValue, TEST_TX_TIME + 30.days, DUMMY_NOTARY)
|
||||
issueBuilder.setTimeWindow(TEST_TX_TIME, 30.seconds)
|
||||
val issuePtx = bigCorpServices.signInitialTransaction(issueBuilder)
|
||||
val issueTx = notaryServices.addSignature(issuePtx)
|
||||
|
||||
databaseAlice.transaction {
|
||||
// Alice pays $9000 to BigCorp to own some of their debt.
|
||||
moveTX = run {
|
||||
val builder = TransactionBuilder(DUMMY_NOTARY)
|
||||
Cash.generateSpend(aliceServices, builder, 9000.DOLLARS, AnonymousParty(BIG_CORP_KEY.public))
|
||||
CommercialPaper().generateMove(builder, issueTx.tx.outRef(0), AnonymousParty(ALICE_PUBKEY))
|
||||
val ptx = aliceServices.signInitialTransaction(builder)
|
||||
val ptx2 = bigCorpServices.addSignature(ptx)
|
||||
val stx = notaryServices.addSignature(ptx2)
|
||||
stx
|
||||
}
|
||||
}
|
||||
|
||||
databaseBigCorp.transaction {
|
||||
// Verify the txns are valid and insert into both sides.
|
||||
listOf(issueTx, moveTX).forEach {
|
||||
it.toLedgerTransaction(aliceServices).verify()
|
||||
aliceServices.recordTransactions(it)
|
||||
bigCorpServices.recordTransactions(it)
|
||||
}
|
||||
}
|
||||
|
||||
databaseBigCorp.transaction {
|
||||
fun makeRedeemTX(time: Instant): Pair<SignedTransaction, UUID> {
|
||||
val builder = TransactionBuilder(DUMMY_NOTARY)
|
||||
builder.setTimeWindow(time, 30.seconds)
|
||||
CommercialPaper().generateRedeem(builder, moveTX.tx.outRef(1), bigCorpServices, bigCorpServices.myInfo.chooseIdentityAndCert())
|
||||
val ptx = aliceServices.signInitialTransaction(builder)
|
||||
val ptx2 = bigCorpServices.addSignature(ptx)
|
||||
val stx = notaryServices.addSignature(ptx2)
|
||||
return Pair(stx, builder.lockId)
|
||||
}
|
||||
|
||||
val redeemTX = makeRedeemTX(TEST_TX_TIME + 10.days)
|
||||
val tooEarlyRedemption = redeemTX.first
|
||||
val tooEarlyRedemptionLockId = redeemTX.second
|
||||
val e = assertFailsWith(TransactionVerificationException::class) {
|
||||
tooEarlyRedemption.toLedgerTransaction(aliceServices).verify()
|
||||
}
|
||||
// manually release locks held by this failing transaction
|
||||
aliceServices.vaultService.softLockRelease(tooEarlyRedemptionLockId)
|
||||
assertTrue(e.cause!!.message!!.contains("paper must have matured"))
|
||||
|
||||
val validRedemption = makeRedeemTX(TEST_TX_TIME + 31.days).first
|
||||
validRedemption.toLedgerTransaction(aliceServices).verify()
|
||||
// soft lock not released after success either!!! (as transaction not recorded)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,13 +137,19 @@ class CashTests {
|
||||
@Before
|
||||
fun setUp() {
|
||||
LogHelper.setLevel(NodeVaultService::class)
|
||||
megaCorpServices = MockServices(listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas"), rigorousMock(), MEGA_CORP.name, MEGA_CORP_KEY)
|
||||
miniCorpServices = MockServices(listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas"), rigorousMock<IdentityServiceInternal>().also {
|
||||
doNothing().whenever(it).justVerifyAndRegisterIdentity(argThat { name == MINI_CORP.name })
|
||||
}, MINI_CORP.name, MINI_CORP_KEY)
|
||||
val notaryServices = MockServices(listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas"), rigorousMock(), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
|
||||
val databaseAndServices = makeTestDatabaseAndMockServices(
|
||||
megaCorpServices = MockServices(
|
||||
listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas"),
|
||||
rigorousMock(), MEGA_CORP.name, MEGA_CORP_KEY)
|
||||
miniCorpServices = MockServices(
|
||||
listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas"),
|
||||
rigorousMock<IdentityServiceInternal>().also {
|
||||
doNothing().whenever(it).justVerifyAndRegisterIdentity(argThat { name == MINI_CORP.name })
|
||||
}, MINI_CORP.name, MINI_CORP_KEY)
|
||||
val notaryServices = MockServices(
|
||||
listOf("com.r3.corda.enterprise.perftestcordapp.contracts.asset", "com.r3.corda.enterprise.perftestcordapp.schemas"),
|
||||
rigorousMock(), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
|
||||
val databaseAndServices = makeTestDatabaseAndMockServices(
|
||||
listOf("net.corda.finance.contracts.asset", "net.corda.finance.schemas"),
|
||||
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY),
|
||||
TestIdentity(CordaX500Name("Me", "London", "GB")))
|
||||
database = databaseAndServices.first
|
||||
@ -879,7 +885,7 @@ class CashTests {
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input("MEGA_CORP cash")
|
||||
// We send it to another publicKey so that the transaction is not identical to the previous one
|
||||
// We send it to another pubkey so that the transaction is not identical to the previous one
|
||||
output(Cash.PROGRAM_ID, "MEGA_CORP cash 3", "MEGA_CORP cash".output<Cash.State>().copy(owner = ALICE))
|
||||
command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
|
||||
this.verifies()
|
||||
|
@ -51,6 +51,9 @@ include 'samples:notary-demo'
|
||||
include 'samples:bank-of-corda-demo'
|
||||
include 'samples:business-network-demo'
|
||||
include 'cordform-common'
|
||||
include 'network-management'
|
||||
include 'network-management:capsule'
|
||||
include 'network-management:capsule-hsm'
|
||||
include 'verify-enclave'
|
||||
include 'hsm-tool'
|
||||
project(':hsm-tool').with {
|
||||
|
@ -268,8 +268,7 @@ class InMemoryMessagingNetwork internal constructor(
|
||||
private val peerHandle: PeerHandle,
|
||||
private val executor: AffinityExecutor,
|
||||
private val database: CordaPersistence) : SingletonSerializeAsToken(), MessagingService {
|
||||
private inner class Handler(val topicSession: String,
|
||||
val callback: MessageHandler) : MessageHandlerRegistration
|
||||
inner class Handler(val topicSession: String, val callback: MessageHandler) : MessageHandlerRegistration
|
||||
|
||||
@Volatile
|
||||
private var running = true
|
||||
|
@ -398,7 +398,7 @@ class MockNetwork(private val cordappPackages: List<String>,
|
||||
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(makeTestDataSourceProperties("node_${id}_net_$networkId")).whenever(it).dataSourceProperties
|
||||
doReturn(makeTestDatabaseProperties("node_$id")).whenever(it).database
|
||||
parameters.configOverrides(it)
|
||||
}
|
||||
@ -503,6 +503,5 @@ private fun mockNodeConfiguration(): NodeConfiguration {
|
||||
doReturn(5.seconds.toMillis()).whenever(it).additionalNodeInfoPollingFrequencyMsec
|
||||
doReturn(null).whenever(it).devModeOptions
|
||||
doReturn(true).whenever(it).useAMQPBridges
|
||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000))).whenever(it).enterpriseConfiguration
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,9 @@ import net.corda.core.serialization.internal.effectiveSerializationEnv
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.services.api.StartedNodeServices
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.TestIdentity
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.dsl.*
|
||||
|
||||
/**
|
||||
|
@ -39,6 +39,9 @@ dependencies {
|
||||
runtime group: 'org.apache.jmeter', name: 'ApacheJMeter_config', version: "$jmVersion"
|
||||
runtime group: 'org.apache.jmeter', name: 'ApacheJMeter', version: "$jmVersion"
|
||||
runtime group: 'org.apache.jmeter', name: 'jorphan', version: "$jmVersion"
|
||||
//For Azure SQL and SQL Server support in persistence
|
||||
runtime group: 'com.microsoft.sqlserver', name: 'mssql-jdbc', version: '6.2.1.jre8'
|
||||
|
||||
|
||||
testCompile project(':test-utils')
|
||||
testCompile project(':node-driver')
|
||||
|
244
tools/jmeter/src/main/resources/Testplans/SQL.jmx
Normal file
244
tools/jmeter/src/main/resources/Testplans/SQL.jmx
Normal file
@ -0,0 +1,244 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<jmeterTestPlan version="1.2" properties="3.2" jmeter="3.3 r1808647">
|
||||
<hashTree>
|
||||
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
|
||||
<stringProp name="TestPlan.comments"></stringProp>
|
||||
<boolProp name="TestPlan.functional_mode">false</boolProp>
|
||||
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
|
||||
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="TestPlan.user_define_classpath"></stringProp>
|
||||
</TestPlan>
|
||||
<hashTree>
|
||||
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
|
||||
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
||||
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
||||
<boolProp name="LoopController.continue_forever">false</boolProp>
|
||||
<stringProp name="LoopController.loops">40000</stringProp>
|
||||
</elementProp>
|
||||
<stringProp name="ThreadGroup.num_threads">3</stringProp>
|
||||
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
|
||||
<longProp name="ThreadGroup.start_time">1509455820000</longProp>
|
||||
<longProp name="ThreadGroup.end_time">1509455820000</longProp>
|
||||
<boolProp name="ThreadGroup.scheduler">false</boolProp>
|
||||
<stringProp name="ThreadGroup.duration"></stringProp>
|
||||
<stringProp name="ThreadGroup.delay"></stringProp>
|
||||
</ThreadGroup>
|
||||
<hashTree>
|
||||
<JDBCDataSource guiclass="TestBeanGUI" testclass="JDBCDataSource" testname="JDBC Connection Configuration" enabled="true">
|
||||
<boolProp name="autocommit">true</boolProp>
|
||||
<stringProp name="checkQuery"></stringProp>
|
||||
<stringProp name="connectionAge">5000</stringProp>
|
||||
<stringProp name="dataSource">testpool</stringProp>
|
||||
<stringProp name="dbUrl">jdbc:sqlserver://perfperformancetest.database.windows.net:1433;databaseName=perftesting</stringProp>
|
||||
<stringProp name="driver">com.microsoft.sqlserver.jdbc.SQLServerDriver</stringProp>
|
||||
<boolProp name="keepAlive">true</boolProp>
|
||||
<stringProp name="password">yourStrong(!)Password</stringProp>
|
||||
<stringProp name="poolMax">0</stringProp>
|
||||
<stringProp name="timeout">10000</stringProp>
|
||||
<stringProp name="transactionIsolation">TRANSACTION_REPEATABLE_READ</stringProp>
|
||||
<stringProp name="trimInterval">60000</stringProp>
|
||||
<stringProp name="username">perfnode1@perfperformancetest</stringProp>
|
||||
</JDBCDataSource>
|
||||
<hashTree/>
|
||||
<JDBCSampler guiclass="TestBeanGUI" testclass="JDBCSampler" testname="JDBC Request" enabled="true">
|
||||
<stringProp name="dataSource">testpool</stringProp>
|
||||
<stringProp name="query">select dbtransact0_.tx_id as tx_id1_22_0_, dbtransact0_.transaction_value as transact2_22_0_ from perfnode1.node_transactions dbtransact0_ where dbtransact0_.tx_id='${__UUID()}'</stringProp>
|
||||
<stringProp name="queryArguments"></stringProp>
|
||||
<stringProp name="queryArgumentsTypes"></stringProp>
|
||||
<stringProp name="queryTimeout"></stringProp>
|
||||
<stringProp name="queryType">Select Statement</stringProp>
|
||||
<stringProp name="resultSetHandler">Store as String</stringProp>
|
||||
<stringProp name="resultVariable"></stringProp>
|
||||
<stringProp name="variableNames"></stringProp>
|
||||
</JDBCSampler>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
<ResultCollector guiclass="StatGraphVisualizer" testclass="ResultCollector" testname="Aggregate Graph" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="GraphVisualizer" testclass="ResultCollector" testname="Graph Results" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="TableVisualizer" testclass="ResultCollector" testname="View Results in Table" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
<WorkBench guiclass="WorkBenchGui" testclass="WorkBench" testname="WorkBench" enabled="true">
|
||||
<boolProp name="WorkBench.save">true</boolProp>
|
||||
</WorkBench>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
</jmeterTestPlan>
|
246
tools/jmeter/src/main/resources/Testplans/SQL_parmeterized.jmx
Normal file
246
tools/jmeter/src/main/resources/Testplans/SQL_parmeterized.jmx
Normal file
@ -0,0 +1,246 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<jmeterTestPlan version="1.2" properties="3.2" jmeter="3.3 r1808647">
|
||||
<hashTree>
|
||||
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
|
||||
<stringProp name="TestPlan.comments"></stringProp>
|
||||
<boolProp name="TestPlan.functional_mode">false</boolProp>
|
||||
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
|
||||
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="TestPlan.user_define_classpath"></stringProp>
|
||||
</TestPlan>
|
||||
<hashTree>
|
||||
<JDBCDataSource guiclass="TestBeanGUI" testclass="JDBCDataSource" testname="JDBC Connection Configuration" enabled="true">
|
||||
<boolProp name="autocommit">true</boolProp>
|
||||
<stringProp name="checkQuery"></stringProp>
|
||||
<stringProp name="connectionAge">5000</stringProp>
|
||||
<stringProp name="dataSource">testpool</stringProp>
|
||||
<stringProp name="dbUrl">jdbc:sqlserver://perfperformancetest.database.windows.net:1433;databaseName=perftesting</stringProp>
|
||||
<stringProp name="driver">com.microsoft.sqlserver.jdbc.SQLServerDriver</stringProp>
|
||||
<boolProp name="keepAlive">true</boolProp>
|
||||
<stringProp name="password">yourStrong(!)Password</stringProp>
|
||||
<stringProp name="poolMax">0</stringProp>
|
||||
<stringProp name="timeout">10000</stringProp>
|
||||
<stringProp name="transactionIsolation">TRANSACTION_REPEATABLE_READ</stringProp>
|
||||
<stringProp name="trimInterval">60000</stringProp>
|
||||
<stringProp name="username">perfnode1@perfperformancetest</stringProp>
|
||||
</JDBCDataSource>
|
||||
<hashTree/>
|
||||
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
|
||||
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
||||
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
||||
<boolProp name="LoopController.continue_forever">false</boolProp>
|
||||
<stringProp name="LoopController.loops">40000</stringProp>
|
||||
</elementProp>
|
||||
<stringProp name="ThreadGroup.num_threads">3</stringProp>
|
||||
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
|
||||
<longProp name="ThreadGroup.start_time">1509455820000</longProp>
|
||||
<longProp name="ThreadGroup.end_time">1509455820000</longProp>
|
||||
<boolProp name="ThreadGroup.scheduler">false</boolProp>
|
||||
<stringProp name="ThreadGroup.duration"></stringProp>
|
||||
<stringProp name="ThreadGroup.delay"></stringProp>
|
||||
</ThreadGroup>
|
||||
<hashTree>
|
||||
<JDBCSampler guiclass="TestBeanGUI" testclass="JDBCSampler" testname="JDBC Request" enabled="true">
|
||||
<stringProp name="dataSource">testpool</stringProp>
|
||||
<stringProp name="query">declare @sql nvarchar(4000)
|
||||
set @sql = N'select dbtransact0_.tx_id as tx_id1_22_0_, dbtransact0_.transaction_value as transact2_22_0_ from perfnode1.node_transactions dbtransact0_ where dbtransact0_.tx_id=@P0'
|
||||
exec sp_executesql @sql, N'@P0 nvarchar(4000)', '${__UUID()}'</stringProp>
|
||||
<stringProp name="queryArguments"></stringProp>
|
||||
<stringProp name="queryArgumentsTypes"></stringProp>
|
||||
<stringProp name="queryTimeout"></stringProp>
|
||||
<stringProp name="queryType">Select Statement</stringProp>
|
||||
<stringProp name="resultSetHandler">Store as String</stringProp>
|
||||
<stringProp name="resultVariable"></stringProp>
|
||||
<stringProp name="variableNames"></stringProp>
|
||||
</JDBCSampler>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
<ResultCollector guiclass="StatGraphVisualizer" testclass="ResultCollector" testname="Aggregate Graph" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="GraphVisualizer" testclass="ResultCollector" testname="Graph Results" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="TableVisualizer" testclass="ResultCollector" testname="View Results in Table" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
<WorkBench guiclass="WorkBenchGui" testclass="WorkBench" testname="WorkBench" enabled="true">
|
||||
<boolProp name="WorkBench.save">true</boolProp>
|
||||
</WorkBench>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
</jmeterTestPlan>
|
@ -34,14 +34,13 @@ class NativeSgxApiTest {
|
||||
val DUMMY_CASH_ISSUER = DUMMY_CASH_ISSUER_IDENTITY.party.ref(1)
|
||||
val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB"))
|
||||
val MEGA_CORP get() = megaCorp.party
|
||||
val MEGA_CORP_PUBKEY get() = megaCorp.publicKey
|
||||
val MINI_CORP_PUBKEY = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")).publicKey
|
||||
val MEGA_CORP_PUBKEY get() = megaCorp.keyPair.public
|
||||
val MINI_CORP_PUBKEY = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")).keyPair.public
|
||||
}
|
||||
|
||||
private val identityService = rigorousMock<IdentityServiceInternal>().also {
|
||||
doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY)
|
||||
}
|
||||
private val ledgerServices = MockServices(emptyList(), identityService, MEGA_CORP.name)
|
||||
private val ledgerServices = MockServices(emptyList(), rigorousMock<IdentityServiceInternal>().also {
|
||||
doReturn(NativeSgxApiTest.MEGA_CORP).whenever(it).partyFromKey(NativeSgxApiTest.MEGA_CORP_PUBKEY)
|
||||
}, NativeSgxApiTest.MEGA_CORP.name)
|
||||
|
||||
@Ignore("The SGX code is not part of the standard build yet")
|
||||
@Test
|
||||
|
@ -32,17 +32,16 @@ class EnclaveletTest {
|
||||
val DUMMY_CASH_ISSUER = DUMMY_CASH_ISSUER_IDENTITY.party.ref(1)
|
||||
val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB"))
|
||||
val MEGA_CORP get() = megaCorp.party
|
||||
val MEGA_CORP_PUBKEY get() = megaCorp.publicKey
|
||||
val MINI_CORP_PUBKEY = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")).publicKey
|
||||
val MEGA_CORP_PUBKEY get() = megaCorp.keyPair.public
|
||||
val MINI_CORP_PUBKEY = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")).keyPair.public
|
||||
}
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
|
||||
private val identityService = rigorousMock<IdentityServiceInternal>().also {
|
||||
private val ledgerServices = MockServices(emptyList(), rigorousMock<IdentityServiceInternal>().also {
|
||||
doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY)
|
||||
}
|
||||
private val ledgerServices = MockServices(emptyList(), identityService, MEGA_CORP.name)
|
||||
}, MEGA_CORP.name)
|
||||
|
||||
@Ignore("Pending Gradle bug: https://github.com/gradle/gradle/issues/2657")
|
||||
@Test
|
||||
|
Loading…
Reference in New Issue
Block a user