From 64c0d41a5dacf4d98273c23e94fe7268e60e2bb3 Mon Sep 17 00:00:00 2001 From: Tommy Lillehagen Date: Tue, 12 Dec 2017 11:47:14 +0000 Subject: [PATCH 1/9] ENT-1237 Fix run script for IRS Demo web apps --- samples/irs-demo/README.md | 6 +++--- .../web/src/test/resources/scripts/runwebapps.sh | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/samples/irs-demo/README.md b/samples/irs-demo/README.md index b6576f192a..12af545191 100644 --- a/samples/irs-demo/README.md +++ b/samples/irs-demo/README.md @@ -21,8 +21,8 @@ To run from the command line in Windows: ``samples\irs-demo\build`` 2. Run ``gradlew.bat samples:irs-demo:web:deployWebapps`` to install configs and tools for running webservers 3. Run ``cd samples\irs-demo`` to change current working directory -4. Run ``cordapp\build\nodes\runnodes`` to open up several 3 terminals for each nodes -5. Run ``web\build\webapps\webapps`` to open up several 3 terminals for each nodes' webservers +4. Run ``cordapp\build\nodes\runnodes.bat`` to open up several 3 terminals for each nodes +5. Run ``web\build\webapps\webapps.bat`` to open up several 3 terminals for each nodes' webservers This demo also has a web app. To use this, run nodes and then navigate to http://localhost:10007/ and http://localhost:10010/ to see each node's view of the ledger. @@ -33,4 +33,4 @@ view it. *Note:* The IRS web UI currently has a bug when changing the clock time where it may show no numbers or apply fixings inconsistently. The issues will be addressed in a future milestone release. Meanwhile, you can take a look at a simpler -oracle example here: https://github.com/corda/oracle-example. \ No newline at end of file +oracle example here: https://github.com/corda/oracle-example. diff --git a/samples/irs-demo/web/src/test/resources/scripts/runwebapps.sh b/samples/irs-demo/web/src/test/resources/scripts/runwebapps.sh index c87f95821c..23da672eba 100755 --- a/samples/irs-demo/web/src/test/resources/scripts/runwebapps.sh +++ b/samples/irs-demo/web/src/test/resources/scripts/runwebapps.sh @@ -2,13 +2,13 @@ function run_webapp() { if [ ! -z "$TMUX" ]; then - tmux new-window -n $1 $2; [ $? -eq 0 -o $? -eq 143 ] || sh + tmux new-window -n $1 "$2"; [ $? -eq 0 -o $? -eq 143 ] || sh else - xterm -T $1 -e $2; [ $? -eq 0 -o $? -eq 143 ] || sh + xterm -T $1 -e "$2"; [ $? -eq 0 -o $? -eq 143 ] || sh fi; } -run_webapp "NotaryService" "cd \"#DIR#\" && java -Dspring.profiles.active=NotaryService -jar #JAR_PATH#" -run_webapp "BankA" "cd \"#DIR#\" && java -Dspring.profiles.active=BankA -jar #JAR_PATH#" -run_webapp "BankB" "cd \"#DIR#\" && java -Dspring.profiles.active=BankB -jar #JAR_PATH#" +run_webapp "NotaryService" "cd \"#DIR#\" && java -Dspring.profiles.active=NotaryService -jar #JAR_PATH#" & +run_webapp "BankA" "cd \"#DIR#\" && java -Dspring.profiles.active=BankA -jar #JAR_PATH#" & +run_webapp "BankB" "cd \"#DIR#\" && java -Dspring.profiles.active=BankB -jar #JAR_PATH#" & From 537e304536393c06585f30b61fad2ad8c741b0d3 Mon Sep 17 00:00:00 2001 From: Maksymilian Pawlak <120831+m4ksio@users.noreply.github.com> Date: Tue, 12 Dec 2017 13:56:12 +0000 Subject: [PATCH 2/9] IRS demo permissions fix (#2231) --- samples/irs-demo/cordapp/build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/samples/irs-demo/cordapp/build.gradle b/samples/irs-demo/cordapp/build.gradle index 26f7839d04..3b5dbd4c08 100644 --- a/samples/irs-demo/cordapp/build.gradle +++ b/samples/irs-demo/cordapp/build.gradle @@ -53,7 +53,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { "StartFlow.net.corda.irs.flows.UpdateBusinessDayFlow\$Broadcast", "StartFlow.net.corda.irs.api.NodeInterestRates\$UploadFixesFlow", "InvokeRpc.vaultQueryBy", - "InvokeRpc.networkMapSnapshot" + "InvokeRpc.networkMapSnapshot", + "InvokeRpc.currentNodeTime", + "InvokeRpc.wellKnownPartyFromX500Name" ]] ] From 08bbf9061e5267cd0c85b2c3c422addbdcd9c559 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Tue, 12 Dec 2017 15:52:05 +0000 Subject: [PATCH 3/9] Introduce TestIdentity. (#2217) --- .../finance/contracts/CommercialPaperTests.kt | 10 ++++------ .../finance/contracts/asset/CashTests.kt | 4 +--- .../contracts/asset/ObligationTests.kt | 5 +---- .../services/vault/VaultQueryJavaTests.java | 20 +++++++------------ .../persistence/HibernateConfigurationTest.kt | 8 +++----- .../services/vault/NodeVaultServiceTest.kt | 11 ++++------ .../node/services/vault/VaultQueryTests.kt | 11 +++++----- .../node/services/vault/VaultWithCashTest.kt | 12 ++++------- .../corda/irs/api/NodeInterestRatesTest.kt | 9 +++------ .../net/corda/testing/node/MockServices.kt | 9 +++++---- .../kotlin/net/corda/testing/CoreTestUtils.kt | 10 ++++++++++ .../net/corda/loadtest/tests/NotaryTest.kt | 12 ++++------- 12 files changed, 51 insertions(+), 70 deletions(-) diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt index ff6d194737..7b5623e077 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt @@ -3,7 +3,6 @@ package net.corda.finance.contracts import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.* -import net.corda.core.crypto.entropyToKeyPair import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -26,7 +25,6 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized -import java.math.BigInteger import java.time.Instant import java.util.* import kotlin.test.assertFailsWith @@ -91,9 +89,9 @@ class CommercialPaperTestsGeneric { @JvmStatic fun data() = listOf(JavaCommercialPaperTest(), KotlinCommercialPaperTest(), KotlinCommercialPaperLegacyTest()) - private val DUMMY_CASH_ISSUER_KEY = entropyToKeyPair(BigInteger.valueOf(10)) - private val DUMMY_CASH_ISSUER_IDENTITY = getTestPartyAndCertificate(Party(CordaX500Name("Snake Oil Issuer", "London", "GB"), DUMMY_CASH_ISSUER_KEY.public)) - private val DUMMY_CASH_ISSUER = DUMMY_CASH_ISSUER_IDENTITY.party.ref(1) + 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) } @Parameterized.Parameter @@ -243,7 +241,7 @@ class CommercialPaperTestsGeneric { private lateinit var aliceVaultService: VaultService private lateinit var alicesVault: Vault private val notaryServices = MockServices(rigorousMock(), MEGA_CORP.name, DUMMY_NOTARY_KEY) - private val issuerServices = MockServices(listOf("net.corda.finance.contracts"), rigorousMock(), MEGA_CORP.name, DUMMY_CASH_ISSUER_KEY) + private val issuerServices = MockServices(listOf("net.corda.finance.contracts"), rigorousMock(), MEGA_CORP.name, dummyCashIssuer.key) private lateinit var moveTX: SignedTransaction @Test fun `issue move and then redeem`() { diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt index 261ee64b95..0b38ba922e 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt @@ -3,7 +3,6 @@ package net.corda.finance.contracts.asset import com.nhaarman.mockito_kotlin.* import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.* import net.corda.core.node.ServiceHub @@ -30,13 +29,12 @@ import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test -import java.math.BigInteger import java.util.* import kotlin.test.* class CashTests { companion object { - private val DUMMY_CASH_ISSUER_IDENTITY = getTestPartyAndCertificate(Party(CordaX500Name("Snake Oil Issuer", "London", "GB"), entropyToKeyPair(BigInteger.valueOf(10)).public)) + private val DUMMY_CASH_ISSUER_IDENTITY = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10).identity } @Rule diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt index 803954541f..9489de4734 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt @@ -5,12 +5,10 @@ import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.* import net.corda.core.crypto.NullKeys.NULL_PARTY import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.sha256 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.transactions.TransactionBuilder import net.corda.core.utilities.NonEmptySet import net.corda.core.utilities.OpaqueBytes @@ -27,7 +25,6 @@ import net.corda.testing.contracts.DummyState import net.corda.testing.node.MockServices import org.junit.Rule import org.junit.Test -import java.math.BigInteger import java.time.Instant import java.time.temporal.ChronoUnit import java.util.* @@ -38,7 +35,7 @@ import kotlin.test.assertTrue class ObligationTests { companion object { - private val DUMMY_OBLIGATION_ISSUER = Party(CordaX500Name("Snake Oil Issuer", "London", "GB"), entropyToKeyPair(BigInteger.valueOf(10)).public) + private val DUMMY_OBLIGATION_ISSUER = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10).party } @Rule diff --git a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java index f8a19ddb5f..827ab12342 100644 --- a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java +++ b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java @@ -7,8 +7,6 @@ import net.corda.core.contracts.*; import net.corda.core.crypto.CryptoUtils; import net.corda.core.identity.AbstractParty; import net.corda.core.identity.CordaX500Name; -import net.corda.core.identity.Party; -import net.corda.core.identity.PartyAndCertificate; import net.corda.core.messaging.DataFeed; import net.corda.core.node.services.Vault; import net.corda.core.node.services.VaultQueryException; @@ -25,6 +23,7 @@ import net.corda.node.services.api.IdentityServiceInternal; import net.corda.nodeapi.internal.persistence.CordaPersistence; import net.corda.nodeapi.internal.persistence.DatabaseTransaction; import net.corda.testing.SerializationEnvironmentRule; +import net.corda.testing.TestIdentity; import net.corda.testing.contracts.DummyLinearContract; import net.corda.testing.contracts.VaultFiller; import net.corda.testing.node.MockServices; @@ -36,16 +35,13 @@ import rx.Observable; import java.io.IOException; import java.lang.reflect.Field; -import java.math.BigInteger; import java.security.InvalidAlgorithmParameterException; -import java.security.KeyPair; import java.security.cert.CertificateException; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; -import static net.corda.core.crypto.CryptoUtils.entropyToKeyPair; 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; @@ -56,10 +52,8 @@ import static net.corda.testing.node.MockServicesKt.makeTestIdentityService; import static org.assertj.core.api.Assertions.assertThat; public class VaultQueryJavaTests { - private static final CordaX500Name DUMMY_CASH_ISSUER_NAME = new CordaX500Name("Snake Oil Issuer", "London", "GB"); - private static final KeyPair DUMMY_CASH_ISSUER_KEY = entropyToKeyPair(BigInteger.valueOf(10)); - private static final PartyAndCertificate DUMMY_CASH_ISSUER_IDENTITY = getTestPartyAndCertificate(new Party(DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY.getPublic())); - private static final PartyAndReference DUMMY_CASH_ISSUER = DUMMY_CASH_ISSUER_IDENTITY.getParty().ref((byte) 1); + private static final TestIdentity DUMMY_CASH_ISSUER_INFO = new TestIdentity(new CordaX500Name("Snake Oil Issuer", "London", "GB"), (long) 10); + private static final PartyAndReference DUMMY_CASH_ISSUER = DUMMY_CASH_ISSUER_INFO.ref((byte) 1); @Rule public final SerializationEnvironmentRule testSerialization = new SerializationEnvironmentRule(); private VaultFiller vaultFiller; @@ -70,13 +64,13 @@ public class VaultQueryJavaTests { @Before public void setUp() throws CertificateException, InvalidAlgorithmParameterException { List cordappPackages = Arrays.asList("net.corda.testing.contracts", "net.corda.finance.contracts.asset", CashSchemaV1.class.getPackage().getName()); - IdentityServiceInternal identitySvc = makeTestIdentityService(Arrays.asList(getMEGA_CORP_IDENTITY(), DUMMY_CASH_ISSUER_IDENTITY, getDUMMY_NOTARY_IDENTITY())); + IdentityServiceInternal identitySvc = makeTestIdentityService(Arrays.asList(getMEGA_CORP_IDENTITY(), DUMMY_CASH_ISSUER_INFO.getIdentity(), getDUMMY_NOTARY_IDENTITY())); Pair databaseAndServices = makeTestDatabaseAndMockServices( Arrays.asList(getMEGA_CORP_KEY(), getDUMMY_NOTARY_KEY()), identitySvc, cordappPackages, getMEGA_CORP().getName()); - issuerServices = new MockServices(cordappPackages, rigorousMock(IdentityServiceInternal.class), DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY, getBOC_KEY()); + issuerServices = new MockServices(cordappPackages, rigorousMock(IdentityServiceInternal.class), DUMMY_CASH_ISSUER_INFO, getBOC_KEY()); database = databaseAndServices.getFirst(); MockServices services = databaseAndServices.getSecond(); vaultFiller = new VaultFiller(services, getDUMMY_NOTARY(), getDUMMY_NOTARY_KEY()); @@ -469,13 +463,13 @@ public class VaultQueryJavaTests { assertThat(results.getOtherResults().get(1)).isEqualTo(CryptoUtils.toStringShort(getBOC_PUBKEY())); assertThat(results.getOtherResults().get(2)).isEqualTo("GBP"); assertThat(results.getOtherResults().get(3)).isEqualTo(300L); - assertThat(results.getOtherResults().get(4)).isEqualTo(CryptoUtils.toStringShort(DUMMY_CASH_ISSUER_KEY.getPublic())); + assertThat(results.getOtherResults().get(4)).isEqualTo(CryptoUtils.toStringShort(DUMMY_CASH_ISSUER_INFO.getPubkey())); assertThat(results.getOtherResults().get(5)).isEqualTo("GBP"); assertThat(results.getOtherResults().get(6)).isEqualTo(200L); assertThat(results.getOtherResults().get(7)).isEqualTo(CryptoUtils.toStringShort(getBOC_PUBKEY())); assertThat(results.getOtherResults().get(8)).isEqualTo("USD"); assertThat(results.getOtherResults().get(9)).isEqualTo(100L); - assertThat(results.getOtherResults().get(10)).isEqualTo(CryptoUtils.toStringShort(DUMMY_CASH_ISSUER_KEY.getPublic())); + assertThat(results.getOtherResults().get(10)).isEqualTo(CryptoUtils.toStringShort(DUMMY_CASH_ISSUER_INFO.getPubkey())); assertThat(results.getOtherResults().get(11)).isEqualTo("USD"); } catch (NoSuchFieldException e) { diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt index 58666e4204..b29a7f0d69 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt @@ -57,9 +57,7 @@ import javax.persistence.criteria.CriteriaBuilder class HibernateConfigurationTest { private companion object { - val DUMMY_CASH_ISSUER_NAME = CordaX500Name("Snake Oil Issuer", "London", "GB") - val DUMMY_CASH_ISSUER_KEY = entropyToKeyPair(BigInteger.valueOf(10)) - val DUMMY_CASH_ISSUER = Party(DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY.public) + val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10) } @Rule @@ -92,13 +90,13 @@ class HibernateConfigurationTest { fun setUp() { val cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset") bankServices = MockServices(cordappPackages, rigorousMock(), BOC.name, BOC_KEY) - issuerServices = MockServices(cordappPackages, rigorousMock(), DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY) + issuerServices = MockServices(cordappPackages, rigorousMock(), dummyCashIssuer) notaryServices = MockServices(cordappPackages, rigorousMock(), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) notary = notaryServices.myInfo.singleIdentity() val dataSourceProps = makeTestDataSourceProperties() val identityService = rigorousMock().also { mock -> doReturn(null).whenever(mock).wellKnownPartyFromAnonymous(any()) - listOf(DUMMY_CASH_ISSUER, DUMMY_NOTARY).forEach { + listOf(dummyCashIssuer.party, DUMMY_NOTARY).forEach { doReturn(it).whenever(mock).wellKnownPartyFromAnonymous(it) doReturn(it).whenever(mock).wellKnownPartyFromX500Name(it.name) } diff --git a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt index d40bc86126..281a222234 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt @@ -6,7 +6,6 @@ import com.nhaarman.mockito_kotlin.doNothing import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.* import net.corda.core.crypto.NullKeys -import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.* import net.corda.core.internal.packageName @@ -43,7 +42,6 @@ import org.junit.Rule import org.junit.Test import rx.observers.TestSubscriber import java.math.BigDecimal -import java.math.BigInteger import java.util.* import java.util.concurrent.CountDownLatch import java.util.concurrent.Executors @@ -54,10 +52,9 @@ import kotlin.test.assertTrue class NodeVaultServiceTest { private companion object { val cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName) - val DUMMY_CASH_ISSUER_NAME = CordaX500Name("Snake Oil Issuer", "London", "GB") - val DUMMY_CASH_ISSUER_KEY = entropyToKeyPair(BigInteger.valueOf(10)) - val DUMMY_CASH_ISSUER_IDENTITY = getTestPartyAndCertificate(Party(DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY.public)) - val DUMMY_CASH_ISSUER = DUMMY_CASH_ISSUER_IDENTITY.party.ref(1) + val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10) + val DUMMY_CASH_ISSUER_IDENTITY get() = dummyCashIssuer.identity + val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1) } @Rule @@ -84,7 +81,7 @@ class NodeVaultServiceTest { vaultFiller = VaultFiller(services, DUMMY_NOTARY, DUMMY_NOTARY_KEY) // This is safe because MockServices only ever have a single identity identity = services.myInfo.singleIdentityAndCert() - issuerServices = MockServices(cordappPackages, rigorousMock(), DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY) + issuerServices = MockServices(cordappPackages, rigorousMock(), dummyCashIssuer) bocServices = MockServices(cordappPackages, rigorousMock(), BOC_NAME, BOC_KEY) services.identityService.verifyAndRegisterIdentity(DUMMY_CASH_ISSUER_IDENTITY) services.identityService.verifyAndRegisterIdentity(BOC_IDENTITY) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index 33b15435b1..95c3c6d0a1 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -49,10 +49,9 @@ import java.util.* class VaultQueryTests { private companion object { - val DUMMY_CASH_ISSUER_KEY = entropyToKeyPair(BigInteger.valueOf(10)) - val DUMMY_CASH_ISSUER_IDENTITY = getTestPartyAndCertificate(Party(CordaX500Name("Snake Oil Issuer", "London", "GB"), DUMMY_CASH_ISSUER_KEY.public)) - val DUMMY_CASH_ISSUER = DUMMY_CASH_ISSUER_IDENTITY.party.ref(1) - val DUMMY_OBLIGATION_ISSUER = Party(CordaX500Name("Snake Oil Issuer", "London", "GB"), entropyToKeyPair(BigInteger.valueOf(10)).public) + val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10) + val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1) + val DUMMY_OBLIGATION_ISSUER = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10).party } @Rule @@ -86,14 +85,14 @@ class VaultQueryTests { // register additional identities val databaseAndServices = makeTestDatabaseAndMockServices( listOf(MEGA_CORP_KEY, DUMMY_NOTARY_KEY), - makeTestIdentityService(listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY)), + makeTestIdentityService(listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, DUMMY_NOTARY_IDENTITY)), cordappPackages, MEGA_CORP.name) database = databaseAndServices.first services = databaseAndServices.second vaultFiller = VaultFiller(services, DUMMY_NOTARY, DUMMY_NOTARY_KEY) vaultFillerCashNotary = VaultFiller(services, DUMMY_NOTARY, DUMMY_NOTARY_KEY, CASH_NOTARY) - notaryServices = MockServices(cordappPackages, rigorousMock(), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY, DUMMY_CASH_ISSUER_KEY, BOC_KEY, MEGA_CORP_KEY) + notaryServices = MockServices(cordappPackages, rigorousMock(), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY, dummyCashIssuer.key, BOC_KEY, MEGA_CORP_KEY) identitySvc = services.identityService // Register all of the identities we're going to use (notaryServices.myInfo.legalIdentitiesAndCerts + BOC_IDENTITY + CASH_NOTARY_IDENTITY + MINI_CORP_IDENTITY + MEGA_CORP_IDENTITY).forEach { identity -> diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt index 2bed2fd84e..3a0d957869 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt @@ -1,7 +1,6 @@ package net.corda.node.services.vault import net.corda.core.contracts.* -import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name @@ -32,7 +31,6 @@ import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test -import java.math.BigInteger import java.util.concurrent.Executors import kotlin.test.assertEquals import kotlin.test.fail @@ -42,10 +40,8 @@ import kotlin.test.fail class VaultWithCashTest { private companion object { val cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", CashSchemaV1::class.packageName) - val DUMMY_CASH_ISSUER_NAME = CordaX500Name("Snake Oil Issuer", "London", "GB") - val DUMMY_CASH_ISSUER_KEY = entropyToKeyPair(BigInteger.valueOf(10)) - val DUMMY_CASH_ISSUER_IDENTITY = getTestPartyAndCertificate(Party(DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY.public)) - val DUMMY_CASH_ISSUER = DUMMY_CASH_ISSUER_IDENTITY.party.ref(1) + val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10) + val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1) } @Rule @@ -64,13 +60,13 @@ class VaultWithCashTest { LogHelper.setLevel(VaultWithCashTest::class) val databaseAndServices = makeTestDatabaseAndMockServices( listOf(generateKeyPair(), DUMMY_NOTARY_KEY), - makeTestIdentityService(listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY)), + makeTestIdentityService(listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, DUMMY_NOTARY_IDENTITY)), cordappPackages, MEGA_CORP.name) database = databaseAndServices.first services = databaseAndServices.second vaultFiller = VaultFiller(services, DUMMY_NOTARY, DUMMY_NOTARY_KEY) - issuerServices = MockServices(cordappPackages, rigorousMock(), DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY, MEGA_CORP_KEY) + issuerServices = MockServices(cordappPackages, rigorousMock(), dummyCashIssuer, MEGA_CORP_KEY) notaryServices = MockServices(cordappPackages, rigorousMock(), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) notary = notaryServices.myInfo.legalIdentitiesAndCerts.single().party } diff --git a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index ad45bbfbc6..a42a210a6d 100644 --- a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -3,7 +3,6 @@ package net.corda.irs.api import net.corda.core.contracts.Command import net.corda.core.contracts.ContractState import net.corda.core.contracts.TransactionState -import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.transactions.TransactionBuilder @@ -44,10 +43,8 @@ class NodeInterestRatesTest { EURIBOR 2016-03-15 1M = 0.123 EURIBOR 2016-03-15 2M = 0.111 """.trimIndent()) - - private val DUMMY_CASH_ISSUER_KEY = generateKeyPair() - private val DUMMY_CASH_ISSUER = Party(CordaX500Name(organisation = "Cash issuer", locality = "London", country = "GB"), DUMMY_CASH_ISSUER_KEY.public) - private val services = MockServices(listOf("net.corda.finance.contracts.asset"), rigorousMock(), DUMMY_CASH_ISSUER.name, DUMMY_CASH_ISSUER_KEY, MEGA_CORP_KEY) + private val dummyCashIssuer = TestIdentity(CordaX500Name("Cash issuer", "London", "GB")) + private val services = MockServices(listOf("net.corda.finance.contracts.asset"), rigorousMock(), dummyCashIssuer, MEGA_CORP_KEY) // This is safe because MockServices only ever have a single identity private val identity = services.myInfo.singleIdentity() @@ -244,7 +241,7 @@ class NodeInterestRatesTest { } private fun makePartialTX() = TransactionBuilder(DUMMY_NOTARY).withItems( - TransactionState(1000.DOLLARS.CASH issuedBy DUMMY_CASH_ISSUER ownedBy ALICE, Cash.PROGRAM_ID, DUMMY_NOTARY)) + TransactionState(1000.DOLLARS.CASH issuedBy dummyCashIssuer.party ownedBy ALICE, Cash.PROGRAM_ID, DUMMY_NOTARY)) private fun makeFullTx() = makePartialTX().withItems(dummyCommand()) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index f0870f6a32..c04165319e 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -52,7 +52,7 @@ open class MockServices private constructor( override val validatedTransactions: WritableTransactionStorage, override val identityService: IdentityServiceInternal, private val initialIdentityName: CordaX500Name, - vararg val keys: KeyPair + val keys: Array ) : ServiceHub, StateLoader by validatedTransactions { companion object { @JvmStatic @@ -91,7 +91,7 @@ open class MockServices private constructor( val schemaService = NodeSchemaService(cordappLoader.cordappSchemas) val database = configureDatabase(dataSourceProps, DatabaseConfig(), identityService, schemaService) val mockService = database.transaction { - object : MockServices(cordappLoader, identityService, initialIdentityName, *(keys.toTypedArray())) { + object : MockServices(cordappLoader, identityService, initialIdentityName, keys.toTypedArray()) { override val vaultService: VaultServiceInternal = makeVaultService(database.hibernateConfig, schemaService) override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable) { @@ -107,8 +107,9 @@ open class MockServices private constructor( } } - private constructor(cordappLoader: CordappLoader, identityService: IdentityServiceInternal, initialIdentityName: CordaX500Name, vararg keys: KeyPair) : this(cordappLoader, MockTransactionStorage(), identityService, initialIdentityName, *keys) - constructor(cordappPackages: List, identityService: IdentityServiceInternal, initialIdentityName: CordaX500Name, vararg keys: KeyPair) : this(CordappLoader.createWithTestPackages(cordappPackages), identityService, initialIdentityName, *keys) + private constructor(cordappLoader: CordappLoader, identityService: IdentityServiceInternal, initialIdentityName: CordaX500Name, keys: Array) : this(cordappLoader, MockTransactionStorage(), identityService, initialIdentityName, keys) + constructor(cordappPackages: List, identityService: IdentityServiceInternal, initialIdentityName: CordaX500Name, vararg keys: KeyPair) : this(CordappLoader.createWithTestPackages(cordappPackages), identityService, initialIdentityName, keys) + constructor(cordappPackages: List, identityService: IdentityServiceInternal, initialIdentity: TestIdentity, vararg moreKeys: KeyPair) : this(CordappLoader.createWithTestPackages(cordappPackages), identityService, initialIdentity.name, arrayOf(initialIdentity.key) + moreKeys) constructor(identityService: IdentityServiceInternal, initialIdentityName: CordaX500Name, vararg keys: KeyPair) : this(emptyList(), identityService, initialIdentityName, *keys) constructor(identityService: IdentityServiceInternal, initialIdentityName: CordaX500Name) : this(identityService, initialIdentityName, generateKeyPair()) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index a6dc7f1c09..433acd7c59 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -6,6 +6,7 @@ package net.corda.testing import net.corda.core.contracts.StateRef import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -30,6 +31,7 @@ import org.bouncycastle.cert.X509CertificateHolder import org.mockito.Mockito.mock import org.mockito.internal.stubbing.answers.ThrowsException import java.lang.reflect.Modifier +import java.math.BigInteger import java.nio.file.Files import java.security.KeyPair import java.security.PublicKey @@ -151,6 +153,14 @@ fun getTestPartyAndCertificate(name: CordaX500Name, publicKey: PublicKey): Party return getTestPartyAndCertificate(Party(name, publicKey)) } +class TestIdentity @JvmOverloads constructor(val name: CordaX500Name, entropy: Long? = null) { + val key = if (entropy != null) entropyToKeyPair(BigInteger.valueOf(entropy)) else generateKeyPair() + val pubkey get() = key.public!! + val party = Party(name, pubkey) + val identity by lazy { getTestPartyAndCertificate(party) } // Often not needed. + fun ref(vararg bytes: Byte) = party.ref(*bytes) +} + @Suppress("unused") inline fun T.kryoSpecific(reason: String, function: () -> Unit) = if (!AMQP_ENABLED) { function() diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt index 592f0375a4..34e6ea9ffb 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt @@ -1,11 +1,9 @@ package net.corda.loadtest.tests import net.corda.client.mock.Generator -import net.corda.core.crypto.entropyToKeyPair import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FlowException import net.corda.core.identity.CordaX500Name -import net.corda.core.identity.Party import net.corda.core.internal.concurrent.thenMatch import net.corda.core.messaging.startFlow import net.corda.core.transactions.SignedTransaction @@ -16,25 +14,23 @@ import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestIdentityService import org.slf4j.LoggerFactory -import java.math.BigInteger private val log = LoggerFactory.getLogger("NotaryTest") -private val DUMMY_CASH_ISSUER_KEY = entropyToKeyPair(BigInteger.valueOf(10)) -private val DUMMY_CASH_ISSUER_IDENTITY = getTestPartyAndCertificate(Party(CordaX500Name("Snake Oil Issuer", "London", "GB"), DUMMY_CASH_ISSUER_KEY.public)) -private val DUMMY_CASH_ISSUER = DUMMY_CASH_ISSUER_IDENTITY.party.ref(1) +private val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10) +private val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1) data class NotariseCommand(val issueTx: SignedTransaction, val moveTx: SignedTransaction, val node: NodeConnection) val dummyNotarisationTest = LoadTest( "Notarising dummy transactions", generate = { _, _ -> - val issuerServices = MockServices(makeTestIdentityService(listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY)), MEGA_CORP.name, DUMMY_CASH_ISSUER_KEY) + val issuerServices = MockServices(makeTestIdentityService(listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, DUMMY_NOTARY_IDENTITY)), MEGA_CORP.name, dummyCashIssuer.key) val generateTx = Generator.pickOne(simpleNodes).flatMap { node -> Generator.int().map { val issueBuilder = DummyContract.generateInitial(it, notary.info.legalIdentities[0], DUMMY_CASH_ISSUER) // TODO notary choice val issueTx = issuerServices.signInitialTransaction(issueBuilder) val asset = issueTx.tx.outRef(0) - val moveBuilder = DummyContract.move(asset, DUMMY_CASH_ISSUER.party) + val moveBuilder = DummyContract.move(asset, dummyCashIssuer.party) val moveTx = issuerServices.signInitialTransaction(moveBuilder) NotariseCommand(issueTx, moveTx, node) } From 42782f889062dca4b2b3bcd3455b075ff1ce2d74 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Tue, 12 Dec 2017 16:52:14 +0000 Subject: [PATCH 4/9] ENT-1151: Rework unicode block validation rules (#2125) * Redo legal name validation rules so that direction change chars are rejected * Split name validation into minimal rules that all nodes can require, plus extended rules that the Doorman will apply (and we may need to change, without updating the entire network). * Break down name validation rule sets to better match expectations * Add test for nulls in Corda names --- .../net/corda/core/identity/CordaX500Name.kt | 2 +- .../corda/core/internal/LegalNameValidator.kt | 79 +++++++++++------- .../core/internal/LegalNameValidatorTest.kt | 81 ++++++++++++++----- docs/source/generating-a-node.rst | 12 ++- .../net/corda/demobench/views/NodeTabView.kt | 9 ++- 5 files changed, 126 insertions(+), 57 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt index ee8baa8733..c3e394b6b5 100644 --- a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt +++ b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt @@ -45,7 +45,7 @@ data class CordaX500Name(val commonName: String?, init { // Legal name checks. - LegalNameValidator.validateOrganization(organisation) + LegalNameValidator.validateOrganization(organisation, LegalNameValidator.Validation.MINIMAL) // Attribute data width checks. require(country.length == LENGTH_COUNTRY) { "Invalid country '$country' Country code must be $LENGTH_COUNTRY letters ISO code " } diff --git a/core/src/main/kotlin/net/corda/core/internal/LegalNameValidator.kt b/core/src/main/kotlin/net/corda/core/internal/LegalNameValidator.kt index 892f83b224..ffdba208b8 100644 --- a/core/src/main/kotlin/net/corda/core/internal/LegalNameValidator.kt +++ b/core/src/main/kotlin/net/corda/core/internal/LegalNameValidator.kt @@ -1,18 +1,27 @@ package net.corda.core.internal -import java.lang.Character.UnicodeScript.* +import net.corda.core.internal.LegalNameValidator.normalize import java.text.Normalizer -import java.util.regex.Pattern import javax.security.auth.x500.X500Principal object LegalNameValidator { + enum class Validation { + MINIMAL, + FULL + } + @Deprecated("Use validateOrganization instead", replaceWith = ReplaceWith("validateOrganization(normalizedLegalName)")) - fun validateLegalName(normalizedLegalName: String) = validateOrganization(normalizedLegalName) + fun validateLegalName(normalizedLegalName: String) = validateOrganization(normalizedLegalName, Validation.FULL) /** * The validation function validates a string for use as part of a legal name. It applies the following rules: * - * - No blacklisted words like "node", "server". + * - Does not contain the null character + * - Must be normalized (as per the [normalize] function). + * - Length must be 255 characters or shorter. + * + * Full validation (typically this is only done for names the Doorman approves) adds: + * * - Restrict names to Latin scripts for now to avoid right-to-left issues, debugging issues when we can't pronounce * names over the phone, and character confusability attacks. * - No commas or equals signs. @@ -20,25 +29,37 @@ object LegalNameValidator { * * @throws IllegalArgumentException if the name does not meet the required rules. The message indicates why not. */ - fun validateNameAttribute(normalizedNameAttribute: String) { - Rule.baseNameRules.forEach { it.validate(normalizedNameAttribute) } + fun validateNameAttribute(normalizedNameAttribute: String, validation: Validation) { + when (validation) { + Validation.MINIMAL -> Rule.attributeRules.forEach { it.validate(normalizedNameAttribute) } + Validation.FULL -> Rule.attributeFullRules.forEach { it.validate(normalizedNameAttribute) } + } } /** * The validation function validates a string for use as the organization attribute of a name, which includes additional - * constraints over basic name attribute checks. It applies the following rules: + * constraints over basic name attribute checks. It applies the following additional rules: * + * - Must be normalized (as per the [normalize] function). + * - Length must be 255 characters or shorter. * - No blacklisted words like "node", "server". + * - Must consist of at least three letters. + * + * Full validation (typically this is only done for names the Doorman approves) adds: + * * - Restrict names to Latin scripts for now to avoid right-to-left issues, debugging issues when we can't pronounce * names over the phone, and character confusability attacks. - * - Must consist of at least three letters and should start with a capital letter. + * - Must start with a capital letter. * - No commas or equals signs. * - No dollars or quote marks, we might need to relax the quote mark constraint in future to handle Irish company names. * * @throws IllegalArgumentException if the name does not meet the required rules. The message indicates why not. */ - fun validateOrganization(normalizedOrganization: String) { - Rule.legalNameRules.forEach { it.validate(normalizedOrganization) } + fun validateOrganization(normalizedOrganization: String, validation: Validation) { + when (validation) { + Validation.MINIMAL -> Rule.legalNameRules.forEach { it.validate(normalizedOrganization) } + Validation.FULL -> Rule.legalNameFullRules.forEach { it.validate(normalizedOrganization) } + } } @Deprecated("Use normalize instead", replaceWith = ReplaceWith("normalize(legalName)")) @@ -57,18 +78,27 @@ object LegalNameValidator { sealed class Rule { companion object { - val baseNameRules: List> = listOf( + val attributeRules: List> = listOf( UnicodeNormalizationRule(), - CharacterRule(',', '=', '$', '"', '\'', '\\'), - WordRule("node", "server"), LengthRule(maxLength = 255), + MustHaveAtLeastTwoLettersRule(), + CharacterRule('\u0000') // Ban null + ) + val attributeFullRules: List> = attributeRules + listOf( + CharacterRule(',', '=', '$', '"', '\'', '\\'), // TODO: Implement confusable character detection if we add more scripts. - UnicodeRangeRule(LATIN, COMMON, INHERITED), + UnicodeRangeRule(Character.UnicodeBlock.BASIC_LATIN), + CapitalLetterRule() + ) + val legalNameRules: List> = attributeRules + listOf( + WordRule("node", "server"), X500NameRule() ) - val legalNameRules: List> = baseNameRules + listOf( - CapitalLetterRule(), - MustHaveAtLeastTwoLettersRule() + val legalNameFullRules: List> = legalNameRules + listOf( + CharacterRule(',', '=', '$', '"', '\'', '\\'), + // TODO: Implement confusable character detection if we add more scripts. + UnicodeRangeRule(Character.UnicodeBlock.BASIC_LATIN), + CapitalLetterRule() ) } @@ -80,18 +110,13 @@ object LegalNameValidator { } } - private class UnicodeRangeRule(vararg supportScripts: Character.UnicodeScript) : Rule() { - private val pattern = supportScripts.map { "\\p{Is$it}" }.joinToString(separator = "", prefix = "[", postfix = "]*").let { Pattern.compile(it) } + private class UnicodeRangeRule(vararg supportScripts: Character.UnicodeBlock) : Rule() { + val supportScriptsSet = supportScripts.toSet() override fun validate(legalName: String) { - require(pattern.matcher(legalName).matches()) { - val illegalChars = legalName.replace(pattern.toRegex(), "").toSet() - if (illegalChars.size > 1) { - "Forbidden characters $illegalChars in \"$legalName\"." - } else { - "Forbidden character $illegalChars in \"$legalName\"." - } - } + val illegalChars = legalName.toCharArray().filter { Character.UnicodeBlock.of(it) !in supportScriptsSet }.size + // We don't expose the characters or the legal name, for security reasons + require (illegalChars == 0) { "$illegalChars forbidden characters in legal name." } } } diff --git a/core/src/test/kotlin/net/corda/core/internal/LegalNameValidatorTest.kt b/core/src/test/kotlin/net/corda/core/internal/LegalNameValidatorTest.kt index fa854b42e4..5db052fcc1 100644 --- a/core/src/test/kotlin/net/corda/core/internal/LegalNameValidatorTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/LegalNameValidatorTest.kt @@ -8,55 +8,94 @@ class LegalNameValidatorTest { @Test fun `no double spaces`() { assertFailsWith(IllegalArgumentException::class) { - LegalNameValidator.validateOrganization("Test Legal Name") + LegalNameValidator.validateOrganization("Test Legal Name", LegalNameValidator.Validation.FULL) } - LegalNameValidator.validateOrganization(LegalNameValidator.normalize("Test Legal Name")) + LegalNameValidator.validateOrganization(LegalNameValidator.normalize("Test Legal Name"), LegalNameValidator.Validation.FULL) } @Test fun `no trailing white space`() { assertFailsWith(IllegalArgumentException::class) { - LegalNameValidator.validateOrganization("Test ") + LegalNameValidator.validateOrganization("Test ", LegalNameValidator.Validation.FULL) } } @Test fun `no prefixed white space`() { assertFailsWith(IllegalArgumentException::class) { - LegalNameValidator.validateOrganization(" Test") + LegalNameValidator.validateOrganization(" Test", LegalNameValidator.Validation.FULL) } } @Test fun `blacklisted words`() { assertFailsWith(IllegalArgumentException::class) { - LegalNameValidator.validateOrganization("Test Server") + LegalNameValidator.validateOrganization("Test Server", LegalNameValidator.Validation.FULL) } } @Test fun `blacklisted characters`() { - LegalNameValidator.validateOrganization("Test") + LegalNameValidator.validateOrganization("Test", LegalNameValidator.Validation.FULL) assertFailsWith(IllegalArgumentException::class) { - LegalNameValidator.validateOrganization("\$Test") + LegalNameValidator.validateOrganization("\$Test", LegalNameValidator.Validation.FULL) } assertFailsWith(IllegalArgumentException::class) { - LegalNameValidator.validateOrganization("\"Test") + LegalNameValidator.validateOrganization("\"Test", LegalNameValidator.Validation.FULL) } assertFailsWith(IllegalArgumentException::class) { - LegalNameValidator.validateOrganization("\'Test") + LegalNameValidator.validateOrganization("\'Test", LegalNameValidator.Validation.FULL) } assertFailsWith(IllegalArgumentException::class) { - LegalNameValidator.validateOrganization("=Test") + LegalNameValidator.validateOrganization("=Test", LegalNameValidator.Validation.FULL) } } @Test - fun `unicode range`() { - LegalNameValidator.validateOrganization("Test A") + fun `unicode range in organization`() { + LegalNameValidator.validateOrganization("The quick brown fox jumped over the lazy dog.1234567890", LegalNameValidator.Validation.FULL) + assertFailsWith(IllegalArgumentException::class) { + // Null + LegalNameValidator.validateOrganization("\u0000R3 Null", LegalNameValidator.Validation.FULL) + } + assertFailsWith(IllegalArgumentException::class) { + // Right to left direction override + LegalNameValidator.validateOrganization("\u202EdtL 3R", LegalNameValidator.Validation.FULL) + } assertFailsWith(IllegalArgumentException::class) { // Greek letter A. - LegalNameValidator.validateOrganization("Test Α") + LegalNameValidator.validateOrganization("Test \u0391", LegalNameValidator.Validation.FULL) + } + // Latin capital letter turned m + assertFailsWith { + LegalNameValidator.validateOrganization( "Test\u019CLtd", LegalNameValidator.Validation.FULL) + } + // Latin small letter turned e + assertFailsWith { + LegalNameValidator.validateOrganization("Test\u01ddLtd", LegalNameValidator.Validation.FULL) + } + } + + @Test + fun `unicode range in general attributes`() { + LegalNameValidator.validateNameAttribute("The quick brown fox jumped over the lazy dog.1234567890", LegalNameValidator.Validation.FULL) + assertFailsWith(IllegalArgumentException::class) { + // Right to left direction override + LegalNameValidator.validateNameAttribute("\u202EdtL 3R", LegalNameValidator.Validation.FULL) + } + // Right to left direction override is okay with minimal validation though + LegalNameValidator.validateNameAttribute("\u202EdtL 3R", LegalNameValidator.Validation.MINIMAL) + assertFailsWith(IllegalArgumentException::class) { + // Greek letter A. + LegalNameValidator.validateNameAttribute("Test \u0391", LegalNameValidator.Validation.FULL) + } + // Latin capital letter turned m + assertFailsWith { + LegalNameValidator.validateNameAttribute( "Test\u019CLtd", LegalNameValidator.Validation.FULL) + } + // Latin small letter turned e + assertFailsWith { + LegalNameValidator.validateNameAttribute("Test\u01ddLtd", LegalNameValidator.Validation.FULL) } } @@ -66,21 +105,21 @@ class LegalNameValidatorTest { while (longLegalName.length < 255) { longLegalName.append("A") } - LegalNameValidator.validateOrganization(longLegalName.toString()) + LegalNameValidator.validateOrganization(longLegalName.toString(), LegalNameValidator.Validation.FULL) assertFailsWith(IllegalArgumentException::class) { - LegalNameValidator.validateOrganization(longLegalName.append("A").toString()) + LegalNameValidator.validateOrganization(longLegalName.append("A").toString(), LegalNameValidator.Validation.FULL) } } @Test fun `legal name should be capitalized`() { - LegalNameValidator.validateOrganization("Good legal name") + LegalNameValidator.validateOrganization("Good legal name", LegalNameValidator.Validation.FULL) assertFailsWith(IllegalArgumentException::class) { - LegalNameValidator.validateOrganization("bad name") + LegalNameValidator.validateOrganization("bad name", LegalNameValidator.Validation.FULL) } assertFailsWith(IllegalArgumentException::class) { - LegalNameValidator.validateOrganization("bad Name") + LegalNameValidator.validateOrganization("bad Name", LegalNameValidator.Validation.FULL) } } @@ -90,13 +129,13 @@ class LegalNameValidatorTest { assertEquals("Legal Name With Unicode Whitespaces", LegalNameValidator.normalize("Legal Name\u2004With\u0009Unicode\u0020Whitespaces")) assertEquals("Legal Name With Line Breaks", LegalNameValidator.normalize("Legal Name With\n\rLine\nBreaks")) assertFailsWith(IllegalArgumentException::class) { - LegalNameValidator.validateOrganization("Legal Name With\tTab") + LegalNameValidator.validateOrganization("Legal Name With\tTab", LegalNameValidator.Validation.FULL) } assertFailsWith(IllegalArgumentException::class) { - LegalNameValidator.validateOrganization("Legal Name\u2004With\u0009Unicode\u0020Whitespaces") + LegalNameValidator.validateOrganization("Legal Name\u2004With\u0009Unicode\u0020Whitespaces", LegalNameValidator.Validation.FULL) } assertFailsWith(IllegalArgumentException::class) { - LegalNameValidator.validateOrganization("Legal Name With\n\rLine\nBreaks") + LegalNameValidator.validateOrganization("Legal Name With\n\rLine\nBreaks", LegalNameValidator.Validation.FULL) } } } \ No newline at end of file diff --git a/docs/source/generating-a-node.rst b/docs/source/generating-a-node.rst index 614c8084ec..4a721e304d 100644 --- a/docs/source/generating-a-node.rst +++ b/docs/source/generating-a-node.rst @@ -50,17 +50,21 @@ The name must also obey the following constraints: * The country attribute is a valid ISO 3166-1 two letter code in upper-case -* The organisation field of the name obeys the following constraints: +* All attributes must obey the following constraints: * Upper-case first letter * Has at least two letters * No leading or trailing whitespace - * No double-spacing - * Does not contain the words "node" or "server" * Does not include the following characters: ``,`` , ``=`` , ``$`` , ``"`` , ``'`` , ``\`` * Is in NFKC normalization form + * Does not contain the null character * Only the latin, common and inherited unicode scripts are supported +* The organisation field of the name also obeys the following constraints: + + * No double-spacing + * Does not contain the words "node" or "server" + * This is to avoid right-to-left issues, debugging issues when we can't pronounce names over the phone, and character confusability attacks @@ -149,4 +153,4 @@ in the ``deployNodes`` task, plus a ``runnodes`` shell script (or batch file on for testing and development purposes. If you make any changes to your CorDapp source or ``deployNodes`` task, you will need to re-run the task to see the changes take effect. -You can now run the nodes by following the instructions in :doc:`Running a node `. \ No newline at end of file +You can now run the nodes by following the instructions in :doc:`Running a node `. diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt index cede37faac..5fb32aa9e6 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt @@ -204,14 +204,15 @@ class NodeTabView : Fragment() { private fun Pane.nodeNameField() = textfield(model.legalName) { minWidth = textWidth - validator { - if (it == null) { + validator { rawName -> + val normalizedName: String? = rawName?.let(LegalNameValidator::normalize) + if (normalizedName == null) { error("Node name is required") - } else if (nodeController.nameExists(LegalNameValidator.normalize(it))) { + } else if (nodeController.nameExists(normalizedName)) { error("Node with this name already exists") } else { try { - LegalNameValidator.validateOrganization(LegalNameValidator.normalize(it)) + LegalNameValidator.validateOrganization(normalizedName, LegalNameValidator.Validation.MINIMAL) null } catch (e: IllegalArgumentException) { error(e.message) From 905c8252a6946c2374d688f4d470845ae8946849 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Tue, 12 Dec 2017 18:03:06 +0000 Subject: [PATCH 5/9] CORDA-654 Remaining key constants (#2226) --- .../client/jackson/JacksonSupportTest.kt | 8 +- .../client/rpc/CordaRPCJavaClientTest.java | 4 +- .../client/rpc/BlacklistKotlinClosureTest.kt | 4 +- .../corda/client/rpc/CordaRPCClientTest.kt | 2 +- .../confidential/SwapIdentitiesFlowTests.kt | 20 ++-- .../net/corda/core/flows/FlowsInJavaTest.java | 4 +- .../core/contracts/DummyContractV2Tests.kt | 5 + .../core/crypto/PartialMerkleTreeTest.kt | 11 ++ .../net/corda/core/flows/AttachmentTests.kt | 13 +- .../core/flows/CollectSignaturesFlowTests.kt | 13 +- .../net/corda/core/flows/FinalityFlowTests.kt | 4 + .../net/corda/core/identity/PartyTest.kt | 4 +- .../core/internal/AbstractAttachmentTest.kt | 20 ++-- .../internal/ResolveTransactionsFlowTest.kt | 10 +- .../net/corda/core/node/VaultUpdateTests.kt | 8 +- .../TransactionSerializationTests.kt | 11 ++ .../CompatibleTransactionTests.kt | 2 + .../LedgerTransactionQueryTests.kt | 7 +- .../TransactionEncumbranceTests.kt | 9 ++ .../core/transactions/TransactionTests.kt | 5 + .../corda/docs/IntegrationTestingTutorial.kt | 4 +- .../java/net/corda/docs/FlowCookbookJava.java | 6 +- .../tutorial/testdsl/CommercialPaperTest.java | 113 ++++++++++-------- .../net/corda/docs/ClientRpcTutorial.kt | 4 +- .../kotlin/net/corda/docs/FlowCookbook.kt | 6 +- .../tutorial/tearoffs/TutorialTearOffs.kt | 4 +- .../docs/tutorial/testdsl/TutorialTestDSL.kt | 14 +++ .../corda/finance/contracts/universal/Cap.kt | 7 +- .../finance/contracts/universal/Caplet.kt | 1 - .../contracts/universal/ContractDefinition.kt | 13 +- .../contracts/universal/FXFwdTimeOption.kt | 1 - .../finance/contracts/universal/FXSwap.kt | 1 - .../corda/finance/contracts/universal/IRS.kt | 1 - .../contracts/universal/RollOutTests.kt | 1 - .../finance/contracts/universal/Swaption.kt | 1 - .../contracts/universal/ZeroCouponBond.kt | 1 - .../contracts/asset/CashTestsJava.java | 38 +++--- .../finance/contracts/CommercialPaperTests.kt | 24 +++- .../finance/contracts/asset/CashTests.kt | 29 ++++- .../contracts/asset/ObligationTests.kt | 23 +++- ...tachmentsClassLoaderStaticContractTests.kt | 6 + .../internal/AttachmentsClassLoaderTests.kt | 4 +- .../internal/crypto/X509UtilitiesTest.kt | 13 +- .../ContractAttachmentSerializerTest.kt | 3 +- .../internal/serialization/KryoTests.kt | 7 +- .../amqp/SerializationOutputTests.kt | 16 ++- .../kotlin/net/corda/node/BootTests.kt | 6 +- .../corda/node/CordappScanningDriverTest.kt | 8 +- .../net/corda/node/NodePerformanceTests.kt | 4 +- .../kotlin/net/corda/node/SSHServerTest.kt | 17 ++- .../node/services/AttachmentLoadingTests.kt | 8 +- .../node/services/DistributedServiceTests.kt | 4 +- .../node/services/RaftNotaryServiceTests.kt | 8 +- .../node/services/network/NetworkMapTest.kt | 19 ++- .../services/network/NodeInfoWatcherTest.kt | 8 +- .../network/PersistentNetworkMapCacheTest.kt | 12 +- .../statemachine/FlowVersioningTest.kt | 9 +- .../statemachine/LargeTransactionsTest.kt | 5 + .../messaging/MQSecurityAsNodeTest.kt | 7 +- .../services/messaging/MQSecurityTest.kt | 9 +- .../services/messaging/P2PMessagingTest.kt | 4 +- .../services/vault/VaultQueryJavaTests.java | 36 +++--- .../net/corda/node/InteractiveShellTest.kt | 12 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 10 +- .../corda/node/services/NotaryChangeTests.kt | 6 +- .../config/NodeConfigurationImplTest.kt | 7 +- .../events/NodeSchedulerServiceTest.kt | 9 +- .../identity/InMemoryIdentityServiceTests.kt | 12 +- .../PersistentIdentityServiceTests.kt | 11 ++ .../messaging/ArtemisMessagingTests.kt | 2 +- .../persistence/DBTransactionStorageTests.kt | 5 + .../persistence/HibernateConfigurationTest.kt | 20 ++-- .../services/schema/HibernateObserverTests.kt | 4 +- .../PersistentUniquenessProviderTests.kt | 4 +- .../ValidatingNotaryServiceTests.kt | 3 +- .../services/vault/NodeVaultServiceTest.kt | 20 +++- .../node/services/vault/VaultQueryTests.kt | 50 +++++--- .../node/services/vault/VaultWithCashTest.kt | 17 ++- .../NetworkRegistrationHelperTest.kt | 8 +- .../attachmentdemo/AttachmentDemoTest.kt | 8 +- .../corda/attachmentdemo/AttachmentDemo.kt | 9 +- .../kotlin/net/corda/attachmentdemo/Main.kt | 8 +- .../corda/bank/BankOfCordaRPCClientTest.kt | 5 +- .../net/corda/bank/BankOfCordaCordform.kt | 6 +- .../src/test/kotlin/net/corda/irs/Main.kt | 8 +- .../corda/irs/api/NodeInterestRatesTest.kt | 9 ++ .../kotlin/net/corda/irs/contract/IRSTests.kt | 11 ++ .../kotlin/net/corda/irs/IRSDemoTest.kt | 6 +- .../net/corda/netmap/simulation/Simulation.kt | 7 +- .../net/corda/notarydemo/BFTNotaryCordform.kt | 8 +- .../corda/notarydemo/CustomNotaryCordform.kt | 10 +- .../kotlin/net/corda/notarydemo/Notarise.kt | 4 +- .../corda/notarydemo/RaftNotaryCordform.kt | 9 +- .../corda/notarydemo/SingleNotaryCordform.kt | 10 +- .../net/corda/vega/SimmValuationTest.kt | 8 +- .../src/test/kotlin/net/corda/vega/Main.kt | 10 +- .../net/corda/traderdemo/TraderDemoTest.kt | 11 +- .../kotlin/net/corda/traderdemo/TraderDemo.kt | 8 +- .../test/kotlin/net/corda/traderdemo/Main.kt | 12 +- .../traderdemo/TransactionGraphSearchTests.kt | 19 +-- .../net/corda/testing/driver/DriverTests.kt | 46 +++---- .../kotlin/net/corda/testing/driver/Driver.kt | 4 +- .../corda/testing/internal/DriverDSLImpl.kt | 13 +- .../kotlin/net/corda/testing/node/MockNode.kt | 6 +- .../kotlin/net/corda/testing/CoreTestUtils.kt | 39 +----- .../kotlin/net/corda/testing/TestConstants.kt | 58 ++------- .../main/kotlin/net/corda/testing/TestDSL.kt | 47 +------- .../testing/TransactionDSLInterpreter.kt | 2 +- .../corda/testing/contracts/VaultFiller.kt | 23 ++-- .../net/corda/explorer/ExplorerSimulation.kt | 8 +- .../net/corda/loadtest/tests/NotaryTest.kt | 5 +- .../net/corda/verifier/VerifierTests.kt | 19 ++- .../corda/webserver/WebserverDriverTests.kt | 4 +- 113 files changed, 712 insertions(+), 617 deletions(-) diff --git a/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt b/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt index d84cbd3a62..546d2dc088 100644 --- a/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt +++ b/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt @@ -6,6 +6,7 @@ import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.Amount import net.corda.core.cordapp.CordappProvider import net.corda.core.crypto.* +import net.corda.core.identity.CordaX500Name import net.corda.core.node.ServiceHub import net.corda.core.transactions.SignedTransaction import net.corda.finance.USD @@ -20,9 +21,12 @@ import java.util.* import kotlin.test.assertEquals class JacksonSupportTest { - companion object { - private val SEED = BigInteger.valueOf(20170922L) + private companion object { + val SEED = BigInteger.valueOf(20170922L)!! val mapper = JacksonSupport.createNonRpcMapper() + val ALICE_PUBKEY = TestIdentity(ALICE_NAME, 70).pubkey + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party + val MINI_CORP = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")).party } @Rule diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index 01966d3a97..0ab028c296 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -28,7 +28,7 @@ import static net.corda.finance.Currencies.DOLLARS; import static net.corda.finance.contracts.GetBalances.getCashBalance; import static net.corda.node.services.Permissions.invokeRpc; import static net.corda.node.services.Permissions.startFlow; -import static net.corda.testing.TestConstants.getALICE; +import static net.corda.testing.TestConstants.getALICE_NAME; public class CordaRPCJavaClientTest extends NodeBasedTest { public CordaRPCJavaClientTest() { @@ -56,7 +56,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest { @Before public void setUp() throws ExecutionException, InterruptedException { - node = startNode(getALICE().getName(), 1, singletonList(rpcUser)); + node = startNode(getALICE_NAME(), 1, singletonList(rpcUser)); client = new CordaRPCClient(requireNonNull(node.getInternals().getConfiguration().getRpcAddress())); } diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt index 14ae217a2b..de89672195 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/BlacklistKotlinClosureTest.kt @@ -7,7 +7,7 @@ import net.corda.core.flows.StartableByRPC import net.corda.core.messaging.startFlow import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.getOrThrow -import net.corda.testing.ALICE +import net.corda.testing.ALICE_NAME import net.corda.testing.driver.driver import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.junit.Test @@ -29,7 +29,7 @@ class BlacklistKotlinClosureTest { @Test fun `closure sent via RPC`() { driver(startNodesInProcess = true) { - val rpc = startNode(providedName = ALICE.name).getOrThrow().rpc + val rpc = startNode(providedName = ALICE_NAME).getOrThrow().rpc val packet = Packet { EVIL } assertThatExceptionOfType(KryoException::class.java) .isThrownBy { rpc.startFlow(::FlowC, packet) } diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index 5c14c3becb..39af03fcd3 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -135,7 +135,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C @Test fun `flow initiator via RPC`() { val externalTrace = Trace.newInstance() - val impersonatedActor = Actor(Actor.Id("Mark Dadada"), AuthServiceId("Test"), owningLegalIdentity = BOB.name) + val impersonatedActor = Actor(Actor.Id("Mark Dadada"), AuthServiceId("Test"), owningLegalIdentity = BOB_NAME) login(rpcUser.username, rpcUser.password, externalTrace, impersonatedActor) val proxy = connection!!.proxy diff --git a/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt index e709715eb9..5bb328d28d 100644 --- a/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt @@ -1,14 +1,10 @@ package net.corda.confidential -import net.corda.core.identity.AbstractParty -import net.corda.core.identity.AnonymousParty -import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate +import net.corda.core.identity.* import net.corda.core.utilities.getOrThrow import net.corda.testing.* import net.corda.testing.node.MockNetwork import org.junit.Before -import net.corda.testing.node.MockNodeParameters import org.junit.Test import kotlin.test.* @@ -24,8 +20,8 @@ class SwapIdentitiesFlowTests { @Test fun `issue key`() { // Set up values we'll need - val aliceNode = mockNet.createPartyNode(ALICE.name) - val bobNode = mockNet.createPartyNode(BOB.name) + val aliceNode = mockNet.createPartyNode(ALICE_NAME) + val bobNode = mockNet.createPartyNode(BOB_NAME) val alice = aliceNode.info.singleIdentity() val bob = bobNode.services.myInfo.singleIdentity() @@ -60,9 +56,9 @@ class SwapIdentitiesFlowTests { @Test fun `verifies identity name`() { // Set up values we'll need - val aliceNode = mockNet.createPartyNode(ALICE.name) - val bobNode = mockNet.createPartyNode(BOB.name) - val charlieNode = mockNet.createPartyNode(CHARLIE.name) + val aliceNode = mockNet.createPartyNode(ALICE_NAME) + val bobNode = mockNet.createPartyNode(BOB_NAME) + val charlieNode = mockNet.createPartyNode(CHARLIE_NAME) val bob: Party = bobNode.services.myInfo.singleIdentity() val notBob = charlieNode.database.transaction { charlieNode.services.keyManagementService.freshKeyAndCert(charlieNode.services.myInfo.chooseIdentityAndCert(), false) @@ -83,8 +79,8 @@ class SwapIdentitiesFlowTests { fun `verifies signature`() { // Set up values we'll need val notaryNode = mockNet.defaultNotaryNode - val aliceNode = mockNet.createPartyNode(ALICE.name) - val bobNode = mockNet.createPartyNode(BOB.name) + val aliceNode = mockNet.createPartyNode(ALICE_NAME) + val bobNode = mockNet.createPartyNode(BOB_NAME) val alice: PartyAndCertificate = aliceNode.info.singleIdentityAndCert() val bob: PartyAndCertificate = bobNode.info.singleIdentityAndCert() val notary: PartyAndCertificate = mockNet.defaultNotaryIdentityAndCert diff --git a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java index 1b69fc5a6c..1811328d67 100644 --- a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java +++ b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java @@ -26,8 +26,8 @@ public class FlowsInJavaTest { @Before public void setUp() throws Exception { - aliceNode = mockNet.createPartyNode(TestConstants.getALICE().getName()); - bobNode = mockNet.createPartyNode(TestConstants.getBOB().getName()); + aliceNode = mockNet.createPartyNode(TestConstants.getALICE_NAME()); + bobNode = mockNet.createPartyNode(TestConstants.getBOB_NAME()); bob = singleIdentity(bobNode.getInfo()); } diff --git a/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt b/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt index 29ad386359..460fda3328 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt @@ -18,6 +18,11 @@ import kotlin.test.assertTrue * Tests for the version 2 dummy contract, to cover ensuring upgrade transactions are built correctly. */ class DummyContractV2Tests { + private companion object { + val ALICE = TestIdentity(ALICE_NAME, 70).party + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party + } + @Rule @JvmField val testSerialization = SerializationEnvironmentRule() diff --git a/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt b/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt index be02cc4599..fdbdebbfd8 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt @@ -4,6 +4,7 @@ import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash.Companion.zeroHash +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize @@ -24,6 +25,16 @@ import kotlin.streams.toList import kotlin.test.* class PartialMerkleTreeTest { + private companion object { + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party + val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) + val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) + val MEGA_CORP get() = megaCorp.party + val MEGA_CORP_PUBKEY get() = megaCorp.pubkey + val MINI_CORP get() = miniCorp.party + val MINI_CORP_PUBKEY get() = miniCorp.pubkey + } + @Rule @JvmField val testSerialization = SerializationEnvironmentRule() diff --git a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt index 890836e35b..97339e7388 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -48,9 +48,8 @@ class AttachmentTests { @Test fun `download and store`() { - val aliceNode = mockNet.createPartyNode(ALICE.name) - val bobNode = mockNet.createPartyNode(BOB.name) - + val aliceNode = mockNet.createPartyNode(ALICE_NAME) + val bobNode = mockNet.createPartyNode(BOB_NAME) val alice = aliceNode.info.singleIdentity() aliceNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java) bobNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java) @@ -81,8 +80,8 @@ class AttachmentTests { @Test fun `missing`() { - val aliceNode = mockNet.createPartyNode(ALICE.name) - val bobNode = mockNet.createPartyNode(BOB.name) + val aliceNode = mockNet.createPartyNode(ALICE_NAME) + val bobNode = mockNet.createPartyNode(BOB_NAME) aliceNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java) bobNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java) // Get node one to fetch a non-existent attachment. @@ -97,12 +96,12 @@ class AttachmentTests { @Test fun maliciousResponse() { // Make a node that doesn't do sanity checking at load time. - val aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE.name), nodeFactory = { args -> + val aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME), nodeFactory = { args -> object : MockNetwork.MockNode(args) { override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false } } }) - val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB.name)) + val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME)) val alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME) aliceNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java) bobNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java) diff --git a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt index d38e0fa841..c2499cff45 100644 --- a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt @@ -4,6 +4,7 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.Command import net.corda.core.contracts.StateAndContract import net.corda.core.contracts.requireThat +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.excludeHostNode import net.corda.core.identity.groupAbstractPartyByWellKnownParty @@ -22,6 +23,10 @@ import kotlin.reflect.KClass import kotlin.test.assertFailsWith class CollectSignaturesFlowTests { + companion object { + private val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) + } + private lateinit var mockNet: MockNetwork private lateinit var aliceNode: StartedNode private lateinit var bobNode: StartedNode @@ -34,9 +39,9 @@ class CollectSignaturesFlowTests { @Before fun setup() { mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts")) - aliceNode = mockNet.createPartyNode(ALICE.name) - bobNode = mockNet.createPartyNode(BOB.name) - charlieNode = mockNet.createPartyNode(CHARLIE.name) + aliceNode = mockNet.createPartyNode(ALICE_NAME) + bobNode = mockNet.createPartyNode(BOB_NAME) + charlieNode = mockNet.createPartyNode(CHARLIE_NAME) alice = aliceNode.info.singleIdentity() bob = bobNode.info.singleIdentity() charlie = charlieNode.info.singleIdentity() @@ -129,7 +134,7 @@ class CollectSignaturesFlowTests { @Test fun `fails when not signed by initiator`() { val onePartyDummyContract = DummyContract.generateInitial(1337, notary, alice.ref(1)) - val miniCorpServices = MockServices(listOf("net.corda.testing.contracts"), rigorousMock(), MINI_CORP.name, MINI_CORP_KEY) + val miniCorpServices = MockServices(listOf("net.corda.testing.contracts"), rigorousMock(), miniCorp) val ptx = miniCorpServices.signInitialTransaction(onePartyDummyContract) val flow = aliceNode.services.startFlow(CollectSignaturesFlow(ptx, emptySet())) mockNet.runNetwork() diff --git a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt index 07ac06b4cd..a7db7c5696 100644 --- a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt @@ -16,6 +16,10 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith class FinalityFlowTests { + companion object { + private val CHARLIE = TestIdentity(CHARLIE_NAME, 90).party + } + private lateinit var mockNet: MockNetwork private lateinit var aliceServices: StartedNodeServices private lateinit var bobServices: StartedNodeServices diff --git a/core/src/test/kotlin/net/corda/core/identity/PartyTest.kt b/core/src/test/kotlin/net/corda/core/identity/PartyTest.kt index 707e5b159b..28d6fb1fa3 100644 --- a/core/src/test/kotlin/net/corda/core/identity/PartyTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/PartyTest.kt @@ -1,7 +1,7 @@ package net.corda.core.identity import net.corda.core.crypto.entropyToKeyPair -import net.corda.testing.ALICE +import net.corda.testing.ALICE_NAME import org.junit.Test import java.math.BigInteger import kotlin.test.assertEquals @@ -13,7 +13,7 @@ class PartyTest { val key = entropyToKeyPair(BigInteger.valueOf(20170207L)).public val differentKey = entropyToKeyPair(BigInteger.valueOf(7201702L)).public val anonymousParty = AnonymousParty(key) - val party = Party(ALICE.name, key) + val party = Party(ALICE_NAME, key) assertEquals(party, anonymousParty) assertEquals(anonymousParty, party) assertNotEquals(AnonymousParty(differentKey), anonymousParty) diff --git a/core/src/test/kotlin/net/corda/core/internal/AbstractAttachmentTest.kt b/core/src/test/kotlin/net/corda/core/internal/AbstractAttachmentTest.kt index 3c3f224091..955ff7d2b2 100644 --- a/core/src/test/kotlin/net/corda/core/internal/AbstractAttachmentTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/AbstractAttachmentTest.kt @@ -1,7 +1,7 @@ package net.corda.core.internal -import net.corda.testing.ALICE -import net.corda.testing.BOB +import net.corda.testing.ALICE_NAME +import net.corda.testing.BOB_NAME import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.After import org.junit.AfterClass @@ -29,8 +29,8 @@ class AbstractAttachmentTest { @BeforeClass @JvmStatic fun beforeClass() { - execute("keytool", "-genkey", "-keystore", "_teststore", "-storepass", "storepass", "-keyalg", "RSA", "-alias", "alice", "-keypass", "alicepass", "-dname", ALICE.toString()) - execute("keytool", "-genkey", "-keystore", "_teststore", "-storepass", "storepass", "-keyalg", "RSA", "-alias", "bob", "-keypass", "bobpass", "-dname", BOB.toString()) + execute("keytool", "-genkey", "-keystore", "_teststore", "-storepass", "storepass", "-keyalg", "RSA", "-alias", "alice", "-keypass", "alicepass", "-dname", ALICE_NAME.toString()) + execute("keytool", "-genkey", "-keystore", "_teststore", "-storepass", "storepass", "-keyalg", "RSA", "-alias", "bob", "-keypass", "bobpass", "-dname", BOB_NAME.toString()) (dir / "_signable1").writeLines(listOf("signable1")) (dir / "_signable2").writeLines(listOf("signable2")) (dir / "_signable3").writeLines(listOf("signable3")) @@ -76,10 +76,10 @@ class AbstractAttachmentTest { fun `one signer`() { execute("jar", "cvf", "attachment.jar", "_signable1", "_signable2") execute("jarsigner", "-keystore", "_teststore", "-storepass", "storepass", "-keypass", "alicepass", "attachment.jar", "alice") - assertEquals(listOf(ALICE.name), load("attachment.jar").signers.map { it.name }) // We only reused ALICE's distinguished name, so the keys will be different. + assertEquals(listOf(ALICE_NAME), load("attachment.jar").signers.map { it.name }) // We only reused ALICE's distinguished name, so the keys will be different. (dir / "my-dir").createDirectory() execute("jar", "uvf", "attachment.jar", "my-dir") - assertEquals(listOf(ALICE.name), load("attachment.jar").signers.map { it.name }) // Unsigned directory is irrelevant. + assertEquals(listOf(ALICE_NAME), load("attachment.jar").signers.map { it.name }) // Unsigned directory is irrelevant. } @Test @@ -87,17 +87,17 @@ class AbstractAttachmentTest { execute("jar", "cvf", "attachment.jar", "_signable1", "_signable2") execute("jarsigner", "-keystore", "_teststore", "-storepass", "storepass", "-keypass", "alicepass", "attachment.jar", "alice") execute("jarsigner", "-keystore", "_teststore", "-storepass", "storepass", "-keypass", "bobpass", "attachment.jar", "bob") - assertEquals(listOf(ALICE.name, BOB.name), load("attachment.jar").signers.map { it.name }) + assertEquals(listOf(ALICE_NAME, BOB_NAME), load("attachment.jar").signers.map { it.name }) } @Test fun `a party must sign all the files in the attachment to be a signer`() { execute("jar", "cvf", "attachment.jar", "_signable1") execute("jarsigner", "-keystore", "_teststore", "-storepass", "storepass", "-keypass", "alicepass", "attachment.jar", "alice") - assertEquals(listOf(ALICE.name), load("attachment.jar").signers.map { it.name }) + assertEquals(listOf(ALICE_NAME), load("attachment.jar").signers.map { it.name }) execute("jar", "uvf", "attachment.jar", "_signable2") execute("jarsigner", "-keystore", "_teststore", "-storepass", "storepass", "-keypass", "bobpass", "attachment.jar", "bob") - assertEquals(listOf(BOB.name), load("attachment.jar").signers.map { it.name }) // ALICE hasn't signed the new file. + assertEquals(listOf(BOB_NAME), load("attachment.jar").signers.map { it.name }) // ALICE hasn't signed the new file. execute("jar", "uvf", "attachment.jar", "_signable3") assertEquals(emptyList(), load("attachment.jar").signers) // Neither party has signed the new file. } @@ -107,7 +107,7 @@ class AbstractAttachmentTest { (dir / "volatile").writeLines(listOf("volatile")) execute("jar", "cvf", "attachment.jar", "volatile") execute("jarsigner", "-keystore", "_teststore", "-storepass", "storepass", "-keypass", "alicepass", "attachment.jar", "alice") - assertEquals(listOf(ALICE.name), load("attachment.jar").signers.map { it.name }) + assertEquals(listOf(ALICE_NAME), load("attachment.jar").signers.map { it.name }) (dir / "volatile").writeLines(listOf("garbage")) execute("jar", "uvf", "attachment.jar", "volatile", "_signable1") // ALICE's signature on volatile is now bad. execute("jarsigner", "-keystore", "_teststore", "-storepass", "storepass", "-keypass", "bobpass", "attachment.jar", "bob") diff --git a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt index b56c5add9a..6f60eedad6 100644 --- a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt @@ -3,17 +3,15 @@ package net.corda.core.internal import co.paralleluniverse.fibers.Suspendable import net.corda.core.crypto.SecureHash import net.corda.core.flows.* +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.sequence import net.corda.node.internal.StartedNode -import net.corda.testing.MEGA_CORP -import net.corda.testing.MINI_CORP +import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork -import net.corda.testing.singleIdentity -import net.corda.testing.startFlow import org.junit.After import org.junit.Before import org.junit.Test @@ -40,8 +38,8 @@ class ResolveTransactionsFlowTest { fun setup() { mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts")) notaryNode = mockNet.defaultNotaryNode - megaCorpNode = mockNet.createPartyNode(MEGA_CORP.name) - miniCorpNode = mockNet.createPartyNode(MINI_CORP.name) + megaCorpNode = mockNet.createPartyNode(CordaX500Name("MegaCorp", "London", "GB")) + miniCorpNode = mockNet.createPartyNode(CordaX500Name("MiniCorp", "London", "GB")) megaCorpNode.registerInitiatedFlow(TestResponseFlow::class.java) miniCorpNode.registerInitiatedFlow(TestResponseFlow::class.java) notary = mockNet.defaultNotaryIdentity diff --git a/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt b/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt index fc5872ce18..dd4c993837 100644 --- a/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt +++ b/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt @@ -5,14 +5,18 @@ import net.corda.core.crypto.SecureHash import net.corda.core.identity.AbstractParty import net.corda.core.node.services.Vault import net.corda.core.transactions.LedgerTransaction -import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.DUMMY_NOTARY_NAME +import net.corda.testing.TestIdentity import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith class VaultUpdateTests { - val DUMMY_PROGRAM_ID = "net.corda.core.node.VaultUpdateTests.DummyContract" + private companion object { + val DUMMY_PROGRAM_ID = "net.corda.core.node.VaultUpdateTests.DummyContract" + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party + } object DummyContract : Contract { diff --git a/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt b/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt index bfdc34d30a..ca795cf4f0 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt @@ -3,6 +3,7 @@ package net.corda.core.serialization import net.corda.core.contracts.* import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.seconds @@ -19,6 +20,16 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith class TransactionSerializationTests { + private companion object { + val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20) + val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) + val MINI_CORP = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")).party + val DUMMY_NOTARY get() = dummyNotary.party + val DUMMY_NOTARY_KEY get() = dummyNotary.key + val MEGA_CORP get() = megaCorp.party + val MEGA_CORP_KEY get() = megaCorp.key + } + @Rule @JvmField val testSerialization = SerializationEnvironmentRule() diff --git a/core/src/test/kotlin/net/corda/core/transactions/CompatibleTransactionTests.kt b/core/src/test/kotlin/net/corda/core/transactions/CompatibleTransactionTests.kt index 5461d280a1..e83b05e8f3 100644 --- a/core/src/test/kotlin/net/corda/core/transactions/CompatibleTransactionTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/CompatibleTransactionTests.kt @@ -18,6 +18,8 @@ class CompatibleTransactionTests { private companion object { val DUMMY_KEY_1 = generateKeyPair() val DUMMY_KEY_2 = generateKeyPair() + val BOB = TestIdentity(BOB_NAME, 80).party + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party } @Rule diff --git a/core/src/test/kotlin/net/corda/core/transactions/LedgerTransactionQueryTests.kt b/core/src/test/kotlin/net/corda/core/transactions/LedgerTransactionQueryTests.kt index d9a8e9bc10..b343a5ed2a 100644 --- a/core/src/test/kotlin/net/corda/core/transactions/LedgerTransactionQueryTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/LedgerTransactionQueryTests.kt @@ -5,6 +5,7 @@ import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.* import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.node.services.api.IdentityServiceInternal import net.corda.testing.* @@ -19,13 +20,17 @@ import kotlin.test.assertFailsWith import kotlin.test.assertTrue class LedgerTransactionQueryTests { + companion object { + private val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party + } + @Rule @JvmField val testSerialization = SerializationEnvironmentRule() private val keyPair = generateKeyPair() private val services = MockServices(rigorousMock().also { doReturn(null).whenever(it).partyFromKey(keyPair.public) - }, MEGA_CORP.name, keyPair) + }, CordaX500Name("MegaCorp", "London", "GB"), keyPair) private val identity: Party = services.myInfo.singleIdentity() @Before diff --git a/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt b/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt index b4c824026d..658edad723 100644 --- a/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt @@ -6,6 +6,7 @@ import net.corda.core.contracts.Contract import net.corda.core.contracts.ContractState import net.corda.core.contracts.requireThat import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash @@ -20,6 +21,14 @@ import java.time.temporal.ChronoUnit val TEST_TIMELOCK_ID = "net.corda.core.transactions.TransactionEncumbranceTests\$DummyTimeLock" class TransactionEncumbranceTests { + private companion object { + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party + val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) + val MINI_CORP = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")).party + val MEGA_CORP get() = megaCorp.party + val MEGA_CORP_PUBKEY get() = megaCorp.pubkey + } + @Rule @JvmField val testSerialization = SerializationEnvironmentRule() diff --git a/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt b/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt index fadd968dd9..3696429463 100644 --- a/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt @@ -20,6 +20,11 @@ class TransactionTests { val DUMMY_KEY_1 = generateKeyPair() val DUMMY_KEY_2 = generateKeyPair() val DUMMY_CASH_ISSUER_KEY = entropyToKeyPair(BigInteger.valueOf(10)) + val ALICE = TestIdentity(ALICE_NAME, 70).party + val BOB = TestIdentity(BOB_NAME, 80).party + val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20) + val DUMMY_NOTARY get() = dummyNotary.party + val DUMMY_NOTARY_KEY get() = dummyNotary.key } @Rule diff --git a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt index d8a1b64776..d398072edc 100644 --- a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt +++ b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt @@ -38,8 +38,8 @@ class IntegrationTestingTutorial { invokeRpc(CordaRPCOps::networkMapFeed) )) val (alice, bob) = listOf( - startNode(providedName = ALICE.name, rpcUsers = listOf(aliceUser)), - startNode(providedName = BOB.name, rpcUsers = listOf(bobUser)) + startNode(providedName = ALICE_NAME, rpcUsers = listOf(aliceUser)), + startNode(providedName = BOB_NAME, rpcUsers = listOf(bobUser)) ).transpose().getOrThrow() // END 1 diff --git a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java index ec928e9997..9a6f6d6e5f 100644 --- a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java +++ b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java @@ -33,7 +33,7 @@ import java.util.List; import java.util.Set; import static net.corda.core.contracts.ContractsDSL.requireThat; -import static net.corda.testing.TestConstants.getALICE_KEY; +import static net.corda.core.crypto.Crypto.generateKeyPair; @SuppressWarnings("unused") public class FlowCookbookJava { @@ -107,9 +107,7 @@ public class FlowCookbookJava { @Override public Void call() throws FlowException { // We'll be using a dummy public key for demonstration purposes. - // These are built in to Corda, and are generally used for writing - // tests. - PublicKey dummyPubKey = getALICE_KEY().getPublic(); + PublicKey dummyPubKey = generateKeyPair().getPublic(); /*--------------------------- * IDENTIFYING OTHER NODES * diff --git a/docs/source/example-code/src/main/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java b/docs/source/example-code/src/main/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java index 07f96740a0..81805041d8 100644 --- a/docs/source/example-code/src/main/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java +++ b/docs/source/example-code/src/main/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java @@ -2,47 +2,56 @@ package net.corda.docs.java.tutorial.testdsl; import kotlin.Unit; import net.corda.core.contracts.PartyAndReference; -import net.corda.core.utilities.OpaqueBytes; +import net.corda.core.identity.CordaX500Name; +import net.corda.core.identity.Party; import net.corda.finance.contracts.ICommercialPaperState; import net.corda.finance.contracts.JavaCommercialPaper; import net.corda.finance.contracts.asset.Cash; import net.corda.node.services.api.IdentityServiceInternal; import net.corda.testing.SerializationEnvironmentRule; import net.corda.testing.node.MockServices; +import net.corda.testing.TestIdentity; import org.junit.Rule; import org.junit.Test; +import java.security.PublicKey; import java.time.temporal.ChronoUnit; +import static net.corda.core.crypto.Crypto.generateKeyPair; import static net.corda.finance.Currencies.DOLLARS; import static net.corda.finance.Currencies.issuedBy; import static net.corda.finance.contracts.JavaCommercialPaper.JCP_PROGRAM_ID; -import static net.corda.testing.CoreTestUtils.*; +import static net.corda.testing.CoreTestUtils.rigorousMock; import static net.corda.testing.NodeTestUtils.ledger; import static net.corda.testing.NodeTestUtils.transaction; import static net.corda.testing.TestConstants.*; import static org.mockito.Mockito.doReturn; public class CommercialPaperTest { + private static final TestIdentity ALICE = new TestIdentity(getALICE_NAME(), 70L); + private static final PublicKey BIG_CORP_PUBKEY = generateKeyPair().getPublic(); + private static final TestIdentity BOB = new TestIdentity(getBOB_NAME(), 80L); + private static final TestIdentity MEGA_CORP = new TestIdentity(new CordaX500Name("MegaCorp", "London", "GB")); + private static final Party DUMMY_NOTARY = new TestIdentity(getDUMMY_NOTARY_NAME(), 20L).getParty(); @Rule public final SerializationEnvironmentRule testSerialization = new SerializationEnvironmentRule(); - private final OpaqueBytes defaultRef = new OpaqueBytes(new byte[]{123}); + private final byte[] defaultRef = {123}; private final MockServices ledgerServices; { IdentityServiceInternal identityService = rigorousMock(IdentityServiceInternal.class); - doReturn(getMEGA_CORP()).when(identityService).partyFromKey(getMEGA_CORP_PUBKEY()); - doReturn(null).when(identityService).partyFromKey(getBIG_CORP_PUBKEY()); - doReturn(null).when(identityService).partyFromKey(getALICE_PUBKEY()); - ledgerServices = new MockServices(identityService, getMEGA_CORP().getName()); + doReturn(MEGA_CORP.getParty()).when(identityService).partyFromKey(MEGA_CORP.getPubkey()); + doReturn(null).when(identityService).partyFromKey(BIG_CORP_PUBKEY); + doReturn(null).when(identityService).partyFromKey(ALICE.getPubkey()); + ledgerServices = new MockServices(identityService, MEGA_CORP.getName()); } // DOCSTART 1 private ICommercialPaperState getPaper() { return new JavaCommercialPaper.State( - getMEGA_CORP().ref(defaultRef), - getMEGA_CORP(), - issuedBy(DOLLARS(1000), getMEGA_CORP().ref(defaultRef)), + MEGA_CORP.ref(defaultRef), + MEGA_CORP.getParty(), + issuedBy(DOLLARS(1000), MEGA_CORP.ref(defaultRef)), getTEST_TX_TIME().plus(7, ChronoUnit.DAYS) ); } @@ -52,7 +61,7 @@ public class CommercialPaperTest { @Test public void simpleCP() { ICommercialPaperState inState = getPaper(); - ledger(ledgerServices, getDUMMY_NOTARY(), l -> { + ledger(ledgerServices, DUMMY_NOTARY, l -> { l.transaction(tx -> { tx.attachments(JCP_PROGRAM_ID); tx.input(JCP_PROGRAM_ID, inState); @@ -67,10 +76,10 @@ public class CommercialPaperTest { @Test public void simpleCPMove() { ICommercialPaperState inState = getPaper(); - ledger(ledgerServices, getDUMMY_NOTARY(), l -> { + ledger(ledgerServices, DUMMY_NOTARY, l -> { l.transaction(tx -> { tx.input(JCP_PROGRAM_ID, inState); - tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move()); + tx.command(MEGA_CORP.getPubkey(), new JavaCommercialPaper.Commands.Move()); tx.attachments(JCP_PROGRAM_ID); return tx.verifies(); }); @@ -83,10 +92,10 @@ public class CommercialPaperTest { @Test public void simpleCPMoveFails() { ICommercialPaperState inState = getPaper(); - ledger(ledgerServices, getDUMMY_NOTARY(), l -> { + ledger(ledgerServices, DUMMY_NOTARY, l -> { l.transaction(tx -> { tx.input(JCP_PROGRAM_ID, inState); - tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move()); + tx.command(MEGA_CORP.getPubkey(), new JavaCommercialPaper.Commands.Move()); tx.attachments(JCP_PROGRAM_ID); return tx.failsWith("the state is propagated"); }); @@ -99,13 +108,13 @@ public class CommercialPaperTest { @Test public void simpleCPMoveSuccess() { ICommercialPaperState inState = getPaper(); - ledger(ledgerServices, getDUMMY_NOTARY(), l -> { + ledger(ledgerServices, DUMMY_NOTARY, l -> { l.transaction(tx -> { tx.input(JCP_PROGRAM_ID, inState); - tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move()); + tx.command(MEGA_CORP.getPubkey(), new JavaCommercialPaper.Commands.Move()); tx.attachments(JCP_PROGRAM_ID); tx.failsWith("the state is propagated"); - tx.output(JCP_PROGRAM_ID, "alice's paper", inState.withOwner(getALICE())); + tx.output(JCP_PROGRAM_ID, "alice's paper", inState.withOwner(ALICE.getParty())); return tx.verifies(); }); return Unit.INSTANCE; @@ -116,16 +125,16 @@ public class CommercialPaperTest { // DOCSTART 6 @Test public void simpleIssuanceWithTweak() { - ledger(ledgerServices, getDUMMY_NOTARY(), l -> { + ledger(ledgerServices, DUMMY_NOTARY, l -> { l.transaction(tx -> { tx.output(JCP_PROGRAM_ID, "paper", getPaper()); // Some CP is issued onto the ledger by MegaCorp. tx.attachments(JCP_PROGRAM_ID); tx.tweak(tw -> { - tw.command(getBIG_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Issue()); + tw.command(BIG_CORP_PUBKEY, new JavaCommercialPaper.Commands.Issue()); tw.timeWindow(getTEST_TX_TIME()); return tw.failsWith("output states are issued by a command signer"); }); - tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Issue()); + tx.command(MEGA_CORP.getPubkey(), new JavaCommercialPaper.Commands.Issue()); tx.timeWindow(getTEST_TX_TIME()); return tx.verifies(); }); @@ -137,15 +146,15 @@ public class CommercialPaperTest { // DOCSTART 7 @Test public void simpleIssuanceWithTweakTopLevelTx() { - transaction(ledgerServices, getDUMMY_NOTARY(), tx -> { + transaction(ledgerServices, DUMMY_NOTARY, tx -> { tx.output(JCP_PROGRAM_ID, "paper", getPaper()); // Some CP is issued onto the ledger by MegaCorp. tx.attachments(JCP_PROGRAM_ID); tx.tweak(tw -> { - tw.command(getBIG_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Issue()); + tw.command(BIG_CORP_PUBKEY, new JavaCommercialPaper.Commands.Issue()); tw.timeWindow(getTEST_TX_TIME()); return tw.failsWith("output states are issued by a command signer"); }); - tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Issue()); + tx.command(MEGA_CORP.getPubkey(), new JavaCommercialPaper.Commands.Issue()); tx.timeWindow(getTEST_TX_TIME()); return tx.verifies(); }); @@ -155,11 +164,11 @@ public class CommercialPaperTest { // DOCSTART 8 @Test public void chainCommercialPaper() { - PartyAndReference issuer = getMEGA_CORP().ref(defaultRef); - ledger(ledgerServices, getDUMMY_NOTARY(), l -> { + PartyAndReference issuer = MEGA_CORP.ref(defaultRef); + ledger(ledgerServices, DUMMY_NOTARY, l -> { l.unverifiedTransaction(tx -> { tx.output(Cash.PROGRAM_ID, "alice's $900", - new Cash.State(issuedBy(DOLLARS(900), issuer), getALICE())); + new Cash.State(issuedBy(DOLLARS(900), issuer), ALICE.getParty())); tx.attachments(Cash.PROGRAM_ID); return Unit.INSTANCE; }); @@ -167,7 +176,7 @@ public class CommercialPaperTest { // Some CP is issued onto the ledger by MegaCorp. l.transaction("Issuance", tx -> { tx.output(JCP_PROGRAM_ID, "paper", getPaper()); - tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Issue()); + tx.command(MEGA_CORP.getPubkey(), new JavaCommercialPaper.Commands.Issue()); tx.attachments(JCP_PROGRAM_ID); tx.timeWindow(getTEST_TX_TIME()); return tx.verifies(); @@ -176,11 +185,11 @@ public class CommercialPaperTest { l.transaction("Trade", tx -> { tx.input("paper"); tx.input("alice's $900"); - tx.output(Cash.PROGRAM_ID, "borrowed $900", new Cash.State(issuedBy(DOLLARS(900), issuer), getMEGA_CORP())); + tx.output(Cash.PROGRAM_ID, "borrowed $900", new Cash.State(issuedBy(DOLLARS(900), issuer), MEGA_CORP.getParty())); JavaCommercialPaper.State inputPaper = l.retrieveOutput(JavaCommercialPaper.State.class, "paper"); - tx.output(JCP_PROGRAM_ID, "alice's paper", inputPaper.withOwner(getALICE())); - tx.command(getALICE_PUBKEY(), new Cash.Commands.Move()); - tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move()); + tx.output(JCP_PROGRAM_ID, "alice's paper", inputPaper.withOwner(ALICE.getParty())); + tx.command(ALICE.getPubkey(), new Cash.Commands.Move()); + tx.command(MEGA_CORP.getPubkey(), new JavaCommercialPaper.Commands.Move()); return tx.verifies(); }); return Unit.INSTANCE; @@ -191,11 +200,11 @@ public class CommercialPaperTest { // DOCSTART 9 @Test public void chainCommercialPaperDoubleSpend() { - PartyAndReference issuer = getMEGA_CORP().ref(defaultRef); - ledger(ledgerServices, getDUMMY_NOTARY(), l -> { + PartyAndReference issuer = MEGA_CORP.ref(defaultRef); + ledger(ledgerServices, DUMMY_NOTARY, l -> { l.unverifiedTransaction(tx -> { tx.output(Cash.PROGRAM_ID, "alice's $900", - new Cash.State(issuedBy(DOLLARS(900), issuer), getALICE())); + new Cash.State(issuedBy(DOLLARS(900), issuer), ALICE.getParty())); tx.attachments(Cash.PROGRAM_ID); return Unit.INSTANCE; }); @@ -203,7 +212,7 @@ public class CommercialPaperTest { // Some CP is issued onto the ledger by MegaCorp. l.transaction("Issuance", tx -> { tx.output(Cash.PROGRAM_ID, "paper", getPaper()); - tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Issue()); + tx.command(MEGA_CORP.getPubkey(), new JavaCommercialPaper.Commands.Issue()); tx.attachments(JCP_PROGRAM_ID); tx.timeWindow(getTEST_TX_TIME()); return tx.verifies(); @@ -212,11 +221,11 @@ public class CommercialPaperTest { l.transaction("Trade", tx -> { tx.input("paper"); tx.input("alice's $900"); - tx.output(Cash.PROGRAM_ID, "borrowed $900", new Cash.State(issuedBy(DOLLARS(900), issuer), getMEGA_CORP())); + tx.output(Cash.PROGRAM_ID, "borrowed $900", new Cash.State(issuedBy(DOLLARS(900), issuer), MEGA_CORP.getParty())); JavaCommercialPaper.State inputPaper = l.retrieveOutput(JavaCommercialPaper.State.class, "paper"); - tx.output(JCP_PROGRAM_ID, "alice's paper", inputPaper.withOwner(getALICE())); - tx.command(getALICE_PUBKEY(), new Cash.Commands.Move()); - tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move()); + tx.output(JCP_PROGRAM_ID, "alice's paper", inputPaper.withOwner(ALICE.getParty())); + tx.command(ALICE.getPubkey(), new Cash.Commands.Move()); + tx.command(MEGA_CORP.getPubkey(), new JavaCommercialPaper.Commands.Move()); return tx.verifies(); }); @@ -224,8 +233,8 @@ public class CommercialPaperTest { tx.input("paper"); JavaCommercialPaper.State inputPaper = l.retrieveOutput(JavaCommercialPaper.State.class, "paper"); // We moved a paper to other pubkey. - tx.output(JCP_PROGRAM_ID, "bob's paper", inputPaper.withOwner(getBOB())); - tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move()); + tx.output(JCP_PROGRAM_ID, "bob's paper", inputPaper.withOwner(BOB.getParty())); + tx.command(MEGA_CORP.getPubkey(), new JavaCommercialPaper.Commands.Move()); return tx.verifies(); }); l.fails(); @@ -237,11 +246,11 @@ public class CommercialPaperTest { // DOCSTART 10 @Test public void chainCommercialPaperTweak() { - PartyAndReference issuer = getMEGA_CORP().ref(defaultRef); - ledger(ledgerServices, getDUMMY_NOTARY(), l -> { + PartyAndReference issuer = MEGA_CORP.ref(defaultRef); + ledger(ledgerServices, DUMMY_NOTARY, l -> { l.unverifiedTransaction(tx -> { tx.output(Cash.PROGRAM_ID, "alice's $900", - new Cash.State(issuedBy(DOLLARS(900), issuer), getALICE())); + new Cash.State(issuedBy(DOLLARS(900), issuer), ALICE.getParty())); tx.attachments(Cash.PROGRAM_ID); return Unit.INSTANCE; }); @@ -249,7 +258,7 @@ public class CommercialPaperTest { // Some CP is issued onto the ledger by MegaCorp. l.transaction("Issuance", tx -> { tx.output(Cash.PROGRAM_ID, "paper", getPaper()); - tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Issue()); + tx.command(MEGA_CORP.getPubkey(), new JavaCommercialPaper.Commands.Issue()); tx.attachments(JCP_PROGRAM_ID); tx.timeWindow(getTEST_TX_TIME()); return tx.verifies(); @@ -258,11 +267,11 @@ public class CommercialPaperTest { l.transaction("Trade", tx -> { tx.input("paper"); tx.input("alice's $900"); - tx.output(Cash.PROGRAM_ID, "borrowed $900", new Cash.State(issuedBy(DOLLARS(900), issuer), getMEGA_CORP())); + tx.output(Cash.PROGRAM_ID, "borrowed $900", new Cash.State(issuedBy(DOLLARS(900), issuer), MEGA_CORP.getParty())); JavaCommercialPaper.State inputPaper = l.retrieveOutput(JavaCommercialPaper.State.class, "paper"); - tx.output(JCP_PROGRAM_ID, "alice's paper", inputPaper.withOwner(getALICE())); - tx.command(getALICE_PUBKEY(), new Cash.Commands.Move()); - tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move()); + tx.output(JCP_PROGRAM_ID, "alice's paper", inputPaper.withOwner(ALICE.getParty())); + tx.command(ALICE.getPubkey(), new Cash.Commands.Move()); + tx.command(MEGA_CORP.getPubkey(), new JavaCommercialPaper.Commands.Move()); return tx.verifies(); }); @@ -271,8 +280,8 @@ public class CommercialPaperTest { tx.input("paper"); JavaCommercialPaper.State inputPaper = l.retrieveOutput(JavaCommercialPaper.State.class, "paper"); // We moved a paper to another pubkey. - tx.output(JCP_PROGRAM_ID, "bob's paper", inputPaper.withOwner(getBOB())); - tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move()); + tx.output(JCP_PROGRAM_ID, "bob's paper", inputPaper.withOwner(BOB.getParty())); + tx.command(MEGA_CORP.getPubkey(), new JavaCommercialPaper.Commands.Move()); return tx.verifies(); }); lw.fails(); diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt index 954a67c239..685a10c4ad 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt @@ -18,7 +18,7 @@ import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User -import net.corda.testing.ALICE +import net.corda.testing.ALICE_NAME import net.corda.testing.driver.driver import org.graphstream.graph.Edge import org.graphstream.graph.Node @@ -49,7 +49,7 @@ fun main(args: Array) { invokeRpc(CordaRPCOps::nodeInfo) )) driver(driverDirectory = baseDirectory, extraCordappPackagesToScan = listOf("net.corda.finance"), waitForAllNodesToFinish = true) { - val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user)).get() + val node = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).get() // END 1 // START 2 diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt index 64b3c0845a..f5380582de 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt @@ -6,6 +6,7 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash import net.corda.core.crypto.TransactionSignature +import net.corda.core.crypto.generateKeyPair import net.corda.core.flows.* import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -23,7 +24,6 @@ import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.seconds import net.corda.core.utilities.unwrap import net.corda.finance.contracts.asset.Cash -import net.corda.testing.ALICE_PUBKEY import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState import java.security.PublicKey @@ -87,9 +87,7 @@ class InitiatorFlow(val arg1: Boolean, val arg2: Int, private val counterparty: @Suspendable override fun call() { // We'll be using a dummy public key for demonstration purposes. - // These are built in to Corda, and are generally used for writing - // tests. - val dummyPubKey: PublicKey = ALICE_PUBKEY + val dummyPubKey: PublicKey = generateKeyPair().public /**-------------------------- * IDENTIFYING OTHER NODES * diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/tearoffs/TutorialTearOffs.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/tearoffs/TutorialTearOffs.kt index c560c4f3f4..705f1f0bac 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/tearoffs/TutorialTearOffs.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/tearoffs/TutorialTearOffs.kt @@ -4,16 +4,16 @@ import net.corda.core.contracts.Command import net.corda.core.contracts.StateRef import net.corda.core.contracts.TimeWindow import net.corda.core.crypto.MerkleTreeException +import net.corda.core.identity.AbstractParty import net.corda.core.transactions.FilteredTransaction import net.corda.core.transactions.FilteredTransactionVerificationException import net.corda.core.transactions.SignedTransaction import net.corda.finance.contracts.Fix -import net.corda.testing.ALICE import java.util.function.Predicate fun main(args: Array) { // Typealias to make the example coherent. - val oracle = ALICE + val oracle = Any() as AbstractParty val stx = Any() as SignedTransaction // DOCSTART 1 diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/testdsl/TutorialTestDSL.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/testdsl/TutorialTestDSL.kt index eceadf55a7..000f173207 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/testdsl/TutorialTestDSL.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/testdsl/TutorialTestDSL.kt @@ -2,6 +2,8 @@ package net.corda.docs.tutorial.testdsl import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever +import net.corda.core.crypto.generateKeyPair +import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.days import net.corda.finance.DOLLARS import net.corda.finance.`issued by` @@ -17,6 +19,18 @@ import org.junit.Rule import org.junit.Test class CommercialPaperTest { + private companion object { + val alice = TestIdentity(ALICE_NAME, 70) + val BIG_CORP_PUBKEY = generateKeyPair().public + val BOB = TestIdentity(BOB_NAME, 80).party + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party + val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) + val ALICE get() = alice.party + val ALICE_PUBKEY get() = alice.pubkey + val MEGA_CORP get() = megaCorp.party + val MEGA_CORP_PUBKEY get() = megaCorp.pubkey + } + @Rule @JvmField val testSerialization = SerializationEnvironmentRule() diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt index e77ca43f4c..2177da0ddb 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt @@ -2,9 +2,7 @@ package net.corda.finance.contracts.universal import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever -import net.corda.core.crypto.entropyToKeyPair import net.corda.core.identity.CordaX500Name -import net.corda.core.identity.Party import net.corda.finance.contracts.BusinessCalendar import net.corda.finance.contracts.FixOf import net.corda.finance.contracts.Frequency @@ -12,20 +10,19 @@ import net.corda.finance.contracts.Tenor import net.corda.node.services.api.IdentityServiceInternal import net.corda.testing.* import net.corda.testing.node.MockServices -import net.corda.testing.node.makeTestIdentityService import org.junit.Ignore import org.junit.Rule import org.junit.Test -import java.math.BigInteger import java.time.Instant import java.time.LocalDate +internal val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party fun transaction(script: TransactionDSL.() -> EnforceVerifyOrFail) = run { MockServices(listOf("net.corda.finance.contracts.universal"), rigorousMock().also { listOf(acmeCorp, highStreetBank, momAndPop).forEach { party -> doReturn(null).whenever(it).partyFromKey(party.owningKey) } - }, MEGA_CORP.name).transaction(DUMMY_NOTARY, script) + }, CordaX500Name("MegaCorp", "London", "GB")).transaction(DUMMY_NOTARY, script) } class Cap { diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt index 7436161cd4..c9535fd109 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt @@ -2,7 +2,6 @@ package net.corda.finance.contracts.universal import net.corda.finance.contracts.FixOf import net.corda.finance.contracts.Tenor -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.SerializationEnvironmentRule import org.junit.Ignore import org.junit.Rule diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ContractDefinition.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ContractDefinition.kt index 75924ff0e0..112093b4b4 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ContractDefinition.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ContractDefinition.kt @@ -1,20 +1,17 @@ package net.corda.finance.contracts.universal import net.corda.core.crypto.generateKeyPair -import net.corda.core.identity.Party -import net.corda.testing.ALICE -import net.corda.testing.MEGA_CORP -import net.corda.testing.MINI_CORP +import net.corda.core.identity.CordaX500Name +import net.corda.testing.TestIdentity import org.junit.Test import java.util.* import kotlin.test.assertEquals import kotlin.test.assertTrue // Test parties -val acmeCorp = Party(ALICE.name, generateKeyPair().public) -val highStreetBank = Party(MEGA_CORP.name, generateKeyPair().public) -val momAndPop = Party(MINI_CORP.name, generateKeyPair().public) - +val acmeCorp = TestIdentity(CordaX500Name("Alice Corp", "Madrid", "ES")).party +val highStreetBank = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party +val momAndPop = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")).party val acmeCorporationHasDefaulted = TerminalEvent(acmeCorp, generateKeyPair().public) diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt index 1d05fe061a..380dd564d2 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt @@ -1,6 +1,5 @@ package net.corda.finance.contracts.universal -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.SerializationEnvironmentRule import org.junit.Ignore import org.junit.Rule diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt index fc91215046..4dadb338c2 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt @@ -1,6 +1,5 @@ package net.corda.finance.contracts.universal -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.SerializationEnvironmentRule import org.junit.Ignore import org.junit.Rule diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt index e2f0d4f942..ae88022c78 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt @@ -3,7 +3,6 @@ package net.corda.finance.contracts.universal import net.corda.finance.contracts.FixOf import net.corda.finance.contracts.Frequency import net.corda.finance.contracts.Tenor -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.SerializationEnvironmentRule import org.junit.Ignore import org.junit.Rule diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt index 34ba966a0c..185cebd57a 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt @@ -1,7 +1,6 @@ package net.corda.finance.contracts.universal import net.corda.finance.contracts.Frequency -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.SerializationEnvironmentRule import org.junit.Rule import org.junit.Test diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt index f80c3d5fd3..aa82468c30 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt @@ -2,7 +2,6 @@ package net.corda.finance.contracts.universal import net.corda.finance.contracts.Frequency import net.corda.finance.contracts.Tenor -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.SerializationEnvironmentRule import org.junit.Ignore import org.junit.Rule diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt index 2554b0c58b..10aee2e760 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt @@ -1,6 +1,5 @@ package net.corda.finance.contracts.universal -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.SerializationEnvironmentRule import org.junit.Rule import org.junit.Test diff --git a/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java b/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java index 5ab3569b25..4553eb3b95 100644 --- a/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java +++ b/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java @@ -2,72 +2,76 @@ package net.corda.finance.contracts.asset; import net.corda.core.contracts.PartyAndReference; import net.corda.core.identity.AnonymousParty; -import net.corda.core.utilities.OpaqueBytes; +import net.corda.core.identity.CordaX500Name; +import net.corda.core.identity.Party; import net.corda.node.services.api.IdentityServiceInternal; import net.corda.testing.DummyCommandData; import net.corda.testing.SerializationEnvironmentRule; +import net.corda.testing.TestIdentity; import net.corda.testing.node.MockServices; import org.junit.Rule; import org.junit.Test; import static net.corda.finance.Currencies.DOLLARS; import static net.corda.finance.Currencies.issuedBy; -import static net.corda.testing.CoreTestUtils.*; +import static net.corda.testing.CoreTestUtils.rigorousMock; import static net.corda.testing.NodeTestUtils.transaction; -import static net.corda.testing.TestConstants.getDUMMY_NOTARY; +import static net.corda.testing.TestConstants.getDUMMY_NOTARY_NAME; import static org.mockito.Mockito.doReturn; /** * This is an incomplete Java replica of CashTests.kt to show how to use the Java test DSL */ public class CashTestsJava { - private final OpaqueBytes defaultRef = new OpaqueBytes(new byte[]{1}); - private final PartyAndReference defaultIssuer = getMEGA_CORP().ref(defaultRef); - private final Cash.State inState = new Cash.State(issuedBy(DOLLARS(1000), defaultIssuer), new AnonymousParty(getMEGA_CORP_PUBKEY())); - private final Cash.State outState = new Cash.State(inState.getAmount(), new AnonymousParty(getMINI_CORP_PUBKEY())); + private static final Party DUMMY_NOTARY = new TestIdentity(getDUMMY_NOTARY_NAME(), 20L).getParty(); + private static final TestIdentity MEGA_CORP = new TestIdentity(new CordaX500Name("MegaCorp", "London", "GB")); + private static final TestIdentity MINI_CORP = new TestIdentity(new CordaX500Name("MiniCorp", "London", "GB")); + private final PartyAndReference defaultIssuer = MEGA_CORP.ref((byte) 1); + private final Cash.State inState = new Cash.State(issuedBy(DOLLARS(1000), defaultIssuer), new AnonymousParty(MEGA_CORP.getPubkey())); + private final Cash.State outState = new Cash.State(inState.getAmount(), new AnonymousParty(MINI_CORP.getPubkey())); @Rule public final SerializationEnvironmentRule testSerialization = new SerializationEnvironmentRule(); @Test public void trivial() { IdentityServiceInternal identityService = rigorousMock(IdentityServiceInternal.class); - doReturn(getMEGA_CORP()).when(identityService).partyFromKey(getMEGA_CORP_PUBKEY()); - doReturn(getMINI_CORP()).when(identityService).partyFromKey(getMINI_CORP_PUBKEY()); - transaction(new MockServices(identityService, getMEGA_CORP().getName()), getDUMMY_NOTARY(), tx -> { + doReturn(MEGA_CORP.getParty()).when(identityService).partyFromKey(MEGA_CORP.getPubkey()); + doReturn(MINI_CORP.getParty()).when(identityService).partyFromKey(MINI_CORP.getPubkey()); + transaction(new MockServices(identityService, MEGA_CORP.getName()), DUMMY_NOTARY, tx -> { tx.attachment(Cash.PROGRAM_ID); tx.input(Cash.PROGRAM_ID, inState); tx.tweak(tw -> { - tw.output(Cash.PROGRAM_ID, new Cash.State(issuedBy(DOLLARS(2000), defaultIssuer), new AnonymousParty(getMINI_CORP_PUBKEY()))); - tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move()); + tw.output(Cash.PROGRAM_ID, new Cash.State(issuedBy(DOLLARS(2000), defaultIssuer), new AnonymousParty(MINI_CORP.getPubkey()))); + tw.command(MEGA_CORP.getPubkey(), new Cash.Commands.Move()); return tw.failsWith("the amounts balance"); }); tx.tweak(tw -> { tw.output(Cash.PROGRAM_ID, outState); - tw.command(getMEGA_CORP_PUBKEY(), DummyCommandData.INSTANCE); + tw.command(MEGA_CORP.getPubkey(), DummyCommandData.INSTANCE); // Invalid command return tw.failsWith("required net.corda.finance.contracts.asset.Cash.Commands.Move command"); }); tx.tweak(tw -> { tw.output(Cash.PROGRAM_ID, outState); - tw.command(getMINI_CORP_PUBKEY(), new Cash.Commands.Move()); + tw.command(MINI_CORP.getPubkey(), new Cash.Commands.Move()); return tw.failsWith("the owning keys are a subset of the signing keys"); }); tx.tweak(tw -> { tw.output(Cash.PROGRAM_ID, outState); // issuedBy() can't be directly imported because it conflicts with other identically named functions // with different overloads (for some reason). - tw.output(Cash.PROGRAM_ID, outState.issuedBy(getMINI_CORP())); - tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move()); + tw.output(Cash.PROGRAM_ID, outState.issuedBy(MINI_CORP.getParty())); + tw.command(MEGA_CORP.getPubkey(), new Cash.Commands.Move()); return tw.failsWith("at least one cash input"); }); // Simple reallocation works. return tx.tweak(tw -> { tw.output(Cash.PROGRAM_ID, outState); - tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move()); + tw.command(MEGA_CORP.getPubkey(), new Cash.Commands.Move()); return tw.verifies(); }); }); diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt index 7b5623e077..b05a506112 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt @@ -3,6 +3,7 @@ package net.corda.finance.contracts import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever 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 @@ -40,6 +41,11 @@ interface ICommercialPaperTestTemplate { fun getContract(): ContractClassName } +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.pubkey + class JavaCommercialPaperTest : ICommercialPaperTestTemplate { override fun getPaper(): ICommercialPaperState = JavaCommercialPaper.State( MEGA_CORP.ref(123), @@ -92,6 +98,18 @@ class CommercialPaperTestsGeneric { 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.key + private val ALICE_PUBKEY get() = alice.pubkey + 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.pubkey } @Parameterized.Parameter @@ -240,7 +258,7 @@ class CommercialPaperTestsGeneric { private lateinit var aliceServices: MockServices private lateinit var aliceVaultService: VaultService private lateinit var alicesVault: Vault - private val notaryServices = MockServices(rigorousMock(), MEGA_CORP.name, DUMMY_NOTARY_KEY) + private val notaryServices = MockServices(rigorousMock(), MEGA_CORP.name, dummyNotary.key) private val issuerServices = MockServices(listOf("net.corda.finance.contracts"), rigorousMock(), MEGA_CORP.name, dummyCashIssuer.key) private lateinit var moveTX: SignedTransaction @Test @@ -255,7 +273,7 @@ class CommercialPaperTestsGeneric { aliceVaultService = aliceServices.vaultService databaseAlice.transaction { - alicesVault = VaultFiller(aliceServices, DUMMY_NOTARY, DUMMY_NOTARY_KEY, rngFactory = ::Random).fillWithSomeTestCash(9000.DOLLARS, issuerServices, 1, DUMMY_CASH_ISSUER) + alicesVault = VaultFiller(aliceServices, dummyNotary, rngFactory = ::Random).fillWithSomeTestCash(9000.DOLLARS, issuerServices, 1, DUMMY_CASH_ISSUER) aliceVaultService = aliceServices.vaultService } val bigCorpDatabaseAndServices = makeTestDatabaseAndMockServices( @@ -268,7 +286,7 @@ class CommercialPaperTestsGeneric { bigCorpVaultService = bigCorpServices.vaultService databaseBigCorp.transaction { - bigCorpVault = VaultFiller(bigCorpServices, DUMMY_NOTARY, DUMMY_NOTARY_KEY, rngFactory = ::Random).fillWithSomeTestCash(13000.DOLLARS, issuerServices, 1, DUMMY_CASH_ISSUER) + bigCorpVault = VaultFiller(bigCorpServices, dummyNotary, rngFactory = ::Random).fillWithSomeTestCash(13000.DOLLARS, issuerServices, 1, DUMMY_CASH_ISSUER) bigCorpVaultService = bigCorpServices.vaultService } diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt index 0b38ba922e..0a9cc89666 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt @@ -33,14 +33,35 @@ import java.util.* import kotlin.test.* class CashTests { - companion object { - private val DUMMY_CASH_ISSUER_IDENTITY = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10).identity + private companion object { + val alice = TestIdentity(ALICE_NAME, 70) + val BOB_PUBKEY = TestIdentity(BOB_NAME, 80).pubkey + val charlie = TestIdentity(CHARLIE_NAME, 90) + val DUMMY_CASH_ISSUER_IDENTITY = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10).identity + val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20) + val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) + val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) + val ALICE get() = alice.party + val ALICE_PUBKEY get() = alice.pubkey + val CHARLIE get() = charlie.party + val CHARLIE_IDENTITY get() = charlie.identity + val DUMMY_NOTARY get() = dummyNotary.party + val DUMMY_NOTARY_IDENTITY get() = dummyNotary.identity + val DUMMY_NOTARY_KEY get() = dummyNotary.key + val MEGA_CORP get() = megaCorp.party + val MEGA_CORP_IDENTITY get() = megaCorp.identity + val MEGA_CORP_KEY get() = megaCorp.key + val MEGA_CORP_PUBKEY get() = megaCorp.pubkey + val MINI_CORP get() = miniCorp.party + val MINI_CORP_IDENTITY get() = miniCorp.identity + val MINI_CORP_KEY get() = miniCorp.key + val MINI_CORP_PUBKEY get() = miniCorp.pubkey } @Rule @JvmField val testSerialization = SerializationEnvironmentRule() - private val defaultRef = OpaqueBytes(ByteArray(1, { 1 })) + private val defaultRef = OpaqueBytes.of(1) private val defaultIssuer = MEGA_CORP.ref(defaultRef) private val inState = Cash.State( amount = 1000.DOLLARS `issued by` defaultIssuer, @@ -92,7 +113,7 @@ class CashTests { // Create some cash. Any attempt to spend >$500 will require multiple issuers to be involved. database.transaction { - val vaultFiller = VaultFiller(ourServices, DUMMY_NOTARY, DUMMY_NOTARY_KEY, rngFactory = ::Random) + val vaultFiller = VaultFiller(ourServices, dummyNotary, rngFactory = ::Random) vaultFiller.fillWithSomeTestCash(100.DOLLARS, megaCorpServices, 1, MEGA_CORP.ref(1), ourIdentity) vaultFiller.fillWithSomeTestCash(400.DOLLARS, megaCorpServices, 1, MEGA_CORP.ref(1), ourIdentity) vaultFiller.fillWithSomeTestCash(80.DOLLARS, miniCorpServices, 1, MINI_CORP.ref(1), ourIdentity) diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt index 9489de4734..2d81b49fe8 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt @@ -34,8 +34,23 @@ import kotlin.test.assertNotEquals import kotlin.test.assertTrue class ObligationTests { - companion object { - private val DUMMY_OBLIGATION_ISSUER = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10).party + private companion object { + val alice = TestIdentity(ALICE_NAME, 70) + val bob = TestIdentity(BOB_NAME, 80) + val CHARLIE = TestIdentity(CHARLIE_NAME, 90).party + val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20) + val DUMMY_OBLIGATION_ISSUER = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10).party + val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) + val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) + val ALICE get() = alice.party + val ALICE_PUBKEY get() = alice.pubkey + val BOB get() = bob.party + val BOB_PUBKEY get() = bob.pubkey + val DUMMY_NOTARY get() = dummyNotary.party + val MEGA_CORP get() = megaCorp.party + val MEGA_CORP_PUBKEY get() = megaCorp.pubkey + val MINI_CORP get() = miniCorp.party + val MINI_CORP_PUBKEY get() = miniCorp.pubkey } @Rule @@ -59,8 +74,8 @@ class ObligationTests { beneficiary = CHARLIE ) private val outState = inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) - private val miniCorpServices = MockServices(listOf("net.corda.finance.contracts.asset"), rigorousMock(), MINI_CORP.name, MINI_CORP_KEY) - private val notaryServices = MockServices(rigorousMock(), MEGA_CORP.name, DUMMY_NOTARY_KEY) + private val miniCorpServices = MockServices(listOf("net.corda.finance.contracts.asset"), rigorousMock(), miniCorp) + private val notaryServices = MockServices(rigorousMock(), MEGA_CORP.name, dummyNotary.key) private val identityService = rigorousMock().also { doReturn(null).whenever(it).partyFromKey(ALICE_PUBKEY) doReturn(null).whenever(it).partyFromKey(BOB_PUBKEY) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt index 3d1bbf8e9b..5ec5da6014 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt @@ -4,6 +4,7 @@ import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.* import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.node.ServicesForResolution import net.corda.core.serialization.deserialize @@ -19,6 +20,11 @@ import org.junit.Rule import org.junit.Test class AttachmentsClassLoaderStaticContractTests { + private companion object { + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party + val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party + } + @Rule @JvmField val testSerialization = SerializationEnvironmentRule() diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderTests.kt index 4f98293c53..fecf0b4fb0 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderTests.kt @@ -4,6 +4,7 @@ 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.CordaX500Name import net.corda.core.internal.declaredField import net.corda.core.internal.toWireTransaction import net.corda.core.node.ServiceHub @@ -35,7 +36,8 @@ class AttachmentsClassLoaderTests { companion object { val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentsClassLoaderTests::class.java.getResource("isolated.jar") private const val ISOLATED_CONTRACT_CLASS_NAME = "net.corda.finance.contracts.isolated.AnotherDummyContract" - + private val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party + private val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party private fun SerializationContext.withAttachmentStorage(attachmentStorage: AttachmentStorage): SerializationContext { val serviceHub = rigorousMock() doReturn(attachmentStorage).whenever(serviceHub).attachments diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt index ca4d2b2e7e..30c8004659 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt @@ -17,10 +17,7 @@ import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.nodeapi.internal.serialization.SerializationContextImpl import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 -import net.corda.testing.ALICE -import net.corda.testing.BOB -import net.corda.testing.BOB_PUBKEY -import net.corda.testing.MEGA_CORP +import net.corda.testing.* import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.BasicConstraints import org.bouncycastle.asn1.x509.Extension @@ -49,6 +46,14 @@ import kotlin.concurrent.thread import kotlin.test.* class X509UtilitiesTest { + private companion object { + val ALICE = TestIdentity(ALICE_NAME, 70).party + val bob = TestIdentity(BOB_NAME, 80) + val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party + val BOB get() = bob.party + val BOB_PUBKEY get() = bob.pubkey + } + @Rule @JvmField val tempFolder: TemporaryFolder = TemporaryFolder() diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ContractAttachmentSerializerTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ContractAttachmentSerializerTest.kt index c542330ebd..80ee09701a 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ContractAttachmentSerializerTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/ContractAttachmentSerializerTest.kt @@ -1,6 +1,7 @@ package net.corda.nodeapi.internal.serialization import net.corda.core.contracts.ContractAttachment +import net.corda.core.identity.CordaX500Name import net.corda.core.serialization.* import net.corda.testing.* import net.corda.testing.contracts.DummyContract @@ -22,7 +23,7 @@ class ContractAttachmentSerializerTest { private lateinit var factory: SerializationFactory private lateinit var context: SerializationContext private lateinit var contextWithToken: SerializationContext - private val mockServices = MockServices(rigorousMock(), MEGA_CORP.name) + private val mockServices = MockServices(rigorousMock(), CordaX500Name("MegaCorp", "London", "GB")) @Before fun setup() { factory = testSerialization.env.serializationFactory diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt index 6eee1230be..508a40f7c6 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/KryoTests.kt @@ -14,8 +14,9 @@ import net.corda.core.utilities.sequence import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.services.persistence.NodeAttachmentService import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 -import net.corda.testing.ALICE_PUBKEY +import net.corda.testing.ALICE_NAME import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.TestIdentity import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Before @@ -31,6 +32,10 @@ import kotlin.test.assertNotNull import kotlin.test.assertTrue class KryoTests { + companion object { + private val ALICE_PUBKEY = TestIdentity(ALICE_NAME, 70).pubkey + } + @Rule @JvmField val testSerialization = SerializationEnvironmentRule() diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt index 9a987f1b8a..68bc1a5786 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt @@ -9,6 +9,7 @@ import net.corda.core.crypto.SecureHash import net.corda.core.crypto.secureRandomBytes import net.corda.core.flows.FlowException import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.AbstractAttachment import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.MissingAttachmentsException @@ -19,11 +20,8 @@ import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.nodeapi.internal.serialization.EmptyWhitelist import net.corda.nodeapi.internal.serialization.GeneratedAttachment import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.isPrimitive -import net.corda.testing.BOB_IDENTITY -import net.corda.testing.MEGA_CORP -import net.corda.testing.MEGA_CORP_PUBKEY +import net.corda.testing.* import net.corda.testing.contracts.DummyContract -import net.corda.testing.withTestSerialization import org.apache.activemq.artemis.api.core.SimpleString import org.apache.qpid.proton.amqp.* import org.apache.qpid.proton.codec.DecoderImpl @@ -46,6 +44,16 @@ import kotlin.test.assertNotNull import kotlin.test.assertTrue class SerializationOutputTests { + private companion object { + val BOB_IDENTITY = TestIdentity(BOB_NAME, 80).identity + val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) + val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) + val MEGA_CORP get() = megaCorp.party + val MEGA_CORP_PUBKEY get() = megaCorp.pubkey + val MINI_CORP get() = miniCorp.party + val MINI_CORP_PUBKEY get() = miniCorp.pubkey + } + data class Foo(val bar: String, val pub: Int) data class testFloat(val f: Float) diff --git a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt index 5efb235eb2..33124f11ef 100644 --- a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt @@ -9,7 +9,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.node.internal.NodeStartup import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User -import net.corda.testing.ALICE +import net.corda.testing.ALICE_NAME import net.corda.testing.common.internal.ProjectStructure.projectRootDir import net.corda.testing.driver.driver import org.assertj.core.api.Assertions.assertThat @@ -36,12 +36,12 @@ class BootTests { val logConfigFile = projectRootDir / "config" / "dev" / "log4j2.xml" assertThat(logConfigFile).isRegularFile() driver(isDebug = true, systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString())) { - val alice = startNode(providedName = ALICE.name).get() + val alice = startNode(providedName = ALICE_NAME).get() val logFolder = alice.configuration.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME val logFile = logFolder.toFile().listFiles { _, name -> name.endsWith(".log") }.single() // Start second Alice, should fail assertThatThrownBy { - startNode(providedName = ALICE.name).getOrThrow() + startNode(providedName = ALICE_NAME).getOrThrow() } // We count the number of nodes that wrote into the logfile by counting "Logs can be found in" val numberOfNodesThatLogged = Files.lines(logFile.toPath()).filter { NodeStartup.LOGS_CAN_BE_FOUND_IN_STRING in it }.count() diff --git a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt index c6464056e1..422ae6e1ac 100644 --- a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt @@ -9,9 +9,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User -import net.corda.testing.ALICE -import net.corda.testing.BOB -import net.corda.testing.chooseIdentity +import net.corda.testing.* import net.corda.testing.driver.driver import org.assertj.core.api.Assertions.assertThat import org.junit.Test @@ -23,8 +21,8 @@ class CordappScanningDriverTest { // The driver will automatically pick up the annotated flows below driver { val (alice, bob) = listOf( - startNode(providedName = ALICE.name, rpcUsers = listOf(user)), - startNode(providedName = BOB.name)).transpose().getOrThrow() + startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)), + startNode(providedName = BOB_NAME)).transpose().getOrThrow() val initiatedFlowClass = alice.rpcClientToNode() .start(user.username, user.password) .proxy diff --git a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt index cc3c1e0dc8..074f3a619b 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -14,7 +14,7 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User -import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.DUMMY_NOTARY_NAME import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver import net.corda.testing.internal.InternalDriverDSL @@ -105,7 +105,7 @@ class NodePerformanceTests { fun `self pay rate`() { val user = User("A", "A", setOf(startFlow(), startFlow())) driver( - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name, rpcUsers = listOf(user))), + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, rpcUsers = listOf(user))), startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance") ) { diff --git a/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt b/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt index 585f045eb9..4f597a016c 100644 --- a/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/SSHServerTest.kt @@ -10,17 +10,16 @@ import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.nodeapi.internal.config.User -import net.corda.testing.ALICE import net.corda.testing.driver.driver import org.bouncycastle.util.io.Streams import org.junit.Test import net.corda.node.services.Permissions.Companion.startFlow +import net.corda.testing.ALICE_NAME import java.net.ConnectException import kotlin.test.assertTrue import kotlin.test.fail import org.assertj.core.api.Assertions.assertThat import java.util.regex.Pattern -import kotlin.reflect.jvm.jvmName class SSHServerTest { @@ -28,8 +27,8 @@ class SSHServerTest { fun `ssh server does not start be default`() { val user = User("u", "p", setOf()) // The driver will automatically pick up the annotated flows below - driver() { - val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user)) + driver { + val node = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)) node.getOrThrow() val session = JSch().getSession("u", "localhost", 2222) @@ -50,7 +49,7 @@ class SSHServerTest { val user = User("u", "p", setOf()) // The driver will automatically pick up the annotated flows below driver { - val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user), + val node = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user), customOverrides = mapOf("sshd" to mapOf("port" to 2222))) node.getOrThrow() @@ -70,7 +69,7 @@ class SSHServerTest { val user = User("u", "p", setOf()) // The driver will automatically pick up the annotated flows below driver { - val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user), + val node = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user), customOverrides = mapOf("sshd" to mapOf("port" to 2222))) node.getOrThrow() @@ -93,7 +92,7 @@ class SSHServerTest { val user = User("u", "p", setOf(startFlow())) // The driver will automatically pick up the annotated flows below driver(isDebug = true) { - val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user), + val node = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user), customOverrides = mapOf("sshd" to mapOf("port" to 2222))) node.getOrThrow() @@ -105,7 +104,7 @@ class SSHServerTest { assertTrue(session.isConnected) val channel = session.openChannel("exec") as ChannelExec - channel.setCommand("start FlowICannotRun otherParty: \"O=Alice Corp,L=Madrid,C=ES\"") + channel.setCommand("start FlowICannotRun otherParty: \"${ALICE_NAME}\"") channel.connect() val response = String(Streams.readAll(channel.inputStream)) @@ -123,7 +122,7 @@ class SSHServerTest { val user = User("u", "p", setOf(startFlow())) // The driver will automatically pick up the annotated flows below driver(isDebug = true) { - val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user), + val node = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user), customOverrides = mapOf("sshd" to mapOf("port" to 2222))) node.getOrThrow() diff --git a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt index d31b79c255..c3c579119f 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt @@ -20,14 +20,11 @@ import net.corda.core.utilities.contextLogger import net.corda.core.utilities.getOrThrow import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProviderImpl -import net.corda.testing.DUMMY_BANK_A -import net.corda.testing.DUMMY_NOTARY +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.node.MockAttachmentStorage -import net.corda.testing.rigorousMock -import net.corda.testing.withTestSerialization import org.junit.Assert.assertEquals import org.junit.Test import java.net.URLClassLoader @@ -51,7 +48,8 @@ class AttachmentLoadingTests { val flowInitiatorClass: Class> = Class.forName("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator", true, URLClassLoader(arrayOf(isolatedJAR))) .asSubclass(FlowLogic::class.java) - + val DUMMY_BANK_A = TestIdentity(DUMMY_BANK_A_NAME, 40).party + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party private fun DriverDSL.createTwoNodes(): List { return listOf( startNode(providedName = bankAName), diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt index a1b9a409ac..32a09d06a9 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt @@ -41,9 +41,9 @@ class DistributedServiceTests { driver( extraCordappPackagesToScan = listOf("net.corda.finance.contracts"), - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name, rpcUsers = listOf(testUser), cluster = ClusterSpec.Raft(clusterSize = 3)))) + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, rpcUsers = listOf(testUser), cluster = ClusterSpec.Raft(clusterSize = 3)))) { - alice = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser)).getOrThrow() + alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(testUser)).getOrThrow() raftNotaryIdentity = defaultNotaryIdentity notaryNodes = defaultNotaryHandle.nodeHandles.getOrThrow().map { it as NodeHandle.OutOfProcess } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt index a948d2fe93..c3b7b7544b 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt @@ -12,13 +12,10 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode import net.corda.node.services.transactions.RaftValidatingNotaryService -import net.corda.testing.DUMMY_BANK_A -import net.corda.testing.chooseIdentity +import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver -import net.corda.testing.dummyCommand -import net.corda.testing.startFlow import net.corda.testing.node.ClusterSpec import net.corda.testing.node.NotarySpec import org.junit.Test @@ -36,8 +33,7 @@ class RaftNotaryServiceTests { extraCordappPackagesToScan = listOf("net.corda.testing.contracts"), notarySpecs = listOf(NotarySpec(notaryName, cluster = ClusterSpec.Raft(clusterSize = 3)))) { - val bankA = startNode(providedName = DUMMY_BANK_A.name).map { (it as NodeHandle.InProcess).node }.getOrThrow() - + val bankA = startNode(providedName = DUMMY_BANK_A_NAME).map { (it as NodeHandle.InProcess).node }.getOrThrow() val inputState = issueState(bankA, defaultNotaryIdentity) val firstTxBuilder = TransactionBuilder(defaultNotaryIdentity) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt index 8cd555f231..45e4754ab8 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt @@ -2,8 +2,8 @@ package net.corda.node.services.network import net.corda.core.node.NodeInfo import net.corda.core.utilities.seconds -import net.corda.testing.ALICE -import net.corda.testing.BOB +import net.corda.testing.ALICE_NAME +import net.corda.testing.BOB_NAME import net.corda.testing.internal.CompatibilityZoneParams import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation @@ -37,9 +37,8 @@ class NetworkMapTest { @Test fun `nodes can see each other using the http network map`() { internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone) { - val alice = startNode(providedName = ALICE.name) - val bob = startNode(providedName = BOB.name) - + val alice = startNode(providedName = ALICE_NAME) + val bob = startNode(providedName = BOB_NAME) val notaryNode = defaultNotaryNode.get() val aliceNode = alice.get() val bobNode = bob.get() @@ -53,14 +52,13 @@ class NetworkMapTest { @Test fun `nodes process network map add updates correctly when adding new node to network map`() { internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone) { - val alice = startNode(providedName = ALICE.name) + val alice = startNode(providedName = ALICE_NAME) val notaryNode = defaultNotaryNode.get() val aliceNode = alice.get() notaryNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo) aliceNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo) - - val bob = startNode(providedName = BOB.name) + val bob = startNode(providedName = BOB_NAME) val bobNode = bob.get() // Wait for network map client to poll for the next update. @@ -75,9 +73,8 @@ class NetworkMapTest { @Test fun `nodes process network map remove updates correctly`() { internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone) { - val alice = startNode(providedName = ALICE.name) - val bob = startNode(providedName = BOB.name) - + val alice = startNode(providedName = ALICE_NAME) + val bob = startNode(providedName = BOB_NAME) val notaryNode = defaultNotaryNode.get() val aliceNode = alice.get() val bobNode = bob.get() diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt index 72068d73fe..8977afda78 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt @@ -9,7 +9,6 @@ import net.corda.core.internal.div import net.corda.core.node.NodeInfo import net.corda.core.node.services.KeyManagementService import net.corda.core.serialization.serialize -import net.corda.node.services.identity.InMemoryIdentityService import net.corda.nodeapi.internal.NodeInfoFilesCopier import net.corda.testing.* import net.corda.testing.node.MockKeyManagementService @@ -28,8 +27,9 @@ import kotlin.test.assertEquals import kotlin.test.assertTrue class NodeInfoWatcherTest { - companion object { - val nodeInfo = NodeInfo(listOf(), listOf(getTestPartyAndCertificate(ALICE)), 0, 0) + private companion object { + val alice = TestIdentity(ALICE_NAME, 70) + val nodeInfo = NodeInfo(listOf(), listOf(alice.identity), 0, 0) } @Rule @@ -49,7 +49,7 @@ class NodeInfoWatcherTest { @Before fun start() { val identityService = makeTestIdentityService() - keyManagementService = MockKeyManagementService(identityService, ALICE_KEY) + keyManagementService = MockKeyManagementService(identityService, alice.key) nodeInfoWatcher = NodeInfoWatcher(tempFolder.root.toPath(), scheduler) nodeInfoPath = tempFolder.root.toPath() / CordformNode.NODE_INFO_DIRECTORY } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt index d4bdec79b7..1807d5ad8f 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt @@ -6,9 +6,9 @@ import net.corda.core.node.NodeInfo import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.internal.Node import net.corda.node.internal.StartedNode -import net.corda.testing.ALICE -import net.corda.testing.BOB -import net.corda.testing.DUMMY_REGULATOR +import net.corda.testing.ALICE_NAME +import net.corda.testing.BOB_NAME +import net.corda.testing.TestIdentity import net.corda.testing.chooseIdentity import net.corda.testing.internal.NodeBasedTest import org.junit.Before @@ -16,6 +16,12 @@ import org.junit.Test import kotlin.test.assertEquals class PersistentNetworkMapCacheTest : NodeBasedTest() { + private companion object { + val ALICE = TestIdentity(ALICE_NAME, 70).party + val BOB = TestIdentity(BOB_NAME, 80).party + val DUMMY_REGULATOR = TestIdentity(CordaX500Name("Regulator A", "Paris", "FR"), 100).party + } + private val partiesList = listOf(DUMMY_REGULATOR, ALICE, BOB) private val addressesMap = HashMap() private val infos = HashSet() diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt index 3396277518..0075950722 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt @@ -7,19 +7,16 @@ import net.corda.core.flows.InitiatingFlow import net.corda.core.identity.Party import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap -import net.corda.testing.ALICE -import net.corda.testing.BOB -import net.corda.testing.chooseIdentity +import net.corda.testing.* import net.corda.testing.internal.NodeBasedTest -import net.corda.testing.startFlow import org.assertj.core.api.Assertions.assertThat import org.junit.Test class FlowVersioningTest : NodeBasedTest() { @Test fun `getFlowContext returns the platform version for core flows`() { - val alice = startNode(ALICE.name, platformVersion = 2) - val bob = startNode(BOB.name, platformVersion = 3) + val alice = startNode(ALICE_NAME, platformVersion = 2) + val bob = startNode(BOB_NAME, platformVersion = 3) bob.internals.installCoreFlow(PretendInitiatingCoreFlow::class, ::PretendInitiatedCoreFlow) val (alicePlatformVersionAccordingToBob, bobPlatformVersionAccordingToAlice) = alice.services.startFlow( PretendInitiatingCoreFlow(bob.info.chooseIdentity())).resultFuture.getOrThrow() diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt index 342485889e..cf8b4593b9 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -21,6 +21,11 @@ import kotlin.test.assertEquals * transaction size limit (which should only consider the hashes). */ class LargeTransactionsTest { + private companion object { + val BOB = TestIdentity(BOB_NAME, 80).party + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party + } + @StartableByRPC @InitiatingFlow class SendLargeTransactionFlow(private val hash1: SecureHash, diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt index fc2c15c746..94eb96a7a8 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt @@ -1,14 +1,13 @@ package net.corda.services.messaging import net.corda.core.crypto.Crypto +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.* import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER import net.corda.nodeapi.RPCApi import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.crypto.* -import net.corda.testing.MEGA_CORP -import net.corda.testing.MINI_CORP import net.corda.testing.messaging.SimpleMQClient import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration import org.apache.activemq.artemis.api.core.ActiveMQClusterSecurityException @@ -86,7 +85,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() { override val trustStorePassword: String get() = "trustpass" init { - val legalName = MEGA_CORP.name + val legalName = CordaX500Name("MegaCorp", "London", "GB") certificatesDirectory.createDirectories() if (!trustStoreFile.exists()) { javaClass.classLoader.getResourceAsStream("certificates/cordatruststore.jks").copyTo(trustStoreFile) @@ -106,7 +105,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() { intermediateCA.keyPair, legalName, clientKey.public, nameConstraints = nameConstraints) val tlsKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) // Using different x500 name in the TLS cert which is not allowed in the name constraints. - val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, MINI_CORP.name, tlsKey.public) + val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, CordaX500Name("MiniCorp", "London", "GB"), tlsKey.public) val keyPass = keyStorePassword.toCharArray() val clientCAKeystore = loadOrCreateKeyStore(nodeKeystore, keyStorePassword) clientCAKeystore.addOrReplaceKey( diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt index 45ffb4289a..66960efb89 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt @@ -9,6 +9,7 @@ import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowSession import net.corda.core.flows.InitiatedBy import net.corda.core.flows.InitiatingFlow +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.messaging.CordaRPCOps import net.corda.core.utilities.NetworkHostAndPort @@ -49,7 +50,7 @@ abstract class MQSecurityTest : NodeBasedTest() { @Before fun start() { - alice = startNode(ALICE.name, rpcUsers = extraRPCUsers + rpcUser) + alice = startNode(ALICE_NAME, rpcUsers = extraRPCUsers + rpcUser) attacker = createAttacker() startAttacker(attacker) } @@ -84,7 +85,7 @@ abstract class MQSecurityTest : NodeBasedTest() { @Test fun `create queue for peer which has not been communicated with`() { - val bob = startNode(BOB.name) + val bob = startNode(BOB_NAME) assertAllQueueCreationAttacksFail("$PEERS_PREFIX${bob.info.chooseIdentity().owningKey.toBase58String()}") } @@ -134,7 +135,7 @@ abstract class MQSecurityTest : NodeBasedTest() { assertAllQueueCreationAttacksFail(randomQueue) } - fun clientTo(target: NetworkHostAndPort, sslConfiguration: SSLConfiguration? = configureTestSSL()): SimpleMQClient { + fun clientTo(target: NetworkHostAndPort, sslConfiguration: SSLConfiguration? = configureTestSSL(CordaX500Name("MegaCorp", "London", "GB"))): SimpleMQClient { val client = SimpleMQClient(target, sslConfiguration) clients += client return client @@ -212,7 +213,7 @@ abstract class MQSecurityTest : NodeBasedTest() { } private fun startBobAndCommunicateWithAlice(): Party { - val bob = startNode(BOB.name) + val bob = startNode(BOB_NAME) bob.registerInitiatedFlow(ReceiveFlow::class.java) val bobParty = bob.info.chooseIdentity() // Perform a protocol exchange to force the peer queue to be created diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index 769015bfba..e87ff6e48b 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -16,7 +16,7 @@ import net.corda.node.internal.Node import net.corda.node.internal.StartedNode import net.corda.node.services.messaging.* import net.corda.node.services.transactions.RaftValidatingNotaryService -import net.corda.testing.ALICE +import net.corda.testing.ALICE_NAME import net.corda.testing.chooseIdentity import net.corda.testing.driver.DriverDSL import net.corda.testing.driver.NodeHandle @@ -123,7 +123,7 @@ class P2PMessagingTest { } private fun DriverDSL.startAlice(): StartedNode { - return startNode(providedName = ALICE.name, customOverrides = mapOf("messageRedeliveryDelaySeconds" to 1)) + return startNode(providedName = ALICE_NAME, customOverrides = mapOf("messageRedeliveryDelaySeconds" to 1)) .map { (it as NodeHandle.InProcess).node } .getOrThrow() } diff --git a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java index 827ab12342..b71fc7db16 100644 --- a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java +++ b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java @@ -7,6 +7,7 @@ import net.corda.core.contracts.*; import net.corda.core.crypto.CryptoUtils; import net.corda.core.identity.AbstractParty; import net.corda.core.identity.CordaX500Name; +import net.corda.core.identity.Party; import net.corda.core.messaging.DataFeed; import net.corda.core.node.services.Vault; import net.corda.core.node.services.VaultQueryException; @@ -15,7 +16,6 @@ import net.corda.core.node.services.vault.*; import net.corda.core.node.services.vault.QueryCriteria.LinearStateQueryCriteria; import net.corda.core.node.services.vault.QueryCriteria.VaultCustomQueryCriteria; import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria; -import net.corda.core.utilities.OpaqueBytes; import net.corda.finance.contracts.DealState; import net.corda.finance.contracts.asset.Cash; import net.corda.finance.schemas.CashSchemaV1; @@ -45,15 +45,21 @@ 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.CoreTestUtils.*; -import static net.corda.testing.TestConstants.*; +import static net.corda.testing.CoreTestUtils.rigorousMock; +import static net.corda.testing.TestConstants.getBOC_NAME; +import static net.corda.testing.TestConstants.getCHARLIE_NAME; +import static net.corda.testing.TestConstants.getDUMMY_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; public class VaultQueryJavaTests { - private static final TestIdentity DUMMY_CASH_ISSUER_INFO = new TestIdentity(new CordaX500Name("Snake Oil Issuer", "London", "GB"), (long) 10); + private static final TestIdentity BOC = new TestIdentity(getBOC_NAME()); + private static final Party CHARLIE = new TestIdentity(getCHARLIE_NAME(), 90L).getParty(); + private static final TestIdentity DUMMY_CASH_ISSUER_INFO = new TestIdentity(new CordaX500Name("Snake Oil Issuer", "London", "GB"), 10L); private static final PartyAndReference DUMMY_CASH_ISSUER = DUMMY_CASH_ISSUER_INFO.ref((byte) 1); + private static final TestIdentity DUMMY_NOTARY = new TestIdentity(getDUMMY_NOTARY_NAME(), 20L); + private static final TestIdentity MEGA_CORP = new TestIdentity(new CordaX500Name("MegaCorp", "London", "GB")); @Rule public final SerializationEnvironmentRule testSerialization = new SerializationEnvironmentRule(); private VaultFiller vaultFiller; @@ -64,16 +70,16 @@ public class VaultQueryJavaTests { @Before public void setUp() throws CertificateException, InvalidAlgorithmParameterException { List cordappPackages = Arrays.asList("net.corda.testing.contracts", "net.corda.finance.contracts.asset", CashSchemaV1.class.getPackage().getName()); - IdentityServiceInternal identitySvc = makeTestIdentityService(Arrays.asList(getMEGA_CORP_IDENTITY(), DUMMY_CASH_ISSUER_INFO.getIdentity(), getDUMMY_NOTARY_IDENTITY())); + IdentityServiceInternal identitySvc = makeTestIdentityService(Arrays.asList(MEGA_CORP.getIdentity(), DUMMY_CASH_ISSUER_INFO.getIdentity(), DUMMY_NOTARY.getIdentity())); Pair databaseAndServices = makeTestDatabaseAndMockServices( - Arrays.asList(getMEGA_CORP_KEY(), getDUMMY_NOTARY_KEY()), + Arrays.asList(MEGA_CORP.getKey(), DUMMY_NOTARY.getKey()), identitySvc, cordappPackages, - getMEGA_CORP().getName()); - issuerServices = new MockServices(cordappPackages, rigorousMock(IdentityServiceInternal.class), DUMMY_CASH_ISSUER_INFO, getBOC_KEY()); + MEGA_CORP.getName()); + issuerServices = new MockServices(cordappPackages, rigorousMock(IdentityServiceInternal.class), DUMMY_CASH_ISSUER_INFO, BOC.getKey()); database = databaseAndServices.getFirst(); MockServices services = databaseAndServices.getSecond(); - vaultFiller = new VaultFiller(services, getDUMMY_NOTARY(), getDUMMY_NOTARY_KEY()); + vaultFiller = new VaultFiller(services, DUMMY_NOTARY); vaultService = services.getVaultService(); } @@ -147,7 +153,7 @@ public class VaultQueryJavaTests { return tx; }); database.transaction(tx -> { - vaultFiller.consumeCash(amount, getCHARLIE()); + vaultFiller.consumeCash(amount, CHARLIE); return tx; }); database.transaction(tx -> { @@ -300,7 +306,7 @@ public class VaultQueryJavaTests { QueryCriteria vaultCriteria = new VaultQueryCriteria(Vault.StateStatus.UNCONSUMED, contractStateTypes); List linearIds = Collections.singletonList(uid); - List dealParty = Collections.singletonList(getMEGA_CORP()); + List dealParty = Collections.singletonList(MEGA_CORP.getParty()); QueryCriteria dealCriteria = new LinearStateQueryCriteria(dealParty, null, dealIds); QueryCriteria linearCriteria = new LinearStateQueryCriteria(dealParty, linearIds, Vault.StateStatus.UNCONSUMED, null); QueryCriteria dealOrLinearIdCriteria = dealCriteria.or(linearCriteria); @@ -442,9 +448,9 @@ public class VaultQueryJavaTests { Amount pounds300 = new Amount<>(300, Currency.getInstance("GBP")); Amount pounds400 = new Amount<>(400, Currency.getInstance("GBP")); vaultFiller.fillWithSomeTestCash(dollars100, issuerServices, 1, DUMMY_CASH_ISSUER); - vaultFiller.fillWithSomeTestCash(dollars200, issuerServices, 2, getBOC().ref(new OpaqueBytes("1".getBytes()))); + vaultFiller.fillWithSomeTestCash(dollars200, issuerServices, 2, BOC.ref((byte) '1')); vaultFiller.fillWithSomeTestCash(pounds300, issuerServices, 3, DUMMY_CASH_ISSUER); - vaultFiller.fillWithSomeTestCash(pounds400, issuerServices, 4, getBOC().ref(new OpaqueBytes("1".getBytes()))); + vaultFiller.fillWithSomeTestCash(pounds400, issuerServices, 4, BOC.ref((byte) '1')); return tx; }); database.transaction(tx -> { @@ -460,13 +466,13 @@ public class VaultQueryJavaTests { assertThat(results.getOtherResults()).hasSize(12); assertThat(results.getOtherResults().get(0)).isEqualTo(400L); - assertThat(results.getOtherResults().get(1)).isEqualTo(CryptoUtils.toStringShort(getBOC_PUBKEY())); + assertThat(results.getOtherResults().get(1)).isEqualTo(CryptoUtils.toStringShort(BOC.getPubkey())); assertThat(results.getOtherResults().get(2)).isEqualTo("GBP"); assertThat(results.getOtherResults().get(3)).isEqualTo(300L); assertThat(results.getOtherResults().get(4)).isEqualTo(CryptoUtils.toStringShort(DUMMY_CASH_ISSUER_INFO.getPubkey())); assertThat(results.getOtherResults().get(5)).isEqualTo("GBP"); assertThat(results.getOtherResults().get(6)).isEqualTo(200L); - assertThat(results.getOtherResults().get(7)).isEqualTo(CryptoUtils.toStringShort(getBOC_PUBKEY())); + assertThat(results.getOtherResults().get(7)).isEqualTo(CryptoUtils.toStringShort(BOC.getPubkey())); assertThat(results.getOtherResults().get(8)).isEqualTo("USD"); assertThat(results.getOtherResults().get(9)).isEqualTo(100L); assertThat(results.getOtherResults().get(10)).isEqualTo(CryptoUtils.toStringShort(DUMMY_CASH_ISSUER_INFO.getPubkey())); diff --git a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt index 242536a172..d2caa2f214 100644 --- a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt +++ b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt @@ -6,6 +6,7 @@ import net.corda.core.contracts.Amount import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic import net.corda.core.flows.StateMachineRunId +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.concurrent.openFuture import net.corda.core.messaging.FlowProgressHandleImpl @@ -13,8 +14,7 @@ import net.corda.core.utilities.ProgressTracker import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.node.shell.InteractiveShell import net.corda.node.internal.configureDatabase -import net.corda.testing.MEGA_CORP -import net.corda.testing.MEGA_CORP_IDENTITY +import net.corda.testing.TestIdentity import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestIdentityService import net.corda.testing.rigorousMock @@ -26,6 +26,10 @@ import java.util.* import kotlin.test.assertEquals class InteractiveShellTest { + companion object { + private val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) + } + @Before fun setup() { InteractiveShell.database = configureDatabase(MockServices.makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock()) @@ -48,7 +52,7 @@ class InteractiveShellTest { override fun call() = a } - private val ids = makeTestIdentityService(listOf(MEGA_CORP_IDENTITY)) + private val ids = makeTestIdentityService(listOf(megaCorp.identity)) private val om = JacksonSupport.createInMemoryMapper(ids, YAMLFactory()) private fun check(input: String, expected: String) { @@ -90,5 +94,5 @@ class InteractiveShellTest { fun flowTooManyParams() = check("b: 12, c: Yo, d: Bar", "") @Test - fun party() = check("party: \"${MEGA_CORP.name}\"", MEGA_CORP.name.toString()) + fun party() = check("party: \"${megaCorp.name}\"", megaCorp.name.toString()) } diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index b311425257..921637ba47 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -72,6 +72,10 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { @JvmStatic @Parameterized.Parameters(name = "Anonymous = {0}") fun data(): Collection = listOf(true, false) + + private val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20) + private val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party + private val DUMMY_NOTARY get() = dummyNotary.party } private lateinit var mockNet: MockNetwork @@ -111,7 +115,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { bobNode.internals.disableDBCloseOnStop() bobNode.database.transaction { - VaultFiller(bobNode.services, DUMMY_NOTARY, DUMMY_NOTARY_KEY, notary, ::Random).fillWithSomeTestCash(2000.DOLLARS, bankNode.services, 3, 10, cashIssuer) + VaultFiller(bobNode.services, dummyNotary, notary, ::Random).fillWithSomeTestCash(2000.DOLLARS, bankNode.services, 3, 10, cashIssuer) } val alicesFakePaper = aliceNode.database.transaction { @@ -162,7 +166,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { bobNode.internals.disableDBCloseOnStop() val cashStates = bobNode.database.transaction { - VaultFiller(bobNode.services, DUMMY_NOTARY, DUMMY_NOTARY_KEY, notary, ::Random).fillWithSomeTestCash(2000.DOLLARS, bankNode.services, 3, issuer) + VaultFiller(bobNode.services, dummyNotary, notary, ::Random).fillWithSomeTestCash(2000.DOLLARS, bankNode.services, 3, issuer) } val alicesFakePaper = aliceNode.database.transaction { @@ -223,7 +227,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { val issuer = bank.ref(1, 2, 3) bobNode.database.transaction { - VaultFiller(bobNode.services, DUMMY_NOTARY, DUMMY_NOTARY_KEY, notary, ::Random).fillWithSomeTestCash(2000.DOLLARS, bankNode.services, 3, 10, issuer) + VaultFiller(bobNode.services, dummyNotary, notary, ::Random).fillWithSomeTestCash(2000.DOLLARS, bankNode.services, 3, 10, issuer) } val alicesFakePaper = aliceNode.database.transaction { fillUpForSeller(false, issuer, alice, diff --git a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt index 78539f9c39..7b08ece646 100644 --- a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt @@ -38,16 +38,16 @@ class NotaryChangeTests { @Before fun setUp() { - val oldNotaryName = DUMMY_REGULATOR.name + val oldNotaryName = CordaX500Name("Regulator A", "Paris", "FR") mockNet = MockNetwork( - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name), NotarySpec(oldNotaryName)), + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME), NotarySpec(oldNotaryName)), cordappPackages = listOf("net.corda.testing.contracts") ) clientNodeA = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME)) clientNodeB = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME)) clientA = clientNodeA.info.singleIdentity() oldNotaryNode = mockNet.notaryNodes[1] - newNotaryParty = clientNodeA.services.networkMapCache.getNotary(DUMMY_NOTARY.name)!! + newNotaryParty = clientNodeA.services.networkMapCache.getNotary(DUMMY_NOTARY_NAME)!! oldNotaryParty = clientNodeA.services.networkMapCache.getNotary(oldNotaryName)!! } diff --git a/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt b/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt index 65b85a51cf..f04ca1ec4d 100644 --- a/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt @@ -1,8 +1,7 @@ package net.corda.node.services.config import net.corda.core.utilities.NetworkHostAndPort -import net.corda.nodeapi.internal.persistence.DatabaseConfig -import net.corda.testing.ALICE +import net.corda.testing.ALICE_NAME import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test @@ -34,11 +33,11 @@ class NodeConfigurationImplTest { private val testConfiguration = NodeConfigurationImpl( baseDirectory = Paths.get("."), - myLegalName = ALICE.name, + myLegalName = ALICE_NAME, emailAddress = "", keyStorePassword = "cordacadevpass", trustStorePassword = "trustpass", - dataSourceProperties = makeTestDataSourceProperties(ALICE.name.organisation), + dataSourceProperties = makeTestDataSourceProperties(ALICE_NAME.organisation), rpcUsers = emptyList(), verifierType = VerifierType.InMemory, useHTTPS = false, diff --git a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt index c73f0a893a..576d55d870 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt @@ -15,6 +15,7 @@ import net.corda.core.node.NodeInfo import net.corda.core.node.ServiceHub import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.transactions.TransactionBuilder +import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.days import net.corda.node.internal.FlowStarterImpl import net.corda.node.internal.cordapp.CordappLoader @@ -49,9 +50,11 @@ import java.util.concurrent.TimeUnit import kotlin.test.assertTrue class NodeSchedulerServiceTest : SingletonSerializeAsToken() { - companion object { - private val DUMMY_IDENTITY_1 = getTestPartyAndCertificate(Party(CordaX500Name("Dummy", "Madrid", "ES"), generateKeyPair().public)) - private val myInfo = NodeInfo(listOf(MOCK_HOST_AND_PORT), listOf(DUMMY_IDENTITY_1), 1, serial = 1L) + private companion object { + val ALICE_KEY = TestIdentity(ALICE_NAME, 70).key + val DUMMY_IDENTITY_1 = getTestPartyAndCertificate(Party(CordaX500Name("Dummy", "Madrid", "ES"), generateKeyPair().public)) + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party + val myInfo = NodeInfo(listOf(NetworkHostAndPort("mockHost", 30000)), listOf(DUMMY_IDENTITY_1), 1, serial = 1L) } @Rule diff --git a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt index 5ffac70560..46a5dc9db0 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt @@ -22,8 +22,16 @@ import kotlin.test.assertNull * Tests for the in memory identity service. */ class InMemoryIdentityServiceTests { - companion object { - private fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities.toSet(), DEV_TRUST_ROOT) + private companion object { + val alice = TestIdentity(ALICE_NAME, 70) + val bob = TestIdentity(BOB_NAME, 80) + val ALICE get() = alice.party + val ALICE_IDENTITY get() = alice.identity + val ALICE_PUBKEY get() = alice.pubkey + val BOB get() = bob.party + val BOB_IDENTITY get() = bob.identity + val BOB_PUBKEY get() = bob.pubkey + fun createService(vararg identities: PartyAndCertificate) = InMemoryIdentityService(identities.toSet(), DEV_TRUST_ROOT) } @Test diff --git a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt index 0df6386b8b..9b50b7f5b0 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt @@ -30,6 +30,17 @@ import kotlin.test.assertNull * Tests for the in memory identity service. */ class PersistentIdentityServiceTests { + private companion object { + val alice = TestIdentity(ALICE_NAME, 70) + val bob = TestIdentity(BOB_NAME, 80) + val ALICE get() = alice.party + val ALICE_IDENTITY get() = alice.identity + val ALICE_PUBKEY get() = alice.pubkey + val BOB get() = bob.party + val BOB_IDENTITY get() = bob.identity + val BOB_PUBKEY get() = bob.pubkey + } + private lateinit var database: CordaPersistence private lateinit var identityService: IdentityService diff --git a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt index a915820f5f..51db2b72c4 100644 --- a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt @@ -62,7 +62,7 @@ class ArtemisMessagingTests { securityManager = RPCSecurityManagerImpl.fromUserList(users = emptyList(), id = AuthServiceId("TEST")) config = testNodeConfiguration( baseDirectory = temporaryFolder.root.toPath(), - myLegalName = ALICE.name) + myLegalName = ALICE_NAME) LogHelper.setLevel(PersistentUniquenessProvider::class) database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock()) networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, emptyList()), rigorousMock()) diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt index f955fd92d0..e495f12f72 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt @@ -23,6 +23,11 @@ import java.util.concurrent.TimeUnit import kotlin.test.assertEquals class DBTransactionStorageTests { + private companion object { + val ALICE_PUBKEY = TestIdentity(ALICE_NAME, 70).pubkey + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party + } + @Rule @JvmField val testSerialization = SerializationEnvironmentRule() diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt index b29a7f0d69..e323c98851 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt @@ -6,7 +6,6 @@ import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateRef import net.corda.core.contracts.TransactionState import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name @@ -48,7 +47,6 @@ import org.assertj.core.api.Assertions.assertThat import org.hibernate.SessionFactory import org.junit.* import java.math.BigDecimal -import java.math.BigInteger import java.time.Instant import java.util.* import javax.persistence.EntityManager @@ -57,7 +55,13 @@ import javax.persistence.criteria.CriteriaBuilder class HibernateConfigurationTest { private companion object { + val ALICE = TestIdentity(ALICE_NAME, 70).party + val bankOfCorda = TestIdentity(BOC_NAME) + val CHARLIE = TestIdentity(CHARLIE_NAME, 90).party val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10) + val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20) + val BOC get() = bankOfCorda.party + val BOC_KEY get() = bankOfCorda.key } @Rule @@ -91,14 +95,14 @@ class HibernateConfigurationTest { val cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset") bankServices = MockServices(cordappPackages, rigorousMock(), BOC.name, BOC_KEY) issuerServices = MockServices(cordappPackages, rigorousMock(), dummyCashIssuer) - notaryServices = MockServices(cordappPackages, rigorousMock(), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) + notaryServices = MockServices(cordappPackages, rigorousMock(), dummyNotary) notary = notaryServices.myInfo.singleIdentity() val dataSourceProps = makeTestDataSourceProperties() val identityService = rigorousMock().also { mock -> doReturn(null).whenever(mock).wellKnownPartyFromAnonymous(any()) - listOf(dummyCashIssuer.party, DUMMY_NOTARY).forEach { - doReturn(it).whenever(mock).wellKnownPartyFromAnonymous(it) - doReturn(it).whenever(mock).wellKnownPartyFromX500Name(it.name) + listOf(dummyCashIssuer, dummyNotary).forEach { + doReturn(it.party).whenever(mock).wellKnownPartyFromAnonymous(it.party) + doReturn(it.party).whenever(mock).wellKnownPartyFromX500Name(it.name) } } val schemaService = NodeSchemaService() @@ -108,7 +112,7 @@ class HibernateConfigurationTest { // `consumeCash` expects we can self-notarise transactions services = object : MockServices(cordappPackages, rigorousMock().also { doNothing().whenever(it).justVerifyAndRegisterIdentity(argThat { name == BOB_NAME }) - }, BOB_NAME, generateKeyPair(), DUMMY_NOTARY_KEY) { + }, BOB_NAME, generateKeyPair(), dummyNotary.key) { override val vaultService = makeVaultService(database.hibernateConfig, schemaService) override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable) { for (stx in txs) { @@ -120,7 +124,7 @@ class HibernateConfigurationTest { override fun jdbcSession() = database.createSession() } - vaultFiller = VaultFiller(services, DUMMY_NOTARY, DUMMY_NOTARY_KEY, notary, ::Random) + vaultFiller = VaultFiller(services, dummyNotary, notary, ::Random) hibernatePersister = services.hibernatePersister } diff --git a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt index 5f12128d53..16fa18453a 100644 --- a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt @@ -6,6 +6,7 @@ import net.corda.core.contracts.StateRef import net.corda.core.contracts.TransactionState import net.corda.core.crypto.SecureHash import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name import net.corda.core.node.services.Vault import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState @@ -15,7 +16,7 @@ import net.corda.node.internal.configureDatabase import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseTransactionManager import net.corda.testing.LogHelper -import net.corda.testing.MEGA_CORP +import net.corda.testing.TestIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.rigorousMock @@ -68,6 +69,7 @@ class HibernateObserverTests { val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock(), schemaService) HibernateObserver.install(rawUpdatesPublisher, database.hibernateConfig, schemaService) database.transaction { + val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party rawUpdatesPublisher.onNext(Vault.Update(emptySet(), setOf(StateAndRef(TransactionState(TestState(), DummyContract.PROGRAM_ID, MEGA_CORP), StateRef(SecureHash.sha256("dummy"), 0))))) val parentRowCountResult = DatabaseTransactionManager.current().connection.prepareStatement("select count(*) from Parents").executeQuery() parentRowCountResult.next() diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt index 75f7b88fd4..1c26932b19 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt @@ -1,6 +1,7 @@ package net.corda.node.services.transactions import net.corda.core.crypto.SecureHash +import net.corda.core.identity.CordaX500Name import net.corda.core.node.services.UniquenessException import net.corda.node.internal.configureDatabase import net.corda.nodeapi.internal.persistence.CordaPersistence @@ -18,8 +19,7 @@ class PersistentUniquenessProviderTests { @Rule @JvmField val testSerialization = SerializationEnvironmentRule() - - private val identity = MEGA_CORP + private val identity = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party private val txID = SecureHash.randomSHA256() private lateinit var database: CordaPersistence diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt index 42a754ce01..7e55d36562 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt @@ -5,6 +5,7 @@ import net.corda.core.contracts.Command import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateRef import net.corda.core.crypto.TransactionSignature +import net.corda.core.crypto.generateKeyPair import net.corda.core.flows.NotaryError import net.corda.core.flows.NotaryException import net.corda.core.flows.NotaryFlow @@ -68,7 +69,7 @@ class ValidatingNotaryServiceTests { @Test fun `should report error for missing signatures`() { - val expectedMissingKey = MEGA_CORP_KEY.public + val expectedMissingKey = generateKeyPair().public val stx = run { val inputState = issueState(aliceServices, alice) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt index 281a222234..0a281f0713 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt @@ -53,8 +53,22 @@ class NodeVaultServiceTest { private companion object { val cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName) val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10) - val DUMMY_CASH_ISSUER_IDENTITY get() = dummyCashIssuer.identity val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1) + val bankOfCorda = TestIdentity(BOC_NAME) + val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20) + val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) + val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) + val BOC get() = bankOfCorda.party + val BOC_IDENTITY get() = bankOfCorda.identity + val DUMMY_CASH_ISSUER_IDENTITY get() = dummyCashIssuer.identity + val DUMMY_NOTARY get() = dummyNotary.party + val DUMMY_NOTARY_IDENTITY get() = dummyNotary.identity + val MEGA_CORP get() = megaCorp.party + val MEGA_CORP_KEY get() = megaCorp.key + val MEGA_CORP_PUBKEY get() = megaCorp.pubkey + val MEGA_CORP_IDENTITY get() = megaCorp.identity + val MINI_CORP get() = miniCorp.party + val MINI_CORP_IDENTITY get() = miniCorp.identity } @Rule @@ -78,11 +92,11 @@ class NodeVaultServiceTest { MEGA_CORP.name) database = databaseAndServices.first services = databaseAndServices.second - vaultFiller = VaultFiller(services, DUMMY_NOTARY, DUMMY_NOTARY_KEY) + vaultFiller = VaultFiller(services, dummyNotary) // This is safe because MockServices only ever have a single identity identity = services.myInfo.singleIdentityAndCert() issuerServices = MockServices(cordappPackages, rigorousMock(), dummyCashIssuer) - bocServices = MockServices(cordappPackages, rigorousMock(), BOC_NAME, BOC_KEY) + bocServices = MockServices(cordappPackages, rigorousMock(), bankOfCorda) services.identityService.verifyAndRegisterIdentity(DUMMY_CASH_ISSUER_IDENTITY) services.identityService.verifyAndRegisterIdentity(BOC_IDENTITY) } diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index 95c3c6d0a1..c27667d52b 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -2,12 +2,9 @@ package net.corda.node.services.vault import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.toStringShort import net.corda.core.identity.CordaX500Name -import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.packageName import net.corda.core.node.services.* import net.corda.core.node.services.vault.* @@ -39,8 +36,6 @@ import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.* import org.junit.rules.ExpectedException import java.lang.Thread.sleep -import java.math.BigInteger -import java.security.KeyPair import java.time.Instant import java.time.LocalDate import java.time.ZoneOffset @@ -49,9 +44,40 @@ import java.util.* class VaultQueryTests { private companion object { + val alice = TestIdentity(ALICE_NAME, 70) + val bankOfCorda = TestIdentity(BOC_NAME) + val bigCorp = TestIdentity(CordaX500Name("BigCorporation", "New York", "US")) + val bob = TestIdentity(BOB_NAME, 80) + val cashNotary = TestIdentity(CordaX500Name("Cash Notary Service", "Zurich", "CH"), 21) + val charlie = TestIdentity(CHARLIE_NAME, 90) val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10) val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1) + val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20) val DUMMY_OBLIGATION_ISSUER = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10).party + val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) + val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) + val ALICE get() = alice.party + val ALICE_IDENTITY get() = alice.identity + val BIG_CORP get() = bigCorp.party + val BIG_CORP_IDENTITY get() = bigCorp.identity + val BOB get() = bob.party + val BOB_IDENTITY get() = bob.identity + val BOC get() = bankOfCorda.party + val BOC_IDENTITY get() = bankOfCorda.identity + val BOC_KEY get() = bankOfCorda.key + val BOC_PUBKEY get() = bankOfCorda.pubkey + val CASH_NOTARY get() = cashNotary.party + val CASH_NOTARY_IDENTITY get() = cashNotary.identity + val CHARLIE get() = charlie.party + val CHARLIE_IDENTITY get() = charlie.identity + val DUMMY_NOTARY get() = dummyNotary.party + val DUMMY_NOTARY_KEY get() = dummyNotary.key + val MEGA_CORP_IDENTITY get() = megaCorp.identity + val MEGA_CORP_PUBKEY get() = megaCorp.pubkey + val MEGA_CORP_KEY get() = megaCorp.key + val MEGA_CORP get() = megaCorp.party + val MINI_CORP_IDENTITY get() = miniCorp.identity + val MINI_CORP get() = miniCorp.party } @Rule @@ -74,25 +100,19 @@ class VaultQueryTests { private val vaultService: VaultService get() = services.vaultService private lateinit var identitySvc: IdentityService private lateinit var database: CordaPersistence - - // test cash notary - private val CASH_NOTARY_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(21)) } - private val CASH_NOTARY: Party get() = Party(CordaX500Name(organisation = "Cash Notary Service", locality = "Zurich", country = "CH"), CASH_NOTARY_KEY.public) - private val CASH_NOTARY_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(CASH_NOTARY.nameOrNull(), CASH_NOTARY_KEY.public) - @Before fun setUp() { // register additional identities val databaseAndServices = makeTestDatabaseAndMockServices( listOf(MEGA_CORP_KEY, DUMMY_NOTARY_KEY), - makeTestIdentityService(listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, DUMMY_NOTARY_IDENTITY)), + makeTestIdentityService(listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, dummyNotary.identity)), cordappPackages, MEGA_CORP.name) database = databaseAndServices.first services = databaseAndServices.second - vaultFiller = VaultFiller(services, DUMMY_NOTARY, DUMMY_NOTARY_KEY) - vaultFillerCashNotary = VaultFiller(services, DUMMY_NOTARY, DUMMY_NOTARY_KEY, CASH_NOTARY) - notaryServices = MockServices(cordappPackages, rigorousMock(), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY, dummyCashIssuer.key, BOC_KEY, MEGA_CORP_KEY) + vaultFiller = VaultFiller(services, dummyNotary) + vaultFillerCashNotary = VaultFiller(services, dummyNotary, CASH_NOTARY) + notaryServices = MockServices(cordappPackages, rigorousMock(), dummyNotary, dummyCashIssuer.key, BOC_KEY, MEGA_CORP_KEY) identitySvc = services.identityService // Register all of the identities we're going to use (notaryServices.myInfo.legalIdentitiesAndCerts + BOC_IDENTITY + CASH_NOTARY_IDENTITY + MINI_CORP_IDENTITY + MEGA_CORP_IDENTITY).forEach { identity -> diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt index 3a0d957869..34ef9e5203 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt @@ -40,8 +40,17 @@ import kotlin.test.fail class VaultWithCashTest { private companion object { val cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", CashSchemaV1::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) + val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20) + val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) + val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) + val DUMMY_NOTARY get() = dummyNotary.party + val MEGA_CORP get() = megaCorp.party + val MEGA_CORP_IDENTITY get() = megaCorp.identity + val MEGA_CORP_KEY get() = megaCorp.key + val MINI_CORP_IDENTITY get() = miniCorp.identity } @Rule @@ -59,15 +68,15 @@ class VaultWithCashTest { fun setUp() { LogHelper.setLevel(VaultWithCashTest::class) val databaseAndServices = makeTestDatabaseAndMockServices( - listOf(generateKeyPair(), DUMMY_NOTARY_KEY), - makeTestIdentityService(listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, DUMMY_NOTARY_IDENTITY)), + listOf(generateKeyPair(), dummyNotary.key), + makeTestIdentityService(listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, dummyNotary.identity)), cordappPackages, MEGA_CORP.name) database = databaseAndServices.first services = databaseAndServices.second - vaultFiller = VaultFiller(services, DUMMY_NOTARY, DUMMY_NOTARY_KEY) + vaultFiller = VaultFiller(services, dummyNotary) issuerServices = MockServices(cordappPackages, rigorousMock(), dummyCashIssuer, MEGA_CORP_KEY) - notaryServices = MockServices(cordappPackages, rigorousMock(), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) + notaryServices = MockServices(cordappPackages, rigorousMock(), dummyNotary) notary = notaryServices.myInfo.legalIdentitiesAndCerts.single().party } diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt index 8c76a0ad82..dec45410ef 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt @@ -9,9 +9,11 @@ import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name import net.corda.core.internal.* import net.corda.node.services.config.NodeConfiguration -import net.corda.node.services.config.createKeystoreForCordaNode import net.corda.nodeapi.internal.crypto.* -import net.corda.testing.ALICE +import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.getX509Certificate +import net.corda.nodeapi.internal.crypto.loadKeyStore +import net.corda.testing.ALICE_NAME import net.corda.testing.rigorousMock import net.corda.testing.testNodeConfiguration import org.assertj.core.api.Assertions.assertThatThrownBy @@ -43,7 +45,7 @@ class NetworkRegistrationHelperTest { @Before fun init() { - config = testNodeConfiguration(baseDirectory = tempFolder.root.toPath(), myLegalName = ALICE.name) + config = testNodeConfiguration(baseDirectory = tempFolder.root.toPath(), myLegalName = ALICE_NAME) } @Test diff --git a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt index d79c5560f9..f2bd4207d3 100644 --- a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt +++ b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt @@ -5,8 +5,8 @@ import net.corda.core.utilities.getOrThrow import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User -import net.corda.testing.DUMMY_BANK_A -import net.corda.testing.DUMMY_BANK_B +import net.corda.testing.DUMMY_BANK_A_NAME +import net.corda.testing.DUMMY_BANK_B_NAME import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver import org.junit.Test @@ -27,8 +27,8 @@ class AttachmentDemoTest { invokeRpc(CordaRPCOps::internalVerifiedTransactionsFeed) ))) val (nodeA, nodeB) = listOf( - startNode(providedName = DUMMY_BANK_A.name, rpcUsers = demoUser, maximumHeapSize = "1g"), - startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser, maximumHeapSize = "1g") + startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = demoUser, maximumHeapSize = "1g"), + startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = demoUser, maximumHeapSize = "1g") ).map { it.getOrThrow() } startWebserver(nodeB).getOrThrow() diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt index e048bc791e..05f6f3c51c 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt @@ -23,8 +23,8 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow -import net.corda.testing.DUMMY_BANK_B -import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.DUMMY_BANK_B_NAME +import net.corda.testing.DUMMY_NOTARY_NAME import net.corda.testing.internal.poll import java.io.InputStream import java.net.HttpURLConnection @@ -88,9 +88,8 @@ fun sender(rpc: CordaRPCOps, numOfClearBytes: Int = 1024) { // default size 1K. private fun sender(rpc: CordaRPCOps, inputStream: InputStream, hash: SecureHash.SHA256, executor: ScheduledExecutorService) { // Get the identity key of the other side (the recipient). - val notaryFuture: CordaFuture = poll(executor, DUMMY_NOTARY.name.toString()) { rpc.wellKnownPartyFromX500Name(DUMMY_NOTARY.name) } - val otherSideFuture: CordaFuture = poll(executor, DUMMY_BANK_B.name.toString()) { rpc.wellKnownPartyFromX500Name(DUMMY_BANK_B.name) } - + val notaryFuture: CordaFuture = poll(executor, DUMMY_NOTARY_NAME.toString()) { rpc.wellKnownPartyFromX500Name(DUMMY_NOTARY_NAME) } + val otherSideFuture: CordaFuture = poll(executor, DUMMY_BANK_B_NAME.toString()) { rpc.wellKnownPartyFromX500Name(DUMMY_BANK_B_NAME) } // Make sure we have the file in storage if (!rpc.attachmentExists(hash)) { inputStream.use { diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt index 9c3b8a2304..9f8c834519 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt @@ -2,8 +2,8 @@ package net.corda.attachmentdemo import net.corda.core.internal.div import net.corda.nodeapi.internal.config.User -import net.corda.testing.DUMMY_BANK_A -import net.corda.testing.DUMMY_BANK_B +import net.corda.testing.DUMMY_BANK_A_NAME +import net.corda.testing.DUMMY_BANK_B_NAME import net.corda.testing.driver.driver /** @@ -13,7 +13,7 @@ import net.corda.testing.driver.driver fun main(args: Array) { val demoUser = listOf(User("demo", "demo", setOf("StartFlow.net.corda.flows.FinalityFlow"))) driver(isDebug = true, driverDirectory = "build" / "attachment-demo-nodes", waitForAllNodesToFinish = true) { - startNode(providedName = DUMMY_BANK_A.name, rpcUsers = demoUser) - startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser) + startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = demoUser) + startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = demoUser) } } diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt index d53cecafe9..268195d935 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt @@ -4,6 +4,7 @@ import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.startFlow import net.corda.core.node.services.Vault import net.corda.core.node.services.vault.QueryCriteria +import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.finance.DOLLARS import net.corda.finance.contracts.asset.Cash @@ -28,7 +29,7 @@ class BankOfCordaRPCClientTest { startFlow()) + commonPermissions) val bigCorpCFO = User("bigCorpCFO", "password2", permissions = emptySet() + commonPermissions) val (nodeBankOfCorda, nodeBigCorporation) = listOf( - startNode(providedName = BOC.name, rpcUsers = listOf(bocManager)), + startNode(providedName = BOC_NAME, rpcUsers = listOf(bocManager)), startNode(providedName = BIGCORP_NAME, rpcUsers = listOf(bigCorpCFO)) ).map { it.getOrThrow() } @@ -52,7 +53,7 @@ class BankOfCordaRPCClientTest { // Kick-off actual Issuer Flow val anonymous = true bocProxy.startFlow(::CashIssueAndPaymentFlow, - 1000.DOLLARS, BIG_CORP_PARTY_REF, + 1000.DOLLARS, OpaqueBytes.of(1), bigCorporation, anonymous, defaultNotaryIdentity).returnValue.getOrThrow() diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt index 0e286a14a0..93b7843843 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt @@ -13,7 +13,7 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.config.NotaryConfig import net.corda.nodeapi.internal.config.User -import net.corda.testing.BOC +import net.corda.testing.BOC_NAME import net.corda.testing.internal.demorun.* import java.util.* import kotlin.system.exitProcess @@ -33,7 +33,7 @@ class BankOfCordaCordform : CordformDefinition() { rpcPort(10003) } node { - name(CordaX500Name(organisation = "BankOfCorda", locality = "London", country = "GB")) + name(BOC_NAME) extraConfig = mapOf("issuableCurrencies" to listOf("USD")) p2pPort(10005) rpcPort(BOC_RPC_PORT) @@ -101,7 +101,7 @@ object IssueCash { } private fun createParams(amount: Amount, notaryName: CordaX500Name): IssueRequestParams { - return IssueRequestParams(amount, BIGCORP_NAME, "1", BOC.name, notaryName) + return IssueRequestParams(amount, BIGCORP_NAME, "1", BOC_NAME, notaryName) } private fun printHelp(parser: OptionParser) { diff --git a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/Main.kt b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/Main.kt index 91315e3e9f..976126a8c8 100644 --- a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/Main.kt +++ b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/Main.kt @@ -1,8 +1,8 @@ package net.corda.irs import net.corda.core.utilities.getOrThrow -import net.corda.testing.DUMMY_BANK_A -import net.corda.testing.DUMMY_BANK_B +import net.corda.testing.DUMMY_BANK_A_NAME +import net.corda.testing.DUMMY_BANK_B_NAME import net.corda.testing.driver.driver /** @@ -12,8 +12,8 @@ import net.corda.testing.driver.driver fun main(args: Array) { driver(useTestClock = true, isDebug = true, waitForAllNodesToFinish = true) { val (nodeA, nodeB) = listOf( - startNode(providedName = DUMMY_BANK_A.name), - startNode(providedName = DUMMY_BANK_B.name) + startNode(providedName = DUMMY_BANK_A_NAME), + startNode(providedName = DUMMY_BANK_B_NAME) ).map { it.getOrThrow() } val controller = defaultNotaryNode.getOrThrow() diff --git a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index a42a210a6d..11d6e8ac1b 100644 --- a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -3,6 +3,7 @@ package net.corda.irs.api import net.corda.core.contracts.Command import net.corda.core.contracts.ContractState import net.corda.core.contracts.TransactionState +import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.transactions.TransactionBuilder @@ -32,6 +33,14 @@ import kotlin.test.assertFailsWith import kotlin.test.assertFalse class NodeInterestRatesTest { + private companion object { + val alice = TestIdentity(ALICE_NAME, 70) + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party + val MEGA_CORP_KEY = generateKeyPair() + val ALICE get() = alice.party + val ALICE_PUBKEY get() = alice.pubkey + } + @Rule @JvmField val testSerialization = SerializationEnvironmentRule() diff --git a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/contract/IRSTests.kt b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/contract/IRSTests.kt index 5e0f0971bb..b796a308d9 100644 --- a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/contract/IRSTests.kt +++ b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/contract/IRSTests.kt @@ -34,6 +34,17 @@ import java.util.* import kotlin.test.assertEquals private val DUMMY_PARTY = Party(CordaX500Name("Dummy", "Madrid", "ES"), generateKeyPair().public) +private val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20) +private val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) +private val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) +private val ORACLE_PUBKEY = TestIdentity(CordaX500Name("Oracle", "London", "GB")).pubkey +private val DUMMY_NOTARY get() = dummyNotary.party +private val DUMMY_NOTARY_KEY get() = dummyNotary.key +private val MEGA_CORP get() = megaCorp.party +private val MEGA_CORP_KEY get() = megaCorp.key +private val MEGA_CORP_PUBKEY get() = megaCorp.pubkey +private val MINI_CORP get() = miniCorp.party +private val MINI_CORP_KEY get() = miniCorp.key fun createDummyIRS(irsSelect: Int): InterestRateSwap.State { return when (irsSelect) { 1 -> { diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt index 6b53396fa4..90df9427a3 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt @@ -47,13 +47,13 @@ class IRSDemoTest { fun `runs IRS demo`() { springDriver( useTestClock = true, - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name, rpcUsers = rpcUsers)), + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, rpcUsers = rpcUsers)), isDebug = true, extraCordappPackagesToScan = listOf("net.corda.irs") ) { val (nodeA, nodeB) = listOf( - startNode(providedName = DUMMY_BANK_A.name, rpcUsers = rpcUsers), - startNode(providedName = DUMMY_BANK_B.name, rpcUsers = rpcUsers) + startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = rpcUsers), + startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = rpcUsers) ).map { it.getOrThrow() } val controller = defaultNotaryNode.getOrThrow() diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt index 033c18b8a9..50a7f2b356 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt @@ -9,7 +9,7 @@ import net.corda.finance.utils.CityDatabase import net.corda.irs.api.NodeInterestRates import net.corda.node.internal.StartedNode import net.corda.node.services.statemachine.StateMachineManager -import net.corda.testing.DUMMY_REGULATOR +import net.corda.testing.TestIdentity import net.corda.testing.node.* import net.corda.testing.node.MockNetwork.MockNode import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties @@ -35,11 +35,12 @@ internal val MockNode.place get() = configuration.myLegalName.locality.let { Cit abstract class Simulation(val networkSendManuallyPumped: Boolean, runAsync: Boolean, latencyInjector: InMemoryMessagingNetwork.LatencyCalculator?) { - companion object { - private val defaultParams // The get() is necessary so that entropyRoot isn't shared. + private companion object { + val defaultParams // The get() is necessary so that entropyRoot isn't shared. get() = MockNodeParameters(configOverrides = { doReturn(makeTestDataSourceProperties(it.myLegalName.organisation)).whenever(it).dataSourceProperties }) + val DUMMY_REGULATOR = TestIdentity(CordaX500Name("Regulator A", "Paris", "FR"), 100).party } init { diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt index 6735271686..57d2e9e28a 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt @@ -11,8 +11,8 @@ import net.corda.node.services.config.NotaryConfig import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.minCorrectReplicas import net.corda.nodeapi.internal.ServiceIdentityGenerator -import net.corda.testing.ALICE -import net.corda.testing.BOB +import net.corda.testing.ALICE_NAME +import net.corda.testing.BOB_NAME import net.corda.testing.internal.demorun.* import java.nio.file.Paths @@ -29,13 +29,13 @@ class BFTNotaryCordform : CordformDefinition() { init { nodesDirectory = Paths.get("build", "nodes", "nodesBFT") node { - name(ALICE.name) + name(ALICE_NAME) p2pPort(10002) rpcPort(10003) rpcUsers(notaryDemoUser) } node { - name(BOB.name) + name(BOB_NAME) p2pPort(10005) rpcPort(10006) } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt index 8117ef9a60..5e5aa47d34 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt @@ -3,9 +3,7 @@ package net.corda.notarydemo import net.corda.cordform.CordformContext import net.corda.cordform.CordformDefinition import net.corda.node.services.config.NotaryConfig -import net.corda.testing.ALICE -import net.corda.testing.BOB -import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.* import net.corda.testing.internal.demorun.* import java.nio.file.Paths @@ -15,18 +13,18 @@ class CustomNotaryCordform : CordformDefinition() { init { nodesDirectory = Paths.get("build", "nodes", "nodesCustom") node { - name(ALICE.name) + name(ALICE_NAME) p2pPort(10002) rpcPort(10003) rpcUsers(notaryDemoUser) } node { - name(BOB.name) + name(BOB_NAME) p2pPort(10005) rpcPort(10006) } node { - name(DUMMY_NOTARY.name) + name(DUMMY_NOTARY_NAME) p2pPort(10009) rpcPort(10010) notary(NotaryConfig(validating = true, custom = true)) diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt index cabacf9476..6a59809cf6 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt @@ -10,7 +10,7 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow import net.corda.notarydemo.flows.DummyIssueAndMove import net.corda.notarydemo.flows.RPCStartableNotaryFlowClient -import net.corda.testing.BOB +import net.corda.testing.BOB_NAME import java.util.concurrent.Future fun main(args: Array) { @@ -31,7 +31,7 @@ private class NotaryDemoClientApi(val rpc: CordaRPCOps) { private val counterparty by lazy { val parties = rpc.networkMapSnapshot() parties.fold(ArrayList()) { acc, elem -> - acc.addAll(elem.legalIdentitiesAndCerts.filter { it.name == BOB.name }) + acc.addAll(elem.legalIdentitiesAndCerts.filter { it.name == BOB_NAME }) acc }.single().party } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt index 4ba0b53210..23acaeceac 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt @@ -4,15 +4,14 @@ import net.corda.cordform.CordformContext import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.div import net.corda.core.node.services.NotaryService import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.RaftConfig import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.nodeapi.internal.ServiceIdentityGenerator -import net.corda.testing.ALICE -import net.corda.testing.BOB +import net.corda.testing.ALICE_NAME +import net.corda.testing.BOB_NAME import net.corda.testing.internal.demorun.* import java.nio.file.Paths @@ -30,13 +29,13 @@ class RaftNotaryCordform : CordformDefinition() { init { nodesDirectory = Paths.get("build", "nodes", "nodesRaft") node { - name(ALICE.name) + name(ALICE_NAME) p2pPort(10002) rpcPort(10003) rpcUsers(notaryDemoUser) } node { - name(BOB.name) + name(BOB_NAME) p2pPort(10005) rpcPort(10006) } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt index ea2366c953..85732133f5 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt @@ -5,9 +5,7 @@ import net.corda.cordform.CordformDefinition import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.config.NotaryConfig import net.corda.nodeapi.internal.config.User -import net.corda.testing.ALICE -import net.corda.testing.BOB -import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.* import net.corda.testing.internal.demorun.* import java.nio.file.Paths @@ -21,18 +19,18 @@ class SingleNotaryCordform : CordformDefinition() { init { nodesDirectory = Paths.get("build", "nodes", "nodesSingle") node { - name(ALICE.name) + name(ALICE_NAME) p2pPort(10002) rpcPort(10003) rpcUsers(notaryDemoUser) } node { - name(BOB.name) + name(BOB_NAME) p2pPort(10005) rpcPort(10006) } node { - name(DUMMY_NOTARY.name) + name(DUMMY_NOTARY_NAME) p2pPort(10009) rpcPort(10010) notary(NotaryConfig(validating = true)) diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index be6c0e17ff..412baeae0c 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -3,8 +3,8 @@ package net.corda.vega import com.opengamma.strata.product.common.BuySell import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.getOrThrow -import net.corda.testing.DUMMY_BANK_A -import net.corda.testing.DUMMY_BANK_B +import net.corda.testing.DUMMY_BANK_A_NAME +import net.corda.testing.DUMMY_BANK_B_NAME import net.corda.testing.driver.driver import net.corda.testing.http.HttpApi import net.corda.vega.api.PortfolioApi @@ -20,8 +20,8 @@ class SimmValuationTest { private companion object { // SIMM demo can only currently handle one valuation date due to a lack of market data or a market data source. val valuationDate: LocalDate = LocalDate.parse("2016-06-06") - val nodeALegalName = DUMMY_BANK_A.name - val nodeBLegalName = DUMMY_BANK_B.name + val nodeALegalName = DUMMY_BANK_A_NAME + val nodeBLegalName = DUMMY_BANK_B_NAME val testTradeId = "trade1" } diff --git a/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt b/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt index 41d82323bd..3bf26cef2a 100644 --- a/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt +++ b/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt @@ -1,9 +1,7 @@ package net.corda.vega import net.corda.core.utilities.getOrThrow -import net.corda.testing.DUMMY_BANK_A -import net.corda.testing.DUMMY_BANK_B -import net.corda.testing.DUMMY_BANK_C +import net.corda.testing.* import net.corda.testing.driver.driver /** @@ -14,9 +12,9 @@ import net.corda.testing.driver.driver fun main(args: Array) { driver(isDebug = true, waitForAllNodesToFinish = true) { val (nodeA, nodeB, nodeC) = listOf( - startNode(providedName = DUMMY_BANK_A.name), - startNode(providedName = DUMMY_BANK_B.name), - startNode(providedName = DUMMY_BANK_C.name) + startNode(providedName = DUMMY_BANK_A_NAME), + startNode(providedName = DUMMY_BANK_B_NAME), + startNode(providedName = DUMMY_BANK_C_NAME) ).map { it.getOrThrow() } startWebserver(nodeA) diff --git a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index 4283f061be..582b5efacf 100644 --- a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -9,10 +9,7 @@ import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User -import net.corda.testing.BOC -import net.corda.testing.DUMMY_BANK_A -import net.corda.testing.DUMMY_BANK_B -import net.corda.testing.chooseIdentity +import net.corda.testing.* import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver import net.corda.testing.internal.poll @@ -34,9 +31,9 @@ class TraderDemoTest { all())) driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance")) { val (nodeA, nodeB, bankNode) = listOf( - startNode(providedName = DUMMY_BANK_A.name, rpcUsers = listOf(demoUser)), - startNode(providedName = DUMMY_BANK_B.name, rpcUsers = listOf(demoUser)), - startNode(providedName = BOC.name, rpcUsers = listOf(bankUser)) + startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser)), + startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser)), + startNode(providedName = BOC_NAME, rpcUsers = listOf(bankUser)) ).map { (it.getOrThrow() as NodeHandle.InProcess).node } nodeA.registerInitiatedFlow(BuyerFlow::class.java) val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map { diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemo.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemo.kt index e519481e3a..7766be2a19 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemo.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemo.kt @@ -5,8 +5,8 @@ import net.corda.client.rpc.CordaRPCClient import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.finance.DOLLARS -import net.corda.testing.DUMMY_BANK_A -import net.corda.testing.DUMMY_BANK_B +import net.corda.testing.DUMMY_BANK_A_NAME +import net.corda.testing.DUMMY_BANK_B_NAME import kotlin.system.exitProcess /** @@ -24,8 +24,8 @@ private class TraderDemo { companion object { private val logger = contextLogger() - val buyerName = DUMMY_BANK_A.name - val sellerName = DUMMY_BANK_B.name + val buyerName = DUMMY_BANK_A_NAME + val sellerName = DUMMY_BANK_B_NAME val sellerRpcPort = 10009 val bankRpcPort = 10012 } diff --git a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt index b59890ab61..c3314ff155 100644 --- a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt +++ b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt @@ -5,9 +5,9 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User -import net.corda.testing.BOC -import net.corda.testing.DUMMY_BANK_A -import net.corda.testing.DUMMY_BANK_B +import net.corda.testing.BOC_NAME +import net.corda.testing.DUMMY_BANK_A_NAME +import net.corda.testing.DUMMY_BANK_B_NAME import net.corda.testing.driver.driver import net.corda.traderdemo.flow.CommercialPaperIssueFlow import net.corda.traderdemo.flow.SellerFlow @@ -26,8 +26,8 @@ fun main(args: Array) { val user = User("user1", "test", permissions = setOf(startFlow(), startFlow(), startFlow())) - startNode(providedName = DUMMY_BANK_A.name, rpcUsers = demoUser) - startNode(providedName = DUMMY_BANK_B.name, rpcUsers = demoUser) - startNode(providedName = BOC.name, rpcUsers = listOf(user)) + startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = demoUser) + startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = demoUser) + startNode(providedName = BOC_NAME, rpcUsers = listOf(user)) } } diff --git a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt index dea21d50d3..7f73c6f306 100644 --- a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt +++ b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt @@ -2,6 +2,7 @@ package net.corda.traderdemo import net.corda.core.contracts.CommandData import net.corda.core.crypto.newSecureRandom +import net.corda.core.identity.CordaX500Name import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction @@ -15,6 +16,11 @@ import org.junit.Test import kotlin.test.assertEquals class TransactionGraphSearchTests { + private companion object { + val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20) + val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) + } + @Rule @JvmField val testSerialization = SerializationEnvironmentRule() @@ -36,18 +42,17 @@ class TransactionGraphSearchTests { * @param signer signer for the two transactions and their commands. */ fun buildTransactions(command: CommandData): GraphTransactionStorage { - val megaCorpServices = MockServices(listOf("net.corda.testing.contracts"), rigorousMock(), MEGA_CORP.name, MEGA_CORP_KEY) - val notaryServices = MockServices(listOf("net.corda.testing.contracts"), rigorousMock(), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) - val originBuilder = TransactionBuilder(DUMMY_NOTARY) + val megaCorpServices = MockServices(listOf("net.corda.testing.contracts"), rigorousMock(), megaCorp) + val notaryServices = MockServices(listOf("net.corda.testing.contracts"), rigorousMock(), dummyNotary) + val originBuilder = TransactionBuilder(dummyNotary.party) .addOutputState(DummyState(random31BitValue()), DummyContract.PROGRAM_ID) - .addCommand(command, MEGA_CORP_PUBKEY) + .addCommand(command, megaCorp.pubkey) val originPtx = megaCorpServices.signInitialTransaction(originBuilder) val originTx = notaryServices.addSignature(originPtx) - - val inputBuilder = TransactionBuilder(DUMMY_NOTARY) + val inputBuilder = TransactionBuilder(dummyNotary.party) .addInputState(originTx.tx.outRef(0)) - .addCommand(dummyCommand(MEGA_CORP_PUBKEY)) + .addCommand(dummyCommand(megaCorp.pubkey)) val inputPtx = megaCorpServices.signInitialTransaction(inputBuilder) val inputTx = megaCorpServices.addSignature(inputPtx) diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt index a429d8f204..b20dc19fda 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt @@ -1,55 +1,38 @@ package net.corda.testing.driver import net.corda.core.concurrent.CordaFuture -import net.corda.core.internal.copyTo +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div import net.corda.core.internal.list import net.corda.core.internal.readLines import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.seconds import net.corda.node.internal.NodeStartup -import net.corda.nodeapi.internal.crypto.X509Utilities -import net.corda.nodeapi.internal.crypto.getX509Certificate -import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore -import net.corda.testing.DUMMY_BANK_A -import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.DUMMY_REGULATOR +import net.corda.testing.DUMMY_BANK_A_NAME +import net.corda.testing.DUMMY_NOTARY_NAME import net.corda.testing.common.internal.ProjectStructure.projectRootDir import net.corda.testing.http.HttpApi -import net.corda.testing.internal.CompatibilityZoneParams import net.corda.testing.internal.addressMustBeBound import net.corda.testing.internal.addressMustNotBeBound import net.corda.testing.internal.internalDriver import net.corda.testing.node.NotarySpec -import net.corda.testing.node.network.NetworkMapServer import org.assertj.core.api.Assertions.assertThat import org.json.simple.JSONObject -import org.junit.Rule import org.junit.Test -import org.junit.rules.TemporaryFolder -import java.net.URL import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService -import javax.ws.rs.GET -import javax.ws.rs.POST -import javax.ws.rs.Path -import javax.ws.rs.core.Response -import javax.ws.rs.core.Response.ok - class DriverTests { - - companion object { - private val executorService: ScheduledExecutorService = Executors.newScheduledThreadPool(2) - - private fun nodeMustBeUp(handleFuture: CordaFuture) = handleFuture.getOrThrow().apply { + private companion object { + val DUMMY_REGULATOR_NAME = CordaX500Name("Regulator A", "Paris", "FR") + val executorService: ScheduledExecutorService = Executors.newScheduledThreadPool(2) + fun nodeMustBeUp(handleFuture: CordaFuture) = handleFuture.getOrThrow().apply { val hostAndPort = nodeInfo.addresses.first() // Check that the port is bound addressMustBeBound(executorService, hostAndPort, (this as? NodeHandle.OutOfProcess)?.process) } - private fun nodeMustBeDown(handle: NodeHandle) { + fun nodeMustBeDown(handle: NodeHandle) { val hostAndPort = handle.nodeInfo.addresses.first() // Check that the port is bound addressMustNotBeBound(executorService, hostAndPort) @@ -59,7 +42,7 @@ class DriverTests { @Test fun `simple node startup and shutdown`() { val handle = driver { - val regulator = startNode(providedName = DUMMY_REGULATOR.name) + val regulator = startNode(providedName = DUMMY_REGULATOR_NAME) nodeMustBeUp(regulator) } nodeMustBeDown(handle) @@ -68,7 +51,7 @@ class DriverTests { @Test fun `random free port allocation`() { val nodeHandle = driver(portAllocation = PortAllocation.RandomFree) { - val nodeInfo = startNode(providedName = DUMMY_BANK_A.name) + val nodeInfo = startNode(providedName = DUMMY_BANK_A_NAME) nodeMustBeUp(nodeInfo) } nodeMustBeDown(nodeHandle) @@ -80,7 +63,7 @@ class DriverTests { val logConfigFile = projectRootDir / "config" / "dev" / "log4j2.xml" assertThat(logConfigFile).isRegularFile() driver(isDebug = true, systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString())) { - val baseDirectory = startNode(providedName = DUMMY_BANK_A.name).getOrThrow().configuration.baseDirectory + val baseDirectory = startNode(providedName = DUMMY_BANK_A_NAME).getOrThrow().configuration.baseDirectory val logFile = (baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list { it.sorted().findFirst().get() } val debugLinesPresent = logFile.readLines { lines -> lines.anyMatch { line -> line.startsWith("[DEBUG]") } } assertThat(debugLinesPresent).isTrue() @@ -91,8 +74,7 @@ class DriverTests { fun `monitoring mode enables jolokia exporting of JMX metrics via HTTP JSON`() { driver(jmxPolicy = JmxPolicy(true)) { // start another node so we gain access to node JMX metrics - startNode(providedName = DUMMY_REGULATOR.name).getOrThrow() - + startNode(providedName = DUMMY_REGULATOR_NAME).getOrThrow() val webAddress = NetworkHostAndPort("localhost", 7006) // request access to some JMX metrics via Jolokia HTTP/JSON val api = HttpApi.fromHostAndPort(webAddress, "/jolokia/") @@ -110,8 +92,8 @@ class DriverTests { assertThat(baseDirectory / "process-id").exists() } - val baseDirectory = internalDriver(notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name))) { - baseDirectory(DUMMY_NOTARY.name) + val baseDirectory = internalDriver(notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME))) { + baseDirectory(DUMMY_NOTARY_NAME) } assertThat(baseDirectory / "process-id").doesNotExist() } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index 364250d13c..db3f0ea88f 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -14,7 +14,7 @@ import net.corda.node.internal.StartedNode import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.VerifierType import net.corda.nodeapi.internal.config.User -import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.DUMMY_NOTARY_NAME import net.corda.testing.internal.DriverDSLImpl import net.corda.testing.internal.genericDriver import net.corda.testing.internal.getTimestampAsDirectoryName @@ -227,7 +227,7 @@ data class DriverParameters( val initialiseSerialization: Boolean = true, val startNodesInProcess: Boolean = false, val waitForAllNodesToFinish: Boolean = false, - val notarySpecs: List = listOf(NotarySpec(DUMMY_NOTARY.name)), + val notarySpecs: List = listOf(NotarySpec(DUMMY_NOTARY_NAME)), val extraCordappPackagesToScan: List = emptyList(), val jmxPolicy: JmxPolicy = JmxPolicy() ) { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/DriverDSLImpl.kt index 09ad8aa7ac..67554aa621 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/DriverDSLImpl.kt @@ -42,17 +42,14 @@ import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.addOrReplaceCertificate import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore import net.corda.nodeapi.internal.crypto.save -import net.corda.testing.ALICE -import net.corda.testing.BOB -import net.corda.testing.DUMMY_BANK_A import net.corda.testing.common.internal.testNetworkParameters +import net.corda.testing.* import net.corda.testing.driver.* import net.corda.testing.internal.DriverDSLImpl.ClusterType.NON_VALIDATING_RAFT import net.corda.testing.internal.DriverDSLImpl.ClusterType.VALIDATING_RAFT import net.corda.testing.node.ClusterSpec import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.NotarySpec -import net.corda.testing.setGlobalSerialization import okhttp3.OkHttpClient import okhttp3.Request import rx.Observable @@ -585,13 +582,7 @@ class DriverDSLImpl( internal val log = contextLogger() private val defaultRpcUserList = listOf(User("default", "default", setOf("ALL")).toConfig().root().unwrapped()) - - private val names = arrayOf( - ALICE.name, - BOB.name, - DUMMY_BANK_A.name - ) - + private val names = arrayOf(ALICE_NAME, BOB_NAME, DUMMY_BANK_A_NAME) /** * A sub-set of permissions that grant most of the essential operations used in the unit/integration tests as well as * in demo application like NodeExplorer. diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 4617e6f8b3..e322c7b753 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -40,9 +40,9 @@ import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.ServiceIdentityGenerator import net.corda.nodeapi.internal.NotaryInfo -import net.corda.testing.DUMMY_NOTARY import net.corda.nodeapi.internal.NetworkParametersCopier import net.corda.testing.common.internal.testNetworkParameters +import net.corda.testing.DUMMY_NOTARY_NAME import net.corda.testing.internal.testThreadFactory import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties @@ -51,8 +51,6 @@ import net.corda.testing.testNodeConfiguration import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.sshd.common.util.security.SecurityUtils import rx.internal.schedulers.CachedThreadScheduler -import org.slf4j.Logger -import java.io.Closeable import java.math.BigInteger import java.nio.file.Path import java.security.KeyPair @@ -128,7 +126,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy, private val defaultFactory: (MockNodeArgs) -> MockNode = defaultParameters.defaultFactory, initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, - private val notarySpecs: List = listOf(NotarySpec(DUMMY_NOTARY.name)), + private val notarySpecs: List = listOf(NotarySpec(DUMMY_NOTARY_NAME)), private val cordappPackages: List = defaultParameters.cordappPackages) { /** Helper constructor for creating a [MockNetwork] with custom parameters from Java. */ constructor(parameters: MockNetworkParameters) : this(defaultParameters = parameters) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index 433acd7c59..5b58e65086 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -15,7 +15,6 @@ import net.corda.core.internal.cert import net.corda.core.internal.x500Name import net.corda.core.node.NodeInfo import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.loggerFor import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.nodeapi.internal.config.SSLConfiguration @@ -33,7 +32,6 @@ import org.mockito.internal.stubbing.answers.ThrowsException import java.lang.reflect.Modifier import java.math.BigInteger import java.nio.file.Files -import java.security.KeyPair import java.security.PublicKey import java.util.* import java.util.concurrent.atomic.AtomicInteger @@ -56,40 +54,6 @@ import java.util.concurrent.atomic.AtomicInteger * - The Int.DOLLARS syntax doesn't work from Java. Use the DOLLARS(int) function instead. */ -// TODO: Refactor these dummies to work with the new identities framework. - -// A few dummy values for testing. -val MEGA_CORP_KEY: KeyPair by lazy { generateKeyPair() } -val MEGA_CORP_PUBKEY: PublicKey get() = MEGA_CORP_KEY.public - -val MINI_CORP_KEY: KeyPair by lazy { generateKeyPair() } -val MINI_CORP_PUBKEY: PublicKey get() = MINI_CORP_KEY.public - -val ORACLE_KEY: KeyPair by lazy { generateKeyPair() } -val ORACLE_PUBKEY: PublicKey get() = ORACLE_KEY.public - -val ALICE_PUBKEY: PublicKey get() = ALICE_KEY.public -val BOB_PUBKEY: PublicKey get() = BOB_KEY.public -val MEGA_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(CordaX500Name(organisation = "MegaCorp", locality = "London", country = "GB"), MEGA_CORP_PUBKEY) -val MEGA_CORP: Party get() = MEGA_CORP_IDENTITY.party -val MINI_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(CordaX500Name(organisation = "MiniCorp", locality = "London", country = "GB"), MINI_CORP_PUBKEY) -val MINI_CORP: Party get() = MINI_CORP_IDENTITY.party - -val BOC_NAME: CordaX500Name = CordaX500Name(organisation = "BankOfCorda", locality = "London", country = "GB") -val BOC_KEY: KeyPair by lazy { generateKeyPair() } -val BOC_PUBKEY: PublicKey get() = BOC_KEY.public -val BOC_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(BOC_NAME, BOC_PUBKEY) -val BOC: Party get() = BOC_IDENTITY.party - -val BIG_CORP_KEY: KeyPair by lazy { generateKeyPair() } -val BIG_CORP_PUBKEY: PublicKey get() = BIG_CORP_KEY.public -val BIG_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(CordaX500Name(organisation = "BigCorporation", locality = "New York", country = "US"), BIG_CORP_PUBKEY) -val BIG_CORP: Party get() = BIG_CORP_IDENTITY.party -val BIG_CORP_PARTY_REF = BIG_CORP.ref(OpaqueBytes.of(1)).reference - -val ALL_TEST_KEYS: List get() = listOf(MEGA_CORP_KEY, MINI_CORP_KEY, ALICE_KEY, BOB_KEY, DUMMY_NOTARY_KEY) -val MOCK_HOST_AND_PORT = NetworkHostAndPort("mockHost", 30000) - fun generateStateRef() = StateRef(SecureHash.randomSHA256(), 0) private val freePortCounter = AtomicInteger(30000) @@ -120,8 +84,7 @@ fun getFreeLocalPorts(hostName: String, numberToAlloc: Int): List R, transactionMap: HashMap = HashMap(), /** If set to true, will add dummy components to [transactionBuilder] to make it valid. */ - fillTransaction: Boolean = false + fillTransaction: Boolean ): WireTransaction { val transactionLocation = getCallerLocation() val transactionInterpreter = interpretTransactionDsl(transactionBuilder, dsl) @@ -255,22 +254,19 @@ data class TestLedgerDSLInterpreter private constructor( */ private fun fillTransaction(transactionBuilder: TransactionBuilder) { if (transactionBuilder.commands().isEmpty()) transactionBuilder.addCommand(dummyCommand()) - if (transactionBuilder.inputStates().isEmpty() && transactionBuilder.outputStates().isEmpty()) { - transactionBuilder.addOutputState(DummyContract.SingleOwnerState(owner = ALICE), DummyContract.PROGRAM_ID) - } } override fun _transaction( transactionLabel: String?, transactionBuilder: TransactionBuilder, dsl: TestTransactionDSLInterpreter.() -> EnforceVerifyOrFail - ) = recordTransactionWithTransactionMap(transactionLabel, transactionBuilder, dsl, transactionWithLocations) + ) = recordTransactionWithTransactionMap(transactionLabel, transactionBuilder, dsl, transactionWithLocations, false) override fun _unverifiedTransaction( transactionLabel: String?, transactionBuilder: TransactionBuilder, dsl: TestTransactionDSLInterpreter.() -> Unit - ) = recordTransactionWithTransactionMap(transactionLabel, transactionBuilder, dsl, nonVerifiedTransactionWithLocations, fillTransaction = true) + ) = recordTransactionWithTransactionMap(transactionLabel, transactionBuilder, dsl, nonVerifiedTransactionWithLocations, true) override fun _tweak(dsl: LedgerDSLInterpreter.() -> Unit) = copy().dsl() @@ -318,40 +314,3 @@ data class TestLedgerDSLInterpreter private constructor( val transactionsToVerify: List get() = transactionWithLocations.values.map { it.transaction } val transactionsUnverified: List get() = nonVerifiedTransactionWithLocations.values.map { it.transaction } } - -/** - * Expands all [CompositeKey]s present in PublicKey iterable to set of single [PublicKey]s. - * If an element of the set is a single PublicKey it gives just that key, if it is a [CompositeKey] it returns all leaf - * keys for that composite element. - */ -val Iterable.expandedCompositeKeys: Set - get() = flatMap { it.keys }.toSet() - -/** - * Signs all transactions passed in. - * @param transactionsToSign Transactions to be signed. - * @param extraKeys extra keys to sign transactions with. - * @return List of [SignedTransaction]s. - */ -fun signAll(transactionsToSign: List, extraKeys: List) = transactionsToSign.map { wtx -> - check(wtx.requiredSigningKeys.isNotEmpty()) - val signatures = ArrayList() - val keyLookup = HashMap() - - (ALL_TEST_KEYS + extraKeys).forEach { - keyLookup[it.public] = it - } - wtx.requiredSigningKeys.expandedCompositeKeys.forEach { - val key = keyLookup[it] ?: throw IllegalArgumentException("Missing required key for ${it.toStringShort()}") - signatures += key.sign(SignableData(wtx.id, SignatureMetadata(1, Crypto.findSignatureScheme(it).schemeNumberID))) - } - SignedTransaction(wtx, signatures) -} - -/** - * Signs all transactions in the ledger. - * @param extraKeys extra keys to sign transactions with. - * @return List of [SignedTransaction]s. - */ -fun LedgerDSL.signAll( - vararg extraKeys: KeyPair) = signAll(this.interpreter.wireTransactions, extraKeys.toList()) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt index 8547a1f9a6..668a53e2aa 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt @@ -88,7 +88,7 @@ class TransactionDSL(interpreter: T, private */ fun input(contractClassName: ContractClassName, state: ContractState) { val transaction = ledgerInterpreter._unverifiedTransaction(null, TransactionBuilder(notary)) { - output(contractClassName, null, DUMMY_NOTARY, null, AlwaysAcceptAttachmentConstraint, state) + output(contractClassName, null, notary, null, AlwaysAcceptAttachmentConstraint, state) } input(transaction.outRef(0).ref) } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt index b16ca113fb..e303cba51f 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt @@ -18,11 +18,7 @@ import net.corda.finance.contracts.Commodity import net.corda.finance.contracts.DealState import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.CommodityContract -import net.corda.testing.chooseIdentity -import net.corda.testing.chooseIdentityAndCert -import net.corda.testing.dummyCommand -import net.corda.testing.singleIdentity -import java.security.KeyPair +import net.corda.testing.* import java.security.PublicKey import java.time.Duration import java.time.Instant @@ -37,9 +33,8 @@ import java.util.* */ class VaultFiller @JvmOverloads constructor( private val services: ServiceHub, - private val defaultNotary: Party, - private val defaultNotaryKeyPair: KeyPair, - private val altNotary: Party = defaultNotary, + private val defaultNotary: TestIdentity, + private val altNotary: Party = defaultNotary.party, private val rngFactory: () -> Random = { Random(0L) }) { companion object { fun calculateRandomlySizedAmounts(howMuch: Amount, min: Int, max: Int, rng: Random): LongArray { @@ -71,10 +66,6 @@ class VaultFiller @JvmOverloads constructor( } } - init { - require(defaultNotary.owningKey == defaultNotaryKeyPair.public) { "Default notary public keys must match." } - } - @JvmOverloads fun fillWithSomeTestDeals(dealIds: List, issuerServices: ServiceHub = services, @@ -84,12 +75,12 @@ class VaultFiller @JvmOverloads constructor( val transactions: List = dealIds.map { // Issue a deal state - val dummyIssue = TransactionBuilder(notary = defaultNotary).apply { + val dummyIssue = TransactionBuilder(notary = defaultNotary.party).apply { addOutputState(DummyDealContract.State(ref = it, participants = participants.plus(me)), DUMMY_DEAL_PROGRAM_ID) addCommand(dummyCommand()) } val stx = issuerServices.signInitialTransaction(dummyIssue) - return@map services.addSignature(stx, defaultNotaryKeyPair.public) + return@map services.addSignature(stx, defaultNotary.pubkey) } services.recordTransactions(transactions) // Get all the StateAndRefs of all the generated transactions. @@ -110,11 +101,11 @@ class VaultFiller @JvmOverloads constructor( linearTimestamp: Instant = now()): Vault { val myKey: PublicKey = services.myInfo.chooseIdentity().owningKey val me = AnonymousParty(myKey) - val issuerKey = defaultNotaryKeyPair + val issuerKey = defaultNotary.key val signatureMetadata = SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(issuerKey.public).schemeNumberID) val transactions: List = (1..numberToCreate).map { // Issue a Linear state - val dummyIssue = TransactionBuilder(notary = defaultNotary).apply { + val dummyIssue = TransactionBuilder(notary = defaultNotary.party).apply { addOutputState(DummyLinearContract.State( linearId = UniqueIdentifier(externalId), participants = participants.plus(me), diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt index 8836b2f4bc..c88d2505d0 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -22,8 +22,8 @@ import net.corda.finance.flows.CashExitFlow.ExitRequest import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User -import net.corda.testing.ALICE -import net.corda.testing.BOB +import net.corda.testing.ALICE_NAME +import net.corda.testing.BOB_NAME import net.corda.testing.driver.JmxPolicy import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation @@ -67,8 +67,8 @@ class ExplorerSimulation(private val options: OptionSet) { val portAllocation = PortAllocation.Incremental(20000) driver(portAllocation = portAllocation, extraCordappPackagesToScan = listOf("net.corda.finance"), waitForAllNodesToFinish = true, jmxPolicy = JmxPolicy(true)) { // TODO : Supported flow should be exposed somehow from the node instead of set of ServiceInfo. - val alice = startNode(providedName = ALICE.name, rpcUsers = listOf(user)) - val bob = startNode(providedName = BOB.name, rpcUsers = listOf(user)) + val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)) + val bob = startNode(providedName = BOB_NAME, rpcUsers = listOf(user)) val ukBankName = CordaX500Name(organisation = "UK Bank Plc", locality = "London", country = "GB") val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "US") val issuerGBP = startNode(providedName = ukBankName, rpcUsers = listOf(manager), diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt index 34e6ea9ffb..d1d500b7a2 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt @@ -18,13 +18,16 @@ import org.slf4j.LoggerFactory private val log = LoggerFactory.getLogger("NotaryTest") private val dummyCashIssuer = TestIdentity(CordaX500Name("Snake Oil Issuer", "London", "GB"), 10) private val DUMMY_CASH_ISSUER = dummyCashIssuer.ref(1) +private val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20) +private val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) +private val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) data class NotariseCommand(val issueTx: SignedTransaction, val moveTx: SignedTransaction, val node: NodeConnection) val dummyNotarisationTest = LoadTest( "Notarising dummy transactions", generate = { _, _ -> - val issuerServices = MockServices(makeTestIdentityService(listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, DUMMY_NOTARY_IDENTITY)), MEGA_CORP.name, dummyCashIssuer.key) + val issuerServices = MockServices(makeTestIdentityService(listOf(megaCorp.identity, miniCorp.identity, dummyCashIssuer.identity, dummyNotary.identity)), megaCorp.name, dummyCashIssuer.key) val generateTx = Generator.pickOne(simpleNodes).flatMap { node -> Generator.int().map { val issueBuilder = DummyContract.generateInitial(it, notary.info.legalIdentities[0], DUMMY_CASH_ISSUER) // TODO notary choice diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt index 2ac14a7773..65336db7e4 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt @@ -12,10 +12,7 @@ import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.config.VerifierType -import net.corda.testing.ALICE -import net.corda.testing.ALICE_NAME -import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.* import net.corda.testing.node.NotarySpec import org.junit.Rule import org.junit.Test @@ -44,7 +41,7 @@ class VerifierTests { @Test fun `single verifier works with requestor`() { verifierDriver(extraCordappPackagesToScan = listOf("net.corda.finance.contracts")) { - val aliceFuture = startVerificationRequestor(ALICE.name) + val aliceFuture = startVerificationRequestor(ALICE_NAME) val transactions = generateTransactions(100) val alice = aliceFuture.get() startVerifier(alice) @@ -61,7 +58,7 @@ class VerifierTests { @Test fun `single verification fails`() { verifierDriver(extraCordappPackagesToScan = listOf("net.corda.finance.contracts")) { - val aliceFuture = startVerificationRequestor(ALICE.name) + val aliceFuture = startVerificationRequestor(ALICE_NAME) // Generate transactions as per usual, but then remove attachments making transaction invalid. val transactions = generateTransactions(1).map { it.copy(attachments = emptyList()) } val alice = aliceFuture.get() @@ -76,7 +73,7 @@ class VerifierTests { @Test fun `multiple verifiers work with requestor`() { verifierDriver { - val aliceFuture = startVerificationRequestor(ALICE.name) + val aliceFuture = startVerificationRequestor(ALICE_NAME) val transactions = generateTransactions(100) val alice = aliceFuture.get() val numberOfVerifiers = 4 @@ -96,7 +93,7 @@ class VerifierTests { @Test fun `verification redistributes on verifier death`() { verifierDriver { - val aliceFuture = startVerificationRequestor(ALICE.name) + val aliceFuture = startVerificationRequestor(ALICE_NAME) val numberOfTransactions = 100 val transactions = generateTransactions(numberOfTransactions) val alice = aliceFuture.get() @@ -124,7 +121,7 @@ class VerifierTests { @Test fun `verification request waits until verifier comes online`() { verifierDriver { - val aliceFuture = startVerificationRequestor(ALICE.name) + val aliceFuture = startVerificationRequestor(ALICE_NAME) val transactions = generateTransactions(100) val alice = aliceFuture.get() val futures = transactions.map { alice.verifyTransaction(it) } @@ -137,9 +134,9 @@ class VerifierTests { fun `single verifier works with a node`() { verifierDriver( extraCordappPackagesToScan = listOf("net.corda.finance.contracts"), - notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name, verifierType = VerifierType.OutOfProcess)) + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, verifierType = VerifierType.OutOfProcess)) ) { - val aliceNode = startNode(providedName = ALICE.name).getOrThrow() + val aliceNode = startNode(providedName = ALICE_NAME).getOrThrow() val notaryNode = defaultNotaryNode.getOrThrow() val alice = aliceNode.rpc.wellKnownPartyFromX500Name(ALICE_NAME)!! startVerifier(notaryNode) diff --git a/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt b/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt index c23c270015..2f5a7a6e00 100644 --- a/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt +++ b/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt @@ -2,7 +2,7 @@ package net.corda.webserver import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow -import net.corda.testing.DUMMY_BANK_A +import net.corda.testing.DUMMY_BANK_A_NAME import net.corda.testing.driver.WebserverHandle import net.corda.testing.internal.addressMustBeBound import net.corda.testing.internal.addressMustNotBeBound @@ -27,7 +27,7 @@ class WebserverDriverTests { @Test fun `starting a node and independent web server works`() { val addr = driver { - val node = startNode(providedName = DUMMY_BANK_A.name).getOrThrow() + val node = startNode(providedName = DUMMY_BANK_A_NAME).getOrThrow() val webserverHandle = startWebserver(node).getOrThrow() webserverMustBeUp(webserverHandle) webserverHandle.listenAddress From c3b99553448eace21c49393ee2df2ac4da6efbd7 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Tue, 12 Dec 2017 19:37:01 +0000 Subject: [PATCH 6/9] CORDA-716 Fix split packages in testing (#2232) --- .../net/corda/client/rpc/CordaRPCJavaClientTest.java | 2 +- .../kotlin/net/corda/client/rpc/CordaRPCClientTest.kt | 2 +- .../kotlin/net/corda/client/rpc/RPCStabilityTests.kt | 2 +- .../kotlin/net/corda/client/rpc/AbstractRPCTest.kt | 8 ++++---- .../corda/client/rpc/ClientRPCInfrastructureTests.kt | 6 +++--- .../kotlin/net/corda/client/rpc/RPCConcurrencyTests.kt | 4 ++-- .../kotlin/net/corda/client/rpc/RPCFailureTests.kt | 4 ++-- .../kotlin/net/corda/client/rpc/RPCPerformanceTests.kt | 10 +++++----- .../kotlin/net/corda/client/rpc/RPCPermissionsTests.kt | 4 ++-- .../net/corda/confidential/IdentitySyncFlowTests.kt | 1 + .../net/corda/confidential/SwapIdentitiesFlowTests.kt | 1 + .../java/net/corda/core/flows/FlowsInJavaTest.java | 2 +- .../net/corda/core/crypto/PartialMerkleTreeTest.kt | 1 + .../kotlin/net/corda/core/flows/AttachmentTests.kt | 1 + .../net/corda/core/flows/CollectSignaturesFlowTests.kt | 1 + .../net/corda/core/flows/ContractUpgradeFlowTest.kt | 10 +++++----- .../kotlin/net/corda/core/flows/FinalityFlowTests.kt | 1 + .../kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt | 2 +- .../corda/core/internal/ResolveTransactionsFlowTest.kt | 3 ++- .../core/serialization/AttachmentSerializationTest.kt | 2 +- .../core/transactions/TransactionEncumbranceTests.kt | 1 + .../net/corda/core/transactions/TransactionTests.kt | 3 +-- .../java/tutorial/testdsl/CommercialPaperTest.java | 4 ++-- .../docs/tutorial/mocknetwork/TutorialMockNetwork.kt | 2 +- .../net/corda/docs/tutorial/testdsl/TutorialTestDSL.kt | 2 ++ .../test/kotlin/net/corda/docs/CustomVaultQueryTest.kt | 2 +- .../net/corda/docs/FxTransactionBuildTutorialTest.kt | 2 +- .../corda/docs/WorkflowTransactionBuildTutorialTest.kt | 1 + .../net/corda/finance/contracts/universal/Cap.kt | 1 + .../corda/finance/contracts/asset/CashTestsJava.java | 2 +- .../corda/finance/contracts/CommercialPaperTests.kt | 2 ++ .../net/corda/finance/contracts/asset/CashTests.kt | 2 ++ .../corda/finance/contracts/asset/ObligationTests.kt | 2 ++ .../asset/cash/selection/CashSelectionH2ImplTest.kt | 2 +- .../net/corda/finance/flows/CashExitFlowTests.kt | 2 +- .../net/corda/finance/flows/CashIssueFlowTests.kt | 2 +- .../net/corda/finance/flows/CashPaymentFlowTests.kt | 1 + .../AttachmentsClassLoaderStaticContractTests.kt | 2 +- .../nodeapi/internal/AttachmentsClassLoaderTests.kt | 2 +- .../internal/serialization/CordaClassResolverTests.kt | 2 +- .../kotlin/net/corda/node/NodePerformanceTests.kt | 8 ++++---- .../net/corda/node/services/AttachmentLoadingTests.kt | 4 +++- .../net/corda/node/services/BFTNotaryServiceTests.kt | 2 +- .../net/corda/node/services/RaftNotaryServiceTests.kt | 2 ++ .../net/corda/node/services/UserAuthServiceTests.kt | 2 +- .../net/corda/node/services/network/NetworkMapTest.kt | 4 ++-- .../services/network/PersistentNetworkMapCacheTest.kt | 2 +- .../node/services/statemachine/FlowVersioningTest.kt | 4 +++- .../utilities/registration/NodeRegistrationTest.kt | 5 ++--- .../net/corda/services/messaging/MQSecurityTest.kt | 3 ++- .../test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt | 1 + .../node/internal/cordapp/CordappProviderImplTests.kt | 2 +- .../net/corda/node/internal/security/PasswordTest.kt | 0 .../net/corda/node/services/NotaryChangeTests.kt | 1 + .../node/services/events/NodeSchedulerServiceTest.kt | 1 + .../corda/node/services/events/ScheduledFlowTests.kt | 1 + .../node/services/messaging/ArtemisMessagingTests.kt | 1 + .../node/services/statemachine/FlowFrameworkTests.kt | 1 + .../node/services/transactions/NotaryServiceTests.kt | 2 +- .../transactions/ValidatingNotaryServiceTests.kt | 1 + .../node/services/vault/VaultSoftLockManagerTest.kt | 2 +- .../registration/NetworkRegistrationHelperTest.kt | 2 +- .../kotlin/net/corda/attachmentdemo/AttachmentDemo.kt | 2 +- .../kotlin/net/corda/bank/BankOfCordaCordformTest.kt | 2 +- .../main/kotlin/net/corda/bank/BankOfCordaCordform.kt | 2 +- .../src/test/kotlin/net/corda/irs/contract/IRSTests.kt | 2 ++ .../kotlin/net/corda/test/spring/SpringDriver.kt | 2 +- .../net/corda/netmap/simulation/IRSSimulation.kt | 2 +- .../kotlin/net/corda/notarydemo/BFTNotaryCordform.kt | 2 +- .../src/main/kotlin/net/corda/notarydemo/Clean.kt | 2 +- .../net/corda/notarydemo/CustomNotaryCordform.kt | 2 +- .../kotlin/net/corda/notarydemo/RaftNotaryCordform.kt | 2 +- .../net/corda/notarydemo/SingleNotaryCordform.kt | 2 +- .../kotlin/net/corda/traderdemo/TraderDemoTest.kt | 2 +- .../kotlin/net/corda/testing/driver/DriverTests.kt | 8 ++++---- .../corda/testing/{ => node}/FlowStackSnapshotTest.kt | 3 +-- .../{ => node}/internal/ProcessUtilitiesTests.kt | 2 +- .../src/main/kotlin/net/corda/testing/driver/Driver.kt | 6 +++--- .../src/main/kotlin/net/corda/testing/node/MockNode.kt | 1 - .../main/kotlin/net/corda/testing/node/MockServices.kt | 2 ++ .../net/corda/testing/{ => node}/NodeTestUtils.kt | 3 ++- .../corda/testing/{ => node}/internal/DriverDSLImpl.kt | 7 ++++--- .../testing/{ => node}/internal/InternalTestUtils.kt | 2 +- .../corda/testing/{ => node}/internal/NodeBasedTest.kt | 3 ++- .../testing/{ => node}/internal/ProcessUtilities.kt | 2 +- .../net/corda/testing/{ => node}/internal/RPCDriver.kt | 2 +- .../testing/{ => node}/internal/ShutdownManager.kt | 2 +- .../{ => node}/internal/demorun/CordformUtils.kt | 2 +- .../testing/{ => node}/internal/demorun/DemoRunner.kt | 6 +++--- .../{ => node}/internal/performance/Injectors.kt | 5 +++-- .../{ => node}/internal/performance/Reporter.kt | 4 ++-- .../src/main/kotlin/net/corda/testing/TestDSL.kt | 4 ++-- .../kotlin/net/corda/testing/node/MockAttachment.kt | 9 --------- .../{node => services}/MockAttachmentStorage.kt | 2 +- .../testing/{node => services}/MockCordappProvider.kt | 2 +- .../kotlin/net/corda/verifier/VerifierDriver.kt | 2 +- .../kotlin/net/corda/webserver/WebserverDriverTests.kt | 4 ++-- 97 files changed, 146 insertions(+), 118 deletions(-) rename {testing/node-driver/src/test/java => node/src/test/kotlin}/net/corda/node/internal/security/PasswordTest.kt (100%) rename testing/node-driver/src/integration-test/kotlin/net/corda/testing/{ => node}/FlowStackSnapshotTest.kt (99%) rename testing/node-driver/src/integration-test/kotlin/net/corda/testing/{ => node}/internal/ProcessUtilitiesTests.kt (96%) rename testing/node-driver/src/main/kotlin/net/corda/testing/{ => node}/NodeTestUtils.kt (98%) rename testing/node-driver/src/main/kotlin/net/corda/testing/{ => node}/internal/DriverDSLImpl.kt (99%) rename testing/node-driver/src/main/kotlin/net/corda/testing/{ => node}/internal/InternalTestUtils.kt (98%) rename testing/node-driver/src/main/kotlin/net/corda/testing/{ => node}/internal/NodeBasedTest.kt (98%) rename testing/node-driver/src/main/kotlin/net/corda/testing/{ => node}/internal/ProcessUtilities.kt (98%) rename testing/node-driver/src/main/kotlin/net/corda/testing/{ => node}/internal/RPCDriver.kt (99%) rename testing/node-driver/src/main/kotlin/net/corda/testing/{ => node}/internal/ShutdownManager.kt (98%) rename testing/node-driver/src/main/kotlin/net/corda/testing/{ => node}/internal/demorun/CordformUtils.kt (93%) rename testing/node-driver/src/main/kotlin/net/corda/testing/{ => node}/internal/demorun/DemoRunner.kt (93%) rename testing/node-driver/src/main/kotlin/net/corda/testing/{ => node}/internal/performance/Injectors.kt (95%) rename testing/node-driver/src/main/kotlin/net/corda/testing/{ => node}/internal/performance/Reporter.kt (92%) delete mode 100644 testing/test-utils/src/main/kotlin/net/corda/testing/node/MockAttachment.kt rename testing/test-utils/src/main/kotlin/net/corda/testing/{node => services}/MockAttachmentStorage.kt (98%) rename testing/test-utils/src/main/kotlin/net/corda/testing/{node => services}/MockCordappProvider.kt (98%) diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index 0ab028c296..c4492eca06 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -12,7 +12,7 @@ import net.corda.node.internal.Node; import net.corda.node.internal.StartedNode; import net.corda.nodeapi.internal.config.User; import net.corda.testing.CoreTestUtils; -import net.corda.testing.internal.NodeBasedTest; +import net.corda.testing.node.internal.NodeBasedTest; import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index 39af03fcd3..51c17f0baf 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -22,7 +22,7 @@ import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.* -import net.corda.testing.internal.NodeBasedTest +import net.corda.testing.node.internal.NodeBasedTest import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatExceptionOfType diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt index cee065b11f..b6f0e5d0cf 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt @@ -13,8 +13,8 @@ import net.corda.core.utilities.* import net.corda.node.services.messaging.RPCServerConfiguration import net.corda.nodeapi.RPCApi import net.corda.testing.SerializationEnvironmentRule -import net.corda.testing.internal.poll import net.corda.testing.internal.* +import net.corda.testing.node.internal.* import org.apache.activemq.artemis.api.core.SimpleString import org.junit.After import org.junit.Assert.assertEquals diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/AbstractRPCTest.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/AbstractRPCTest.kt index 053c8b85c1..11a9a027fa 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/AbstractRPCTest.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/AbstractRPCTest.kt @@ -7,10 +7,10 @@ import net.corda.core.messaging.RPCOps import net.corda.node.services.messaging.RPCServerConfiguration import net.corda.nodeapi.internal.config.User import net.corda.testing.SerializationEnvironmentRule -import net.corda.testing.internal.RPCDriverDSL -import net.corda.testing.internal.rpcTestUser -import net.corda.testing.internal.startInVmRpcClient -import net.corda.testing.internal.startRpcClient +import net.corda.testing.node.internal.RPCDriverDSL +import net.corda.testing.node.internal.rpcTestUser +import net.corda.testing.node.internal.startInVmRpcClient +import net.corda.testing.node.internal.startRpcClient import org.apache.activemq.artemis.api.core.client.ClientSession import org.junit.Rule import org.junit.runners.Parameterized diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt index eee72cf1b5..43cd0c1ce1 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt @@ -7,9 +7,9 @@ import net.corda.core.internal.concurrent.thenMatch import net.corda.core.messaging.RPCOps import net.corda.core.utilities.getOrThrow import net.corda.node.services.messaging.rpcContext -import net.corda.testing.internal.RPCDriverDSL -import net.corda.testing.internal.rpcDriver -import net.corda.testing.internal.rpcTestUser +import net.corda.testing.node.internal.RPCDriverDSL +import net.corda.testing.node.internal.rpcDriver +import net.corda.testing.node.internal.rpcTestUser import org.assertj.core.api.Assertions.assertThat import org.junit.Test import org.junit.runner.RunWith diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCConcurrencyTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCConcurrencyTests.kt index 6d80a39398..22c160a1d0 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCConcurrencyTests.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCConcurrencyTests.kt @@ -9,8 +9,8 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.millis import net.corda.node.services.messaging.RPCServerConfiguration -import net.corda.testing.internal.RPCDriverDSL -import net.corda.testing.internal.rpcDriver +import net.corda.testing.node.internal.RPCDriverDSL +import net.corda.testing.node.internal.rpcDriver import net.corda.testing.internal.testThreadFactory import org.apache.activemq.artemis.utils.collections.ConcurrentHashSet import org.junit.After diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCFailureTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCFailureTests.kt index 5894520661..96ba3ece6b 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCFailureTests.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCFailureTests.kt @@ -6,8 +6,8 @@ import net.corda.core.internal.concurrent.openFuture import net.corda.core.messaging.* import net.corda.core.utilities.getOrThrow import net.corda.testing.SerializationEnvironmentRule -import net.corda.testing.internal.rpcDriver -import net.corda.testing.internal.startRpcClient +import net.corda.testing.node.internal.rpcDriver +import net.corda.testing.node.internal.startRpcClient import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Rule import org.junit.Test diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPerformanceTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPerformanceTests.kt index 90a01ec645..85d3351ade 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPerformanceTests.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPerformanceTests.kt @@ -6,12 +6,12 @@ import net.corda.core.messaging.RPCOps import net.corda.core.utilities.minutes import net.corda.core.utilities.seconds import net.corda.node.services.messaging.RPCServerConfiguration -import net.corda.testing.internal.RPCDriverDSL +import net.corda.testing.node.internal.RPCDriverDSL import net.corda.testing.internal.performance.div -import net.corda.testing.internal.performance.startPublishingFixedRateInjector -import net.corda.testing.internal.performance.startReporter -import net.corda.testing.internal.performance.startTightLoopInjector -import net.corda.testing.internal.rpcDriver +import net.corda.testing.node.internal.performance.startPublishingFixedRateInjector +import net.corda.testing.node.internal.performance.startReporter +import net.corda.testing.node.internal.performance.startTightLoopInjector +import net.corda.testing.node.internal.rpcDriver import net.corda.testing.measure import org.junit.Ignore import org.junit.Test diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt index 0607541ba9..9d57aa401e 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt @@ -3,8 +3,8 @@ package net.corda.client.rpc import net.corda.core.messaging.RPCOps import net.corda.node.services.messaging.rpcContext import net.corda.nodeapi.internal.config.User -import net.corda.testing.internal.RPCDriverDSL -import net.corda.testing.internal.rpcDriver +import net.corda.testing.node.internal.RPCDriverDSL +import net.corda.testing.node.internal.rpcDriver import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized diff --git a/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt index 51b24e8bd1..585cb8b6bf 100644 --- a/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt @@ -16,6 +16,7 @@ import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.testing.* import net.corda.testing.node.MockNetwork +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before import org.junit.Test diff --git a/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt index 5bb328d28d..bc5ad98a35 100644 --- a/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt @@ -5,6 +5,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.testing.* import net.corda.testing.node.MockNetwork import org.junit.Before +import net.corda.testing.node.startFlow import org.junit.Test import kotlin.test.* diff --git a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java index 1811328d67..250877bde4 100644 --- a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java +++ b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java @@ -16,7 +16,7 @@ import java.util.concurrent.Future; import static net.corda.testing.CoreTestUtils.singleIdentity; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.Assert.fail; -import static net.corda.testing.NodeTestUtils.startFlow; +import static net.corda.testing.node.NodeTestUtils.startFlow; public class FlowsInJavaTest { private final MockNetwork mockNet = new MockNetwork(); diff --git a/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt b/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt index fdbdebbfd8..3968d8cae3 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt @@ -15,6 +15,7 @@ import net.corda.finance.contracts.asset.Cash import net.corda.node.services.api.IdentityServiceInternal import net.corda.testing.* import net.corda.testing.node.MockServices +import net.corda.testing.node.ledger import org.junit.Before import org.junit.Rule import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt index 97339e7388..969724731b 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -13,6 +13,7 @@ import net.corda.node.services.persistence.NodeAttachmentService import net.corda.testing.* import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt index c2499cff45..944e7eaca5 100644 --- a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt @@ -16,6 +16,7 @@ import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt index 9e51829bdc..eb3b5609a5 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -24,13 +24,13 @@ import net.corda.testing.ALICE_NAME import net.corda.testing.BOB_NAME import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContractV2 -import net.corda.testing.internal.RPCDriverDSL -import net.corda.testing.internal.rpcDriver -import net.corda.testing.internal.rpcTestUser -import net.corda.testing.internal.startRpcClient +import net.corda.testing.node.internal.RPCDriverDSL +import net.corda.testing.node.internal.rpcDriver +import net.corda.testing.node.internal.rpcTestUser +import net.corda.testing.node.internal.startRpcClient import net.corda.testing.node.MockNetwork import net.corda.testing.singleIdentity -import net.corda.testing.startFlow +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt index a7db7c5696..bd4be11c16 100644 --- a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt @@ -9,6 +9,7 @@ import net.corda.finance.issuedBy import net.corda.node.services.api.StartedNodeServices import net.corda.testing.* import net.corda.testing.node.MockNetwork +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt index 2fc6be8955..d90bf3e2e6 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ReceiveAllFlowTests.kt @@ -7,7 +7,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.testing.node.MockNetwork import net.corda.testing.singleIdentity -import net.corda.testing.startFlow +import net.corda.testing.node.startFlow import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt index 6f60eedad6..5121a9f6e5 100644 --- a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt @@ -9,9 +9,10 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.sequence import net.corda.node.internal.StartedNode -import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork +import net.corda.testing.singleIdentity +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt index ec5cac3898..8d9303db22 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -21,7 +21,7 @@ import net.corda.testing.BOB_NAME import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters import net.corda.testing.singleIdentity -import net.corda.testing.startFlow +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt b/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt index 658edad723..1f487f6808 100644 --- a/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt @@ -13,6 +13,7 @@ import net.corda.finance.contracts.asset.Cash import net.corda.node.services.api.IdentityServiceInternal import net.corda.testing.* import net.corda.testing.node.MockServices +import net.corda.testing.node.ledger import org.junit.Rule import org.junit.Test import java.time.Instant diff --git a/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt b/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt index 3696429463..8280d688e6 100644 --- a/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt @@ -6,7 +6,6 @@ import net.corda.core.crypto.CompositeKey import net.corda.core.identity.Party import net.corda.testing.* import net.corda.testing.contracts.DummyContract -import net.corda.testing.node.MockAttachment import org.junit.Rule import org.junit.Test import java.math.BigInteger @@ -112,7 +111,7 @@ class TransactionTests { val inputs = emptyList>() val outputs = listOf(baseOutState, baseOutState.copy(notary = ALICE), baseOutState.copy(notary = BOB)) val commands = emptyList>() - val attachments = listOf(ContractAttachment(MockAttachment(), DummyContract.PROGRAM_ID)) + val attachments = listOf(ContractAttachment(rigorousMock(), DummyContract.PROGRAM_ID)) val id = SecureHash.randomSHA256() val timeWindow: TimeWindow? = null val privacySalt: PrivacySalt = PrivacySalt() diff --git a/docs/source/example-code/src/main/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java b/docs/source/example-code/src/main/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java index 81805041d8..6c83cabfc6 100644 --- a/docs/source/example-code/src/main/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java +++ b/docs/source/example-code/src/main/java/net/corda/docs/java/tutorial/testdsl/CommercialPaperTest.java @@ -21,9 +21,9 @@ import static net.corda.core.crypto.Crypto.generateKeyPair; import static net.corda.finance.Currencies.DOLLARS; import static net.corda.finance.Currencies.issuedBy; import static net.corda.finance.contracts.JavaCommercialPaper.JCP_PROGRAM_ID; +import static net.corda.testing.node.NodeTestUtils.ledger; +import static net.corda.testing.node.NodeTestUtils.transaction; import static net.corda.testing.CoreTestUtils.rigorousMock; -import static net.corda.testing.NodeTestUtils.ledger; -import static net.corda.testing.NodeTestUtils.transaction; import static net.corda.testing.TestConstants.*; import static org.mockito.Mockito.doReturn; diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/mocknetwork/TutorialMockNetwork.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/mocknetwork/TutorialMockNetwork.kt index ba8802dceb..4c4b5d76a4 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/mocknetwork/TutorialMockNetwork.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/mocknetwork/TutorialMockNetwork.kt @@ -19,7 +19,7 @@ import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.MessagingServiceSpy import net.corda.testing.node.MockNetwork import net.corda.testing.node.setMessagingServiceSpy -import net.corda.testing.startFlow +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before import org.junit.Rule diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/testdsl/TutorialTestDSL.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/testdsl/TutorialTestDSL.kt index 000f173207..6754a6082c 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/testdsl/TutorialTestDSL.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/tutorial/testdsl/TutorialTestDSL.kt @@ -15,6 +15,8 @@ import net.corda.finance.contracts.asset.Cash import net.corda.node.services.api.IdentityServiceInternal import net.corda.testing.* import net.corda.testing.node.MockServices +import net.corda.testing.node.ledger +import net.corda.testing.node.transaction import org.junit.Rule import org.junit.Test diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt index ef2acb083b..f2ecb42a3f 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt @@ -10,7 +10,7 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.node.internal.StartedNode import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork -import net.corda.testing.startFlow +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Assert import org.junit.Before diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt index c53fe8e175..5009779529 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt @@ -10,7 +10,7 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.node.internal.StartedNode import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork -import net.corda.testing.startFlow +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before import org.junit.Test diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt index d4fb476134..dca397eb98 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt @@ -12,6 +12,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.node.services.api.StartedNodeServices import net.corda.testing.* import net.corda.testing.node.MockNetwork +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before import org.junit.Test diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt index 2177da0ddb..bb458188e6 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt @@ -10,6 +10,7 @@ import net.corda.finance.contracts.Tenor import net.corda.node.services.api.IdentityServiceInternal import net.corda.testing.* import net.corda.testing.node.MockServices +import net.corda.testing.node.transaction import org.junit.Ignore import org.junit.Rule import org.junit.Test diff --git a/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java b/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java index 4553eb3b95..ce7273f75b 100644 --- a/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java +++ b/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java @@ -14,8 +14,8 @@ import org.junit.Test; import static net.corda.finance.Currencies.DOLLARS; import static net.corda.finance.Currencies.issuedBy; +import static net.corda.testing.node.NodeTestUtils.transaction; import static net.corda.testing.CoreTestUtils.rigorousMock; -import static net.corda.testing.NodeTestUtils.transaction; import static net.corda.testing.TestConstants.getDUMMY_NOTARY_NAME; import static org.mockito.Mockito.doReturn; diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt index b05a506112..71ae00d507 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt @@ -21,7 +21,9 @@ import net.corda.testing.* import net.corda.testing.contracts.VaultFiller import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices +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 diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt index 0a9cc89666..760aab4aea 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt @@ -24,7 +24,9 @@ import net.corda.testing.contracts.DummyState import net.corda.testing.contracts.VaultFiller import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices +import net.corda.testing.node.ledger import net.corda.testing.node.makeTestIdentityService +import net.corda.testing.node.transaction import org.junit.After import org.junit.Before import org.junit.Rule diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt index 2d81b49fe8..2670b0674f 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt @@ -23,6 +23,8 @@ import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState import net.corda.testing.node.MockServices +import net.corda.testing.node.ledger +import net.corda.testing.node.transaction import org.junit.Rule import org.junit.Test import java.time.Instant diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2ImplTest.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2ImplTest.kt index c044387f00..faba806b2a 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2ImplTest.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2ImplTest.kt @@ -10,7 +10,7 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters -import net.corda.testing.startFlow +import net.corda.testing.node.startFlow import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.After import org.junit.Test diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt index bead3cefde..5bb058c5ac 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt @@ -11,7 +11,7 @@ import net.corda.testing.BOC_NAME import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode -import net.corda.testing.startFlow +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before import org.junit.Test diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt index 7a8fa93377..98df506c31 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt @@ -11,7 +11,7 @@ import net.corda.testing.BOC_NAME import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode -import net.corda.testing.startFlow +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before import org.junit.Test diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt index 26b9ab267d..1c046752a2 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt @@ -14,6 +14,7 @@ import net.corda.testing.* import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Before import org.junit.Test diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt index 5ec5da6014..f3a24002cd 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt @@ -14,7 +14,7 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProviderImpl import net.corda.testing.* -import net.corda.testing.node.MockAttachmentStorage +import net.corda.testing.services.MockAttachmentStorage import org.junit.Assert.* import org.junit.Rule import org.junit.Test diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderTests.kt index fecf0b4fb0..1d624a32f3 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderTests.kt @@ -19,7 +19,7 @@ import net.corda.nodeapi.internal.serialization.SerializeAsTokenContextImpl import net.corda.nodeapi.internal.serialization.attachmentsClassLoaderEnabledPropertyName import net.corda.nodeapi.internal.serialization.withTokenContext import net.corda.testing.* -import net.corda.testing.node.MockAttachmentStorage +import net.corda.testing.services.MockAttachmentStorage import org.apache.commons.io.IOUtils import org.junit.Assert.* import org.junit.Rule diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt index 155229a5bc..654bf323e3 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt @@ -12,7 +12,7 @@ import net.corda.nodeapi.internal.AttachmentsClassLoader import net.corda.nodeapi.internal.AttachmentsClassLoaderTests import net.corda.nodeapi.internal.serialization.kryo.CordaKryo import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 -import net.corda.testing.node.MockAttachmentStorage +import net.corda.testing.services.MockAttachmentStorage import net.corda.testing.rigorousMock import org.junit.Rule import org.junit.Test diff --git a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt index 074f3a619b..583b30b4fd 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -17,11 +17,11 @@ import net.corda.nodeapi.internal.config.User import net.corda.testing.DUMMY_NOTARY_NAME import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver -import net.corda.testing.internal.InternalDriverDSL +import net.corda.testing.node.internal.InternalDriverDSL import net.corda.testing.internal.performance.div -import net.corda.testing.internal.performance.startPublishingFixedRateInjector -import net.corda.testing.internal.performance.startReporter -import net.corda.testing.internal.performance.startTightLoopInjector +import net.corda.testing.node.internal.performance.startPublishingFixedRateInjector +import net.corda.testing.node.internal.performance.startReporter +import net.corda.testing.node.internal.performance.startTightLoopInjector import net.corda.testing.node.NotarySpec import org.junit.Before import org.junit.Ignore diff --git a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt index c3c579119f..2fc3f11770 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt @@ -24,7 +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.node.MockAttachmentStorage +import net.corda.testing.services.MockAttachmentStorage +import net.corda.testing.rigorousMock +import net.corda.testing.withTestSerialization import org.junit.Assert.assertEquals import org.junit.Test import java.net.URLClassLoader diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index 2c21ecbb26..1b1632a34c 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -34,7 +34,7 @@ import net.corda.testing.dummyCommand import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode import net.corda.testing.node.MockNodeParameters -import net.corda.testing.startFlow +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Test import java.nio.file.Paths diff --git a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt index c3b7b7544b..84383cf6e0 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt @@ -16,6 +16,8 @@ import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver +import net.corda.testing.dummyCommand +import net.corda.testing.node.startFlow import net.corda.testing.node.ClusterSpec import net.corda.testing.node.NotarySpec import org.junit.Test diff --git a/node/src/integration-test/kotlin/net/corda/node/services/UserAuthServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/UserAuthServiceTests.kt index 75b86c7d94..8805144b56 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/UserAuthServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/UserAuthServiceTests.kt @@ -16,7 +16,7 @@ import net.corda.node.services.config.SecurityConfiguration import net.corda.node.services.config.AuthDataSourceType import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.toConfig -import net.corda.testing.internal.NodeBasedTest +import net.corda.testing.node.internal.NodeBasedTest import net.corda.testing.* import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.junit.After diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt index 45e4754ab8..faa30be62b 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt @@ -2,12 +2,12 @@ package net.corda.node.services.network import net.corda.core.node.NodeInfo import net.corda.core.utilities.seconds +import net.corda.testing.node.internal.CompatibilityZoneParams import net.corda.testing.ALICE_NAME import net.corda.testing.BOB_NAME -import net.corda.testing.internal.CompatibilityZoneParams import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation -import net.corda.testing.internal.internalDriver +import net.corda.testing.node.internal.internalDriver import net.corda.testing.node.network.NetworkMapServer import org.assertj.core.api.Assertions.assertThat import org.junit.After diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt index 1807d5ad8f..1b98c4f54e 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt @@ -10,7 +10,7 @@ import net.corda.testing.ALICE_NAME import net.corda.testing.BOB_NAME import net.corda.testing.TestIdentity import net.corda.testing.chooseIdentity -import net.corda.testing.internal.NodeBasedTest +import net.corda.testing.node.internal.NodeBasedTest import org.junit.Before import org.junit.Test import kotlin.test.assertEquals diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt index 0075950722..178f470d6c 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt @@ -7,8 +7,10 @@ import net.corda.core.flows.InitiatingFlow import net.corda.core.identity.Party import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap +import net.corda.testing.chooseIdentity +import net.corda.testing.node.internal.NodeBasedTest +import net.corda.testing.node.startFlow import net.corda.testing.* -import net.corda.testing.internal.NodeBasedTest import org.assertj.core.api.Assertions.assertThat import org.junit.Test diff --git a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt index 6cd0058792..2acd637b5d 100644 --- a/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt @@ -1,6 +1,5 @@ package net.corda.node.utilities.registration -import com.google.common.net.HostAndPort import net.corda.core.crypto.Crypto import net.corda.core.identity.CordaX500Name import net.corda.core.internal.cert @@ -15,9 +14,9 @@ import net.corda.nodeapi.internal.crypto.X509Utilities 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.internal.CompatibilityZoneParams +import net.corda.testing.node.internal.CompatibilityZoneParams import net.corda.testing.driver.PortAllocation -import net.corda.testing.internal.internalDriver +import net.corda.testing.node.internal.internalDriver import net.corda.testing.node.network.NetworkMapServer import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt index 66960efb89..cdbb84c8e8 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt @@ -26,8 +26,9 @@ import net.corda.nodeapi.RPCApi import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.testing.* -import net.corda.testing.internal.NodeBasedTest +import net.corda.testing.node.internal.NodeBasedTest import net.corda.testing.messaging.SimpleMQClient +import net.corda.testing.node.startFlow import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.apache.activemq.artemis.api.core.SimpleString diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index a579538eab..ec6086fb7e 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -37,6 +37,7 @@ import net.corda.testing.* import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode import net.corda.testing.node.MockNodeParameters +import net.corda.testing.node.testActor import org.apache.commons.io.IOUtils import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.junit.After diff --git a/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt b/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt index af830013d4..8526b36bcd 100644 --- a/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt +++ b/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt @@ -1,7 +1,7 @@ package net.corda.node.internal.cordapp import net.corda.core.node.services.AttachmentStorage -import net.corda.testing.node.MockAttachmentStorage +import net.corda.testing.services.MockAttachmentStorage import org.junit.Assert import org.junit.Before import org.junit.Test diff --git a/testing/node-driver/src/test/java/net/corda/node/internal/security/PasswordTest.kt b/node/src/test/kotlin/net/corda/node/internal/security/PasswordTest.kt similarity index 100% rename from testing/node-driver/src/test/java/net/corda/node/internal/security/PasswordTest.kt rename to node/src/test/kotlin/net/corda/node/internal/security/PasswordTest.kt diff --git a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt index 7b08ece646..a2b321391d 100644 --- a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt @@ -18,6 +18,7 @@ import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.NotarySpec import net.corda.testing.node.MockNodeParameters +import net.corda.testing.node.startFlow import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.junit.After import org.junit.Before diff --git a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt index 576d55d870..ed6c808619 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt @@ -36,6 +36,7 @@ import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.node.* import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties +import net.corda.testing.services.MockAttachmentStorage import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index af4a80e3b2..d963030134 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -24,6 +24,7 @@ import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Assert.* import org.junit.Before diff --git a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt index 51db2b72c4..96909bb902 100644 --- a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt @@ -17,6 +17,7 @@ import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.* import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties +import net.corda.testing.node.testNodeConfiguration import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.After diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index 083001d9c2..90714adc40 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -37,6 +37,7 @@ import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.pumpReceive +import net.corda.testing.node.startFlow import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt index 734d046401..70cbc90835 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt @@ -20,7 +20,7 @@ import net.corda.testing.dummyCommand import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters import net.corda.testing.singleIdentity -import net.corda.testing.startFlow +import net.corda.testing.node.startFlow import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt index 7e55d36562..ad56bfa5bc 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt @@ -20,6 +20,7 @@ import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters +import net.corda.testing.node.startFlow import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt index 0163cc210b..66f4973877 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt @@ -30,7 +30,7 @@ import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.rigorousMock import net.corda.testing.node.MockNodeParameters -import net.corda.testing.startFlow +import net.corda.testing.node.startFlow import org.junit.After import org.junit.Test import java.util.* diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt index dec45410ef..ec8d1c961d 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt @@ -15,7 +15,7 @@ import net.corda.nodeapi.internal.crypto.getX509Certificate import net.corda.nodeapi.internal.crypto.loadKeyStore import net.corda.testing.ALICE_NAME import net.corda.testing.rigorousMock -import net.corda.testing.testNodeConfiguration +import net.corda.testing.node.testNodeConfiguration import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Before import org.junit.Rule diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt index 05f6f3c51c..38e051f1b6 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt @@ -23,9 +23,9 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow +import net.corda.testing.node.internal.poll import net.corda.testing.DUMMY_BANK_B_NAME import net.corda.testing.DUMMY_NOTARY_NAME -import net.corda.testing.internal.poll import java.io.InputStream import java.net.HttpURLConnection import java.net.URL diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaCordformTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaCordformTest.kt index 6890793aba..34432277a1 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaCordformTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaCordformTest.kt @@ -2,7 +2,7 @@ package net.corda.bank import net.corda.finance.DOLLARS import net.corda.finance.POUNDS -import net.corda.testing.internal.demorun.deployNodesThen +import net.corda.testing.node.internal.demorun.deployNodesThen import org.junit.Test class BankOfCordaCordformTest { diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt index 93b7843843..b135d7baa1 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt @@ -13,8 +13,8 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.config.NotaryConfig import net.corda.nodeapi.internal.config.User +import net.corda.testing.node.internal.demorun.* import net.corda.testing.BOC_NAME -import net.corda.testing.internal.demorun.* import java.util.* import kotlin.system.exitProcess diff --git a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/contract/IRSTests.kt b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/contract/IRSTests.kt index b796a308d9..262681c0c6 100644 --- a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/contract/IRSTests.kt +++ b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/contract/IRSTests.kt @@ -26,6 +26,8 @@ import net.corda.finance.contracts.Tenor import net.corda.node.services.api.IdentityServiceInternal import net.corda.testing.* import net.corda.testing.node.MockServices +import net.corda.testing.node.ledger +import net.corda.testing.node.transaction import org.junit.Rule import org.junit.Test import java.math.BigDecimal diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/test/spring/SpringDriver.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/test/spring/SpringDriver.kt index a55e1a3a5c..c7d7a57ffd 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/test/spring/SpringDriver.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/test/spring/SpringDriver.kt @@ -7,8 +7,8 @@ import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.WebserverHandle -import net.corda.testing.internal.* import net.corda.testing.node.NotarySpec +import net.corda.testing.node.internal.* import okhttp3.OkHttpClient import okhttp3.Request import java.net.ConnectException diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt index af8525a222..02fc5ca84d 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt @@ -24,7 +24,7 @@ import net.corda.irs.flows.FixingFlow import net.corda.testing.chooseIdentity import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.makeTestIdentityService -import net.corda.testing.startFlow +import net.corda.testing.node.startFlow import rx.Observable import java.time.LocalDate import java.util.* diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt index 57d2e9e28a..eb11a9b443 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt @@ -11,9 +11,9 @@ import net.corda.node.services.config.NotaryConfig import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.minCorrectReplicas import net.corda.nodeapi.internal.ServiceIdentityGenerator +import net.corda.testing.node.internal.demorun.* import net.corda.testing.ALICE_NAME import net.corda.testing.BOB_NAME -import net.corda.testing.internal.demorun.* import java.nio.file.Paths fun main(args: Array) = BFTNotaryCordform().deployNodes() diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Clean.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Clean.kt index 7ab6fff629..91aa5ba967 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Clean.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Clean.kt @@ -1,6 +1,6 @@ package net.corda.notarydemo -import net.corda.testing.internal.demorun.clean +import net.corda.testing.node.internal.demorun.clean fun main(args: Array) { listOf(SingleNotaryCordform(), RaftNotaryCordform(), BFTNotaryCordform()).forEach { diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt index 5e5aa47d34..91dc8c5f39 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/CustomNotaryCordform.kt @@ -3,8 +3,8 @@ package net.corda.notarydemo import net.corda.cordform.CordformContext import net.corda.cordform.CordformDefinition import net.corda.node.services.config.NotaryConfig +import net.corda.testing.node.internal.demorun.* import net.corda.testing.* -import net.corda.testing.internal.demorun.* import java.nio.file.Paths fun main(args: Array) = CustomNotaryCordform().deployNodes() diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt index 23acaeceac..cfc21fa060 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt @@ -10,9 +10,9 @@ import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.RaftConfig import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.nodeapi.internal.ServiceIdentityGenerator +import net.corda.testing.node.internal.demorun.* import net.corda.testing.ALICE_NAME import net.corda.testing.BOB_NAME -import net.corda.testing.internal.demorun.* import java.nio.file.Paths fun main(args: Array) = RaftNotaryCordform().deployNodes() diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt index 85732133f5..4910a57d3e 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt @@ -5,8 +5,8 @@ import net.corda.cordform.CordformDefinition import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.config.NotaryConfig import net.corda.nodeapi.internal.config.User +import net.corda.testing.node.internal.demorun.* import net.corda.testing.* -import net.corda.testing.internal.demorun.* import java.nio.file.Paths fun main(args: Array) = SingleNotaryCordform().deployNodes() diff --git a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index 582b5efacf..51bc7c99da 100644 --- a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -12,7 +12,7 @@ import net.corda.nodeapi.internal.config.User import net.corda.testing.* import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver -import net.corda.testing.internal.poll +import net.corda.testing.node.internal.poll import net.corda.traderdemo.flow.BuyerFlow import net.corda.traderdemo.flow.CommercialPaperIssueFlow import net.corda.traderdemo.flow.SellerFlow diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt index b20dc19fda..c618c400cd 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt @@ -8,13 +8,13 @@ import net.corda.core.internal.readLines import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow import net.corda.node.internal.NodeStartup +import net.corda.testing.common.internal.ProjectStructure.projectRootDir +import net.corda.testing.node.internal.addressMustBeBound +import net.corda.testing.node.internal.addressMustNotBeBound +import net.corda.testing.node.internal.internalDriver import net.corda.testing.DUMMY_BANK_A_NAME import net.corda.testing.DUMMY_NOTARY_NAME -import net.corda.testing.common.internal.ProjectStructure.projectRootDir import net.corda.testing.http.HttpApi -import net.corda.testing.internal.addressMustBeBound -import net.corda.testing.internal.addressMustNotBeBound -import net.corda.testing.internal.internalDriver import net.corda.testing.node.NotarySpec import org.assertj.core.api.Assertions.assertThat import org.json.simple.JSONObject diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/FlowStackSnapshotTest.kt similarity index 99% rename from testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt rename to testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/FlowStackSnapshotTest.kt index c6e01c4193..8c3636ffc1 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/FlowStackSnapshotTest.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/FlowStackSnapshotTest.kt @@ -1,4 +1,4 @@ -package net.corda.testing +package net.corda.testing.node import co.paralleluniverse.fibers.Suspendable import net.corda.client.jackson.JacksonSupport @@ -11,7 +11,6 @@ import net.corda.core.serialization.CordaSerializable import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.internal.config.User import net.corda.testing.driver.driver -import net.corda.testing.node.MockNetwork import org.junit.Ignore import org.junit.Test import java.nio.file.Path diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/internal/ProcessUtilitiesTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/ProcessUtilitiesTests.kt similarity index 96% rename from testing/node-driver/src/integration-test/kotlin/net/corda/testing/internal/ProcessUtilitiesTests.kt rename to testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/ProcessUtilitiesTests.kt index 0e6b8a6961..a1822d0e3a 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/internal/ProcessUtilitiesTests.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/ProcessUtilitiesTests.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal +package net.corda.testing.node.internal import org.apache.commons.io.FileUtils import org.junit.Rule diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index db3f0ea88f..45cd567f90 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -14,10 +14,10 @@ import net.corda.node.internal.StartedNode import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.VerifierType import net.corda.nodeapi.internal.config.User +import net.corda.testing.node.internal.DriverDSLImpl +import net.corda.testing.node.internal.genericDriver +import net.corda.testing.node.internal.getTimestampAsDirectoryName import net.corda.testing.DUMMY_NOTARY_NAME -import net.corda.testing.internal.DriverDSLImpl -import net.corda.testing.internal.genericDriver -import net.corda.testing.internal.getTimestampAsDirectoryName import net.corda.testing.node.NotarySpec import java.net.InetSocketAddress import java.net.ServerSocket diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index e322c7b753..4871faafbd 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -47,7 +47,6 @@ import net.corda.testing.internal.testThreadFactory import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.setGlobalSerialization -import net.corda.testing.testNodeConfiguration import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.sshd.common.util.security.SecurityUtils import rx.internal.schedulers.CachedThreadScheduler diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index c04165319e..f5926637ac 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -32,6 +32,8 @@ 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.services.MockAttachmentStorage +import net.corda.testing.services.MockCordappProvider import org.bouncycastle.operator.ContentSigner import rx.Observable import rx.subjects.PublishSubject diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt similarity index 98% rename from testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt rename to testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt index b2544b3245..6c66284a2e 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt @@ -1,6 +1,6 @@ @file:JvmName("NodeTestUtils") -package net.corda.testing +package net.corda.testing.node import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever @@ -22,6 +22,7 @@ import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.VerifierType import net.corda.nodeapi.internal.config.User +import net.corda.testing.* import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import java.nio.file.Path diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt similarity index 99% rename from testing/node-driver/src/main/kotlin/net/corda/testing/internal/DriverDSLImpl.kt rename to testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index 67554aa621..fb92e5b085 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal +package net.corda.testing.node.internal import com.google.common.collect.HashMultimap import com.google.common.util.concurrent.ThreadFactoryBuilder @@ -45,11 +45,12 @@ import net.corda.nodeapi.internal.crypto.save import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.* import net.corda.testing.driver.* -import net.corda.testing.internal.DriverDSLImpl.ClusterType.NON_VALIDATING_RAFT -import net.corda.testing.internal.DriverDSLImpl.ClusterType.VALIDATING_RAFT import net.corda.testing.node.ClusterSpec import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.NotarySpec +import net.corda.testing.node.internal.DriverDSLImpl.ClusterType.NON_VALIDATING_RAFT +import net.corda.testing.node.internal.DriverDSLImpl.ClusterType.VALIDATING_RAFT +import net.corda.testing.setGlobalSerialization import okhttp3.OkHttpClient import okhttp3.Request import rx.Observable diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalTestUtils.kt similarity index 98% rename from testing/node-driver/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt rename to testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalTestUtils.kt index b24db80a3f..7a5bfc4f71 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalTestUtils.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal +package net.corda.testing.node.internal import net.corda.core.CordaException import net.corda.core.concurrent.CordaFuture diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt similarity index 98% rename from testing/node-driver/src/main/kotlin/net/corda/testing/internal/NodeBasedTest.kt rename to testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt index 349f56df22..0cb88acbc0 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal +package net.corda.testing.node.internal import net.corda.core.identity.CordaX500Name import net.corda.core.internal.concurrent.fork @@ -17,6 +17,7 @@ import net.corda.nodeapi.internal.config.User import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.getFreeLocalPorts +import net.corda.testing.internal.testThreadFactory import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import org.apache.logging.log4j.Level import org.junit.After diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/ProcessUtilities.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ProcessUtilities.kt similarity index 98% rename from testing/node-driver/src/main/kotlin/net/corda/testing/internal/ProcessUtilities.kt rename to testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ProcessUtilities.kt index 4c30aaeae6..f8d4a01f98 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/ProcessUtilities.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ProcessUtilities.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal +package net.corda.testing.node.internal import net.corda.core.internal.div import net.corda.core.internal.exists diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/RPCDriver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt similarity index 99% rename from testing/node-driver/src/main/kotlin/net/corda/testing/internal/RPCDriver.kt rename to testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt index 9684db5450..f851d7fb8d 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/RPCDriver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal +package net.corda.testing.node.internal import net.corda.client.mock.Generator import net.corda.client.rpc.internal.KryoClientSerializationScheme diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/ShutdownManager.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ShutdownManager.kt similarity index 98% rename from testing/node-driver/src/main/kotlin/net/corda/testing/internal/ShutdownManager.kt rename to testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ShutdownManager.kt index 022569b958..57114425f6 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/ShutdownManager.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ShutdownManager.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal +package net.corda.testing.node.internal import net.corda.core.concurrent.CordaFuture import net.corda.core.internal.ThreadBox diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/demorun/CordformUtils.kt similarity index 93% rename from testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt rename to testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/demorun/CordformUtils.kt index 9e566a0867..25ab1bed6b 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/demorun/CordformUtils.kt @@ -1,6 +1,6 @@ @file:JvmName("CordformUtils") -package net.corda.testing.internal.demorun +package net.corda.testing.node.internal.demorun import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/DemoRunner.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/demorun/DemoRunner.kt similarity index 93% rename from testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/DemoRunner.kt rename to testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/demorun/DemoRunner.kt index a13badccd4..185a851e99 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/DemoRunner.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/demorun/DemoRunner.kt @@ -1,6 +1,6 @@ @file:JvmName("DemoRunner") -package net.corda.testing.internal.demorun +package net.corda.testing.node.internal.demorun import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode @@ -8,7 +8,7 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow import net.corda.testing.driver.JmxPolicy import net.corda.testing.driver.PortAllocation -import net.corda.testing.internal.internalDriver +import net.corda.testing.node.internal.internalDriver fun CordformDefinition.clean() { System.err.println("Deleting: $nodesDirectory") @@ -40,7 +40,7 @@ private fun CordformDefinition.runNodes(waitForAllNodesToFinish: Boolean, block: .max()!! internalDriver( isDebug = true, - jmxPolicy = JmxPolicy(true), + jmxPolicy = JmxPolicy(true), driverDirectory = nodesDirectory, extraCordappPackagesToScan = cordappPackages, // Notaries are manually specified in Cordform so we don't want the driver automatically starting any diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/performance/Injectors.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/performance/Injectors.kt similarity index 95% rename from testing/node-driver/src/main/kotlin/net/corda/testing/internal/performance/Injectors.kt rename to testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/performance/Injectors.kt index b4b0f9f33d..0e73bbf070 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/performance/Injectors.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/performance/Injectors.kt @@ -1,9 +1,10 @@ -package net.corda.testing.internal.performance +package net.corda.testing.node.internal.performance import com.codahale.metrics.Gauge import com.codahale.metrics.MetricRegistry import com.google.common.base.Stopwatch -import net.corda.testing.internal.ShutdownManager +import net.corda.testing.internal.performance.Rate +import net.corda.testing.node.internal.ShutdownManager import java.time.Duration import java.util.* import java.util.concurrent.CountDownLatch diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/performance/Reporter.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/performance/Reporter.kt similarity index 92% rename from testing/node-driver/src/main/kotlin/net/corda/testing/internal/performance/Reporter.kt rename to testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/performance/Reporter.kt index 5446165087..00efdb38bf 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/performance/Reporter.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/performance/Reporter.kt @@ -1,9 +1,9 @@ -package net.corda.testing.internal.performance +package net.corda.testing.node.internal.performance import com.codahale.metrics.ConsoleReporter import com.codahale.metrics.JmxReporter import com.codahale.metrics.MetricRegistry -import net.corda.testing.internal.ShutdownManager +import net.corda.testing.node.internal.ShutdownManager import java.util.concurrent.TimeUnit import javax.management.ObjectName import kotlin.concurrent.thread diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt index effeec65c8..1fa551c6a1 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt @@ -13,8 +13,8 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction import net.corda.testing.contracts.DummyContract -import net.corda.testing.node.MockAttachmentStorage -import net.corda.testing.node.MockCordappProvider +import net.corda.testing.services.MockAttachmentStorage +import net.corda.testing.services.MockCordappProvider import java.io.InputStream import java.security.PublicKey import java.util.* diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/node/MockAttachment.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/node/MockAttachment.kt deleted file mode 100644 index ac717da3e0..0000000000 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/node/MockAttachment.kt +++ /dev/null @@ -1,9 +0,0 @@ -package net.corda.testing.node - -import net.corda.core.crypto.SecureHash -import net.corda.core.internal.AbstractAttachment - -/** - * An attachment with only an ID and an empty data array - */ -class MockAttachment(override val id: SecureHash = SecureHash.zeroHash) : AbstractAttachment({ ByteArray(0) }) \ No newline at end of file diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/node/MockAttachmentStorage.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/services/MockAttachmentStorage.kt similarity index 98% rename from testing/test-utils/src/main/kotlin/net/corda/testing/node/MockAttachmentStorage.kt rename to testing/test-utils/src/main/kotlin/net/corda/testing/services/MockAttachmentStorage.kt index c12e202b33..8cf1a9ccf2 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/node/MockAttachmentStorage.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/services/MockAttachmentStorage.kt @@ -1,4 +1,4 @@ -package net.corda.testing.node +package net.corda.testing.services import net.corda.core.contracts.Attachment import net.corda.core.crypto.SecureHash diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/node/MockCordappProvider.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/services/MockCordappProvider.kt similarity index 98% rename from testing/test-utils/src/main/kotlin/net/corda/testing/node/MockCordappProvider.kt rename to testing/test-utils/src/main/kotlin/net/corda/testing/services/MockCordappProvider.kt index e50f4dea84..dba07b73be 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/node/MockCordappProvider.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/services/MockCordappProvider.kt @@ -1,4 +1,4 @@ -package net.corda.testing.node +package net.corda.testing.services import net.corda.core.contracts.ContractClassName import net.corda.core.cordapp.Cordapp diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt index 4fa35ff2b9..791df21211 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt @@ -26,8 +26,8 @@ import net.corda.testing.driver.JmxPolicy import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver -import net.corda.testing.internal.* import net.corda.testing.node.NotarySpec +import net.corda.testing.node.internal.* import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.client.ActiveMQClient import org.apache.activemq.artemis.api.core.client.ClientProducer diff --git a/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt b/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt index 2f5a7a6e00..c76709dce5 100644 --- a/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt +++ b/webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt @@ -4,8 +4,8 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow import net.corda.testing.DUMMY_BANK_A_NAME import net.corda.testing.driver.WebserverHandle -import net.corda.testing.internal.addressMustBeBound -import net.corda.testing.internal.addressMustNotBeBound +import net.corda.testing.node.internal.addressMustBeBound +import net.corda.testing.node.internal.addressMustNotBeBound import net.corda.testing.driver.driver import org.junit.Test import java.util.concurrent.Executors From b58e2b89cd31cbcbe40b4fb4dce6427667c437cc Mon Sep 17 00:00:00 2001 From: Konstantinos Chalkias Date: Tue, 12 Dec 2017 20:08:57 +0000 Subject: [PATCH 7/9] TLS supports K1 and mixed K1-R1-RSA (#2216) --- .../node/utilities/TLSAuthenticationTests.kt | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt b/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt index a493f4d885..98ff559fe8 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt @@ -78,8 +78,7 @@ class TLSAuthenticationTests { client2TLSScheme = Crypto.ECDSA_SECP256R1_SHA256 ) - val (serverSocket, clientSocket) = - buildTLSSockets(serverSocketFactory, clientSocketFactory, 0, 0) + val (serverSocket, clientSocket) = buildTLSSockets(serverSocketFactory, clientSocketFactory, 0, 0) testConnect(serverSocket, clientSocket, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256") } @@ -95,12 +94,27 @@ class TLSAuthenticationTests { client2TLSScheme = Crypto.RSA_SHA256 ) - val (serverSocket, clientSocket) = - buildTLSSockets(serverSocketFactory, clientSocketFactory, 0, 0) + val (serverSocket, clientSocket) = buildTLSSockets(serverSocketFactory, clientSocketFactory, 0, 0) testConnect(serverSocket, clientSocket, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256") } + @Test + fun `All EC K1`() { + val (serverSocketFactory, clientSocketFactory) = buildTLSFactories( + rootCAScheme = Crypto.ECDSA_SECP256K1_SHA256, + intermediateCAScheme = Crypto.ECDSA_SECP256K1_SHA256, + client1CAScheme = Crypto.ECDSA_SECP256K1_SHA256, + client1TLSScheme = Crypto.ECDSA_SECP256K1_SHA256, + client2CAScheme = Crypto.ECDSA_SECP256K1_SHA256, + client2TLSScheme = Crypto.ECDSA_SECP256K1_SHA256 + ) + + val (serverSocket, clientSocket) = buildTLSSockets(serverSocketFactory, clientSocketFactory, 0, 0) + + testConnect(serverSocket, clientSocket, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256") + } + // Server's public key type is the one selected if users use different key types (e.g RSA and EC R1). @Test fun `Server RSA - Client EC R1 - CAs all EC R1`() { @@ -113,8 +127,7 @@ class TLSAuthenticationTests { client2TLSScheme = Crypto.ECDSA_SECP256R1_SHA256 ) - val (serverSocket, clientSocket) = - buildTLSSockets(serverSocketFactory, clientSocketFactory, 0, 0) + val (serverSocket, clientSocket) = buildTLSSockets(serverSocketFactory, clientSocketFactory, 0, 0) testConnect(serverSocket, clientSocket, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256") // Server's key type is selected. } @@ -148,6 +161,22 @@ class TLSAuthenticationTests { testConnect(serverSocket, clientSocket, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256") } + @Test + fun `Server EC K1 - Client EC R1 - CAs all RSA`() { + val (serverSocketFactory, clientSocketFactory) = buildTLSFactories( + rootCAScheme = Crypto.RSA_SHA256, + intermediateCAScheme = Crypto.RSA_SHA256, + client1CAScheme = Crypto.RSA_SHA256, + client1TLSScheme = Crypto.ECDSA_SECP256K1_SHA256, + client2CAScheme = Crypto.RSA_SHA256, + client2TLSScheme = Crypto.ECDSA_SECP256R1_SHA256 + ) + + val (serverSocket, clientSocket) = buildTLSSockets(serverSocketFactory, clientSocketFactory, 0, 0) + testConnect(serverSocket, clientSocket, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256") + } + + @Test fun `Server EC R1 - Client RSA - Mixed CAs`() { val (serverSocketFactory, clientSocketFactory) = buildTLSFactories( @@ -155,7 +184,7 @@ class TLSAuthenticationTests { intermediateCAScheme = Crypto.RSA_SHA256, client1CAScheme = Crypto.RSA_SHA256, client1TLSScheme = Crypto.ECDSA_SECP256R1_SHA256, - client2CAScheme = Crypto.ECDSA_SECP256R1_SHA256, + client2CAScheme = Crypto.ECDSA_SECP256K1_SHA256, client2TLSScheme = Crypto.RSA_SHA256 ) From d6df251e3601639f3a07cb4340d819dc0e6fa31b Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Tue, 12 Dec 2017 21:51:32 +0000 Subject: [PATCH 8/9] Bypass needless lookup. (#2236) --- core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt index 969724731b..c11589b8e9 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -103,7 +103,7 @@ class AttachmentTests { } }) val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME)) - val alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME) + val alice = aliceNode.info.singleIdentity() aliceNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java) bobNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java) val attachment = fakeAttachment() From 16833474312ea5c4e33a3cf65a66c0d307aa6afc Mon Sep 17 00:00:00 2001 From: josecoll Date: Wed, 13 Dec 2017 08:41:43 +0000 Subject: [PATCH 9/9] Clone of equivalent design directory from Enterprise repo. (#2120) --- docs/source/design/README.md | 40 +++ docs/source/design/designReviewProcess.md | 106 ++++++++ docs/source/design/designReviewProcess.png | Bin 0 -> 153128 bytes .../designTemplate/decisions/decision.md | 39 +++ docs/source/design/designTemplate/design.md | 242 ++++++++++++++++++ 5 files changed, 427 insertions(+) create mode 100644 docs/source/design/README.md create mode 100644 docs/source/design/designReviewProcess.md create mode 100644 docs/source/design/designReviewProcess.png create mode 100644 docs/source/design/designTemplate/decisions/decision.md create mode 100644 docs/source/design/designTemplate/design.md diff --git a/docs/source/design/README.md b/docs/source/design/README.md new file mode 100644 index 0000000000..94161e68bc --- /dev/null +++ b/docs/source/design/README.md @@ -0,0 +1,40 @@ +![Corda](https://www.corda.net/wp-content/uploads/2016/11/fg005_corda_b.png) + + + +# Design Documentation + +This directory should be used to version control Corda design documents. + +These should be written in [Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) (a design template is provided for general guidance) and follow the design review process outlined below. It is recommended you use a Markdown editor such as [Typora](https://typora.io/), or an appropriate plugin for your favourite editor (eg. [Sublime Markdown editing theme](http://plaintext-productivity.net/2-04-how-to-set-up-sublime-text-for-markdown-editing.html)). + +## Design Review Process + +Please see the [design review process](./designReviewProcess.md). + +* Feature request submission +* High level design +* Review / approve gate +* Technical design +* Review / approve gate +* Plan, prototype, implement, QA + +## Design Template + +Please copy this [directory](./designTemplate) to a new location under `/docs/source/design` (use a meaningful short descriptive directory name) and use the [Design Template](./designTemplate/design.md) contained within to guide writing your Design Proposal. Whilst the section headings may be treated as placeholders for guidance, you are expected to be able to answer any questions related to pertinent section headings (where relevant to your design) at the design review stage. Use the [Design Decision Template](./designTemplate/decisions/decision.md) (as many times as needed) to record the pros and cons, and justification of any design decision recommendations where multiple options are available. These should be directly referenced from the *Design Decisions* section of the main design document. + +The design document may be completed in one or two iterations, by completing the following main two sections individually or singularly: + +* High level design + Where a feature requirement is specified at a high level, and multiple design solutions are possible, this section should be completed and circulated for review prior to completing the detailed technical design. + High level designs will often benefit from a formal meeting and discussion review amongst stakeholders to reach consensus on the preferred way to proceed. The design author will then incorporate all meeting outcome decisions back into a revision for final GitHub PR approval. +* Technical design + The technical design will consist of implementation specific details which require a deeper understanding of the Corda software stack, such as public API's and services, libraries, and associated middleware infrastructure (messaging,security, database persistence, serialization) used to realize these. + Technical designs should lead directly to a GitHub PR review process. + +Once a design is approved using the GitHub PR process, please commit the PR to the GitHub repository with a meaningful version identifier (eg. my super design document - **V1.0**) + +## Design Repository + +All design documents will be version controlled under github under the directory `/docs/source/design`. +For designs that relate to Enterprise-only features (and that may contain proprietary IP), these should be stored under the [Enterprise Github repository](https://github.com/corda/enterprise). All other public designs should be stored under the [Open Source Github repository](https://github.com/corda/corda). diff --git a/docs/source/design/designReviewProcess.md b/docs/source/design/designReviewProcess.md new file mode 100644 index 0000000000..57cc17b5b3 --- /dev/null +++ b/docs/source/design/designReviewProcess.md @@ -0,0 +1,106 @@ + +# Overview + +The Corda Design Review process defines a means of editing, storing, collaborating, reviewing and approving Corda documentation in a consistent, structured, easily accessible and open manner. + +# Background + +Historically, Corda design documentation has been produced in an ad hoc fashion to include: +* Multiple sources and formats of storage + * Internal ([Tech/Arch technical discussion](https://r3-cev.atlassian.net/wiki/spaces/AR/pages/2588746/Internal+Technical+Discussion)) and External ([AWG design documents](https://r3-cev.atlassian.net/wiki/spaces/AWG/pages/56623418/Design+Documents)) facing wiki(s) + * [Public github wiki](https://github.com/corda/corda/wiki) + * [Discourse posts](https://discourse.corda.net/c/corda-discussion) +* Multiple authored versions of same design with differing coverage + * Elaboration and/or additions to scope + * Differing opinions, proposals, suggestions. +* Unstructured prose (no consistency in format and structure) +* Lack of versioning (wiki documents typically evolve without versioned references) +* Lack of traceability (audit) to original requirement(s) +* Undefined review and approval process, leading to misunderstandings and open interpretations at time of implementation by platform development team +* Lack of proposed implementation plan (time, resources, effort). +* Often missing stakeholder collaboration and review in the feedback cycle. + +# Process + +This process specifies: + +1. Usage of a design template to include: + * Versioning: design documents can be referenced at a point in time, and evolve from such. + * Review and approval history: incorporating relevant stakeholders from R3 (Platform, Product Management, Services) and + other relevant review groups (community, partners, customers, key collaborators) as deemed appropriate to the request. Ensure design + meets the requirements and is realizable within a proposed implementation timeframe. + * Consistent structure and headings: top level headings should be preserved, second level headings provide guidance on + content to include, and may be omitted where not relevant. + * The design template includes both High Level (conceptual, logical) and Technical (implementation specific) sections. + * Design decisions are clearly identified with pros/cons of proposed options, and agreed recommendation. + +2. Document review and approval by relevant stakeholders and impacted parties to include R3 organisational units, such as Platform Engineering, Product Management and Services (where relevant), and key stakeholders, to include customers, partners, key collaborators, and community leaders. + * Product owner (originator of requirements) + * Design Approval Board (DAB) + * Platform Development technical lead (and/or nominated developer(s)) + * Project Technical Lead / Solution Architect (if originating from an R3 Technical Services project) + * Other identified stakeholders (community leaders, partners, customers, key collaborators) + +3. Planning: allocation to Corda (open source) or Enterprise project JIRA epic(s) (and/or set of stories) and prioritisation within Product Backlog for future implementation within a Development Team Sprint. + +4. Document repository locations, according to whether the design is related to Open Source or Enterprise (internal only). + The recommended repository source is GitHub, and documents should be stored in [Markdown](https://en.wikipedia.org/wiki/Markdown). + The collaboration and review process should follow the standard [GitHub Pull Request](https://confluence.atlassian.com/bitbucket/work-with-pull-requests-223220593.html) mechanism. + * [Enterprise Github repository](https://github.com/corda/enterprise) + * [Open Source Github repository](https://github.com/corda/corda) + +The following diagram illustrates the process flow: + +![Design Review Process](./designReviewProcess.png) + +# Review Groups +Design documents should include all relevant stakeholders in their distribution (mostly as PR reviewers in github). This will often vary and depend on the origin of the Feature Request, particularly for high level business requirements. Technical Design Documents will tend to include a small set of stakeholders (Design Approval Board, Platform Development, DevOps). Final approval authority lays with at least one member of the Design Approval Board (DAB) or nominated delegate(s). + +Design Approval Board (DAB) +* Richard G Brown (CTO) +* James Carlyle (Chief Engineer) +* Mike Hearn (Lead Platform Engineer) +* Mark Oldfield (Lead Platform Architect) +* Jonathan Sartin (Information Security manager) +* Select external key contributors (directly involved in design process) + +Other review groups inlcude: + +* Product Management + +* Developer Relations + +* Platform Development Team Leads + + (may nominate team members as design leads) + +* DevOps + +* Services – Project (Incubation & Acceleration) + + * Nominated project leads + + Services – Technical (Consulting) + * Nominated solution architects + +* External + + * AWG (general) + * Consortium members + * ISV, SI, Partners + * Customers + * Key collaborators + +# Applicability and Timing + +This process should be applied to any major feature request gathered by the product management team or lead technologists that has been entered in the product backlog as a requirement, and has been prioritized for imminent execution. + +Publication and distribution of a design document from initial review to full approval will vary on a case by case basis. + +In general, + * High Level designs may require a longer approval cycle as they may need to host a formal review meeting with the DAB in attendance, + and will typically have larger stakeholder audiences (potentially including external reviewers), thus leading to multiple iterations of revision. + In either case the High Level design must be raised as a GitHub PR and obtain formal approval by reviewers. + * Technical designs are anticipated to go through a shorter cycle, with immediate feedback via the GitHub PR workflow. + Once approved, a Technical Design should be decomposed into a set of implementable Epic/Stories for prioritization and + scheduling as part of Development team(s) delivery cycle(s). diff --git a/docs/source/design/designReviewProcess.png b/docs/source/design/designReviewProcess.png new file mode 100644 index 0000000000000000000000000000000000000000..c694eea221b51b9e7a626018f46c77a35962f812 GIT binary patch literal 153128 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 +============================================ + +## Background / Context + +Short outline of decision point. + +## Options Analysis + +### A. diff --git a/docs/source/design/designTemplate/design.md b/docs/source/design/designTemplate/design.md new file mode 100644 index 0000000000..37c8ed3660 --- /dev/null +++ b/docs/source/design/designTemplate/design.md @@ -0,0 +1,242 @@ +![Corda](https://www.corda.net/wp-content/uploads/2016/11/fg005_corda_b.png) + +# Design Template + +Please read the [Design Review Process](../designReviewProcess.md) before completing a design. + +This design template should be used for capturing new Corda feature requests that have been raised as JIRA requirements stories by the product management team. The design may be completed in two stages depending on the complexity and scope of the new feature. + +1. High-level: conceptual designs based on business requirements and/or technical vision. Without detailing implementation, this level of design should position the overall solution within the Corda architecture from a logical perspective (independent from code implementation). It should illustrate and walk through the use case scenarios intended to be satisfied by this new feature request. The design should consider non-functional aspects of the system such as performance, scalability, high availability, security, and operational aspects such as management and monitoring. + +This section of the document should go through a formal review process (eg. presentation of design at meeting and subsequent PR review workflow) + +2. Technical: implementable designs with reference to Corda code. This level of design should focus on API specifications, service definitions, public library additions, data models and schemas, code modularity, configuration, execution and deployment of the new feature. It should also list any new software libraries, frameworks or development approaches to be adopted. The technical design should also consider all aspects of the test lifecycle (unit, integration, smoke tests, performance). + +This section of the document should be raised as a PR for development team review. + +An outcome of the Design Document should be an implementation plan that defines JIRA stories and tasks to be completed to produce shippable, demonstrable, executable code. + +Please complete and/or remove section headings as appropriate to the design being proposed. These are provided as guidance and to structure the design in a consistent and coherent manner. + +DOCUMENT MANAGEMENT +--- + +Design documents should follow the standard GitHub version management and pull request (PR) review workflow mechanism. + +## Document Control + +| Title | | +| -------------------- | ---------------------------------------- | +| Date | | +| Author | | +| Distribution | (see review groups in design review process) | +| Corda target version | (enterprise, open source and enterprise) | +| JIRA reference | (reference to primary Feature Request JIRA story outlining requirements) | + +## Approvals + +#### Document Sign-off + +| Author | | +| ----------------- | ---------------------------------------- | +| Reviewer(s) | (GitHub PR reviewers) | +| Final approver(s) | (GitHub PR approver(s) from Design Approval Board) | + +#### Design Decisions + +| Description | Recommendation | Approval* | +| ---------------------------------------- | --------------- | ----------------------- | +| [Design Decision 1](decisions/decision.md) | Selected option | (Design Approval Board) | +| [Design Decision 2](decisions/decision.md) | Selected option | (Design Approval Board) | +| [Design Decision 3](decisions/decision.md) | Selected option | (Design Approval Board) | + +\* only required for formal Design Approval Board meetings. + +## Document History + +To be managed by GitHub revision control +(please use meaningful identifiers when committing a PR approved design to GitHub - eg. my super design V1.0) + +HIGH LEVEL DESIGN +--- + +## Overview + +General overall of design proposal (goal, objectives, simple outline) + +## Background + +Description of existing solution (if any) and/or rationale for requirement. + +* Reference(s) to discussions held elsewhere (slack, wiki, etc). +* Definitions, acronyms and abbreviations + +## Scope + +* Goals +* Non-goals (eg. out of scope) +* Reference(s) to similar or related work + +## Timeline + +* Is this a short, medium or long-term solution? +* Outline timeline expectations + + Eg1. required for Customer Project X by end of Qy'2049) + + Eg2. required to release Enterprise Vx.y (reference roadmap) + +* Where short-term design, is this evolvable / extensible or stop-gap (eg. potentially throwaway)? + +## Requirements + +* Reference(s) to any of following: + + * Captured Product Backlog JIRA entry + + * Internal White Paper feature item and/or visionary feature + + * Project related requirement (POC, RFP, Pilot, Prototype) from + + * Internal Incubator / Accelerator project + + * Direct from Customer, ISV, SI, Partner +* Use Cases +* Assumptions + +## Design Decisions + +List of design decisions identified in defining the target solution: +(for each item, please complete the attached [Design Decision template](decisions/decision.md)) + +| Heading (link to completed Decision document using template) | Recommendation | +| ---------------------------------------- | -------------- | +| [Design Decision 1](decisions/decision.md) | Option A | +| [Design Decision 2](decisions/decision.md) | TBD* | +| [Design Decision 3](decisions/decision.md) | Option B | + +It is reasonable to expect decisions to be challenged prior to any formal review and approval. +*In certain scenarios the Design Decision itself may solicit a recommendation from reviewers. + +## Target Solution + +* Illustrate any business process with diagrams + + * Business Process Flow (or formal BPMN 2.0), swimlane activity + + * UML: activity, state, sequence + +* Illustrate operational solutions with deployment diagrams + + * Network + +* Validation matrix (against requirements) + + * Role, requirement, how design satisfies requirement + +* Sample walk through (against Use Cases) + +* Implications + + * Technical + * Operational + * Security + +* Adherence to existing industry standards or approaches +* List any standards to be followed / adopted +* Outstanding issues + +## Complementary solutions + +Other solutions that provide similar functionality and/or overlap with the proposed. +Where overlap with existing solution(s), describe how this design fits in and complements the current state. + +## Final recommendation + +* Proposed solution (if more than one option presented) +* Proceed direct to implementation +* Proceed to Technical Design stage +* Proposed Platform Technical team(s) to implement design (if not already decided) + +TECHNICAL DESIGN +--- + +## Interfaces + +* Public APIs impact +* Internal APIs impacted +* Modules impacted + + * Illustrate with Software Component diagrams + +## Functional + +* UI requirements + + * Illustrate with UI Mockups and/or Wireframes + +* (Subsystem) Components descriptions and interactions) + + Consider and list existing impacted components and services within Corda: + + * Doorman + * Network Map + * Public API's (ServiceHub, RPCOps) + * Vault + * Notaries + * Identity services + * Flow framework + * Attachments + * Core data structures, libraries or utilities + * Testing frameworks + * Pluggable infrastructure: DBs, Message Brokers, LDAP + +* Data model & serialization impact and changes required + + * Illustrate with ERD diagrams + +* Infrastructure services: persistence (schemas), messaging + +## Non-Functional + +* Performance +* Scalability +* High Availability + +## Operational + +* Deployment + + * Versioning + +* Maintenance + + * Upgradability, migration + +* Management + + * Audit, alerting, monitoring, backup/recovery, archiving + +## Security + +* Data privacy +* Authentication +* Access control + +## Software Development Tools and Programming Standards to be adopted. + +* languages +* frameworks +* 3rd party libraries +* architectural / design patterns +* supporting tools + +## Testability + +* Unit +* Integration +* Smoke +* Non-functional (performance) + +APPENDICES +---