From 2ceb6283af0ece9e39d3ec907b0586b8f01bb5e0 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Mon, 13 Nov 2017 14:35:26 +0000 Subject: [PATCH 01/27] Moved X509Utilities, and some other crypto utilities in node, into node-api so that they can be used by services outside of the node. There's also some cleanup as well. --- .../corda/core/crypto/CompositeKeyTests.kt | 2 +- .../core/crypto/X509NameConstraintsTest.kt | 6 +- .../core/identity/PartyAndCertificateTest.kt | 4 +- .../internal/crypto}/ContentSignerBuilder.kt | 2 +- .../internal/crypto}/KeyStoreUtilities.kt | 46 +----- .../internal/crypto/KeyStoreWrapper.kt | 52 +++++++ .../nodeapi/internal/crypto}/X509Utilities.kt | 137 ++++++++++++------ .../amqp/custom/X509CertificateSerializer.kt | 4 +- .../internal/serialization/kryo/Kryo.kt | 4 +- .../messaging/MQSecurityAsNodeTest.kt | 2 +- .../net/corda/node/internal/AbstractNode.kt | 8 +- .../node/services/config/ConfigUtilities.kt | 2 +- .../identity/InMemoryIdentityService.kt | 8 +- .../identity/PersistentIdentityService.kt | 16 +- .../net/corda/node/services/keys/KMSUtils.kt | 11 +- .../messaging/ArtemisMessagingServer.kt | 8 +- .../services/messaging/P2PMessagingClient.kt | 10 +- .../services/messaging/RPCMessagingClient.kt | 7 +- .../utilities/ServiceIdentityGenerator.kt | 1 + .../HTTPNetworkRegistrationService.kt | 6 +- .../registration/NetworkRegistrationHelper.kt | 8 +- .../identity/InMemoryIdentityServiceTests.kt | 14 +- .../PersistentIdentityServiceTests.kt | 20 ++- .../services/network/NetworkMapClientTest.kt | 4 +- .../services/network/TestNodeInfoFactory.kt | 11 +- .../node/utilities/TLSAuthenticationTests.kt | 1 + .../corda/node/utilities/X509UtilitiesTest.kt | 18 ++- .../NetworkisRegistrationHelperTest.kt | 6 +- .../kotlin/net/corda/testing/CoreTestUtils.kt | 13 +- .../kotlin/net/corda/testing/TestConstants.kt | 8 +- 30 files changed, 248 insertions(+), 191 deletions(-) rename {node/src/main/kotlin/net/corda/node/utilities => node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto}/ContentSignerBuilder.kt (97%) rename {node/src/main/kotlin/net/corda/node/utilities => node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto}/KeyStoreUtilities.kt (77%) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt rename {node/src/main/kotlin/net/corda/node/utilities => node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto}/X509Utilities.kt (76%) diff --git a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt index 60b5306cb6..88724674f1 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt @@ -8,7 +8,7 @@ import net.corda.core.internal.div import net.corda.core.serialization.serialize import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.toBase58String -import net.corda.node.utilities.* +import net.corda.nodeapi.internal.crypto.* import net.corda.testing.kryoSpecific import net.corda.testing.SerializationEnvironmentRule import org.junit.Rule diff --git a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt index 88854550a4..b3b1f332e2 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt @@ -3,7 +3,7 @@ package net.corda.core.crypto import net.corda.core.identity.CordaX500Name import net.corda.core.internal.toTypedArray import net.corda.core.internal.cert -import net.corda.node.utilities.* +import net.corda.nodeapi.internal.crypto.* import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree @@ -50,7 +50,7 @@ class X509NameConstraintsTest { val nameConstraints = NameConstraints(acceptableNames, arrayOf()) val pathValidator = CertPathValidator.getInstance("PKIX") - val certFactory = CertificateFactory.getInstance("X509") + val certFactory = X509CertificateFactory().delegate assertFailsWith(CertPathValidatorException::class) { val (keystore, trustStore) = makeKeyStores(X500Name("CN=Bank B"), nameConstraints) @@ -85,7 +85,7 @@ class X509NameConstraintsTest { .map { GeneralSubtree(GeneralName(X500Name(it))) }.toTypedArray() val nameConstraints = NameConstraints(acceptableNames, arrayOf()) - val certFactory = CertificateFactory.getInstance("X509") + val certFactory = X509CertificateFactory().delegate Crypto.ECDSA_SECP256R1_SHA256 val pathValidator = CertPathValidator.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME) diff --git a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt index 0b839c08e8..bd6ef220a8 100644 --- a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt @@ -4,8 +4,8 @@ import net.corda.core.crypto.entropyToKeyPair import net.corda.core.internal.read import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize -import net.corda.node.utilities.KEYSTORE_TYPE -import net.corda.node.utilities.save +import net.corda.nodeapi.internal.crypto.KEYSTORE_TYPE +import net.corda.nodeapi.internal.crypto.save import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.getTestPartyAndCertificate import org.assertj.core.api.Assertions.assertThat diff --git a/node/src/main/kotlin/net/corda/node/utilities/ContentSignerBuilder.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilder.kt similarity index 97% rename from node/src/main/kotlin/net/corda/node/utilities/ContentSignerBuilder.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilder.kt index f2a365bf89..bfcf1f6631 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/ContentSignerBuilder.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilder.kt @@ -1,4 +1,4 @@ -package net.corda.node.utilities +package net.corda.nodeapi.internal.crypto import net.corda.core.crypto.SignatureScheme import org.bouncycastle.asn1.x509.AlgorithmIdentifier diff --git a/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt similarity index 77% rename from node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt index 0257302d6d..b49493ded2 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreUtilities.kt @@ -1,9 +1,8 @@ @file:JvmName("KeyStoreUtilities") -package net.corda.node.utilities +package net.corda.nodeapi.internal.crypto import net.corda.core.crypto.Crypto -import net.corda.core.identity.CordaX500Name import net.corda.core.internal.* import org.bouncycastle.cert.X509CertificateHolder import java.io.IOException @@ -11,9 +10,7 @@ import java.io.InputStream import java.io.OutputStream import java.nio.file.Path import java.security.* -import java.security.cert.CertPath import java.security.cert.Certificate -import java.security.cert.CertificateFactory import java.security.cert.X509Certificate const val KEYSTORE_TYPE = "JKS" @@ -169,44 +166,3 @@ fun KeyStore.getSupportedKey(alias: String, keyPassword: String): PrivateKey { val key = getKey(alias, keyPass) as PrivateKey return Crypto.toSupportedPrivateKey(key) } - -class KeyStoreWrapper(private val storePath: Path, private val storePassword: String) { - private val keyStore = storePath.read { loadKeyStore(it, storePassword) } - - private fun createCertificate(serviceName: CordaX500Name, pubKey: PublicKey): CertPath { - val clientCertPath = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA) - // Assume key password = store password. - val clientCA = certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) - // Create new keys and store in keystore. - val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, pubKey) - val certPath = CertificateFactory.getInstance("X509").generateCertPath(listOf(cert.cert) + clientCertPath) - require(certPath.certificates.isNotEmpty()) { "Certificate path cannot be empty" } - // TODO: X509Utilities.validateCertificateChain() - return certPath - } - - fun signAndSaveNewKeyPair(serviceName: CordaX500Name, privateKeyAlias: String, keyPair: KeyPair) { - val certPath = createCertificate(serviceName, keyPair.public) - // Assume key password = store password. - keyStore.addOrReplaceKey(privateKeyAlias, keyPair.private, storePassword.toCharArray(), certPath.certificates.toTypedArray()) - keyStore.save(storePath, storePassword) - } - - fun savePublicKey(serviceName: CordaX500Name, pubKeyAlias: String, pubKey: PublicKey) { - val certPath = createCertificate(serviceName, pubKey) - // Assume key password = store password. - keyStore.addOrReplaceCertificate(pubKeyAlias, certPath.certificates.first()) - keyStore.save(storePath, storePassword) - } - - // Delegate methods to keystore. Sadly keystore doesn't have an interface. - fun containsAlias(alias: String) = keyStore.containsAlias(alias) - - fun getX509Certificate(alias: String) = keyStore.getX509Certificate(alias) - - fun getCertificateChain(alias: String): Array = keyStore.getCertificateChain(alias) - - fun getCertificate(alias: String): Certificate = keyStore.getCertificate(alias) - - fun certificateAndKeyPair(alias: String): CertificateAndKeyPair = keyStore.getCertificateAndKeyPair(alias, storePassword) -} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt new file mode 100644 index 0000000000..3e0d526a58 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/KeyStoreWrapper.kt @@ -0,0 +1,52 @@ +package net.corda.nodeapi.internal.crypto + +import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.cert +import net.corda.core.internal.read +import java.nio.file.Path +import java.security.KeyPair +import java.security.PublicKey +import java.security.cert.CertPath +import java.security.cert.Certificate +import java.security.cert.CertificateFactory + +class KeyStoreWrapper(private val storePath: Path, private val storePassword: String) { + private val keyStore = storePath.read { loadKeyStore(it, storePassword) } + + private fun createCertificate(serviceName: CordaX500Name, pubKey: PublicKey): CertPath { + val clientCertPath = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA) + // Assume key password = store password. + val clientCA = certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) + // Create new keys and store in keystore. + val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, pubKey) + val certPath = X509CertificateFactory().delegate.generateCertPath(listOf(cert.cert) + clientCertPath) + require(certPath.certificates.isNotEmpty()) { "Certificate path cannot be empty" } + // TODO: X509Utilities.validateCertificateChain() + return certPath + } + + fun signAndSaveNewKeyPair(serviceName: CordaX500Name, privateKeyAlias: String, keyPair: KeyPair) { + val certPath = createCertificate(serviceName, keyPair.public) + // Assume key password = store password. + keyStore.addOrReplaceKey(privateKeyAlias, keyPair.private, storePassword.toCharArray(), certPath.certificates.toTypedArray()) + keyStore.save(storePath, storePassword) + } + + fun savePublicKey(serviceName: CordaX500Name, pubKeyAlias: String, pubKey: PublicKey) { + val certPath = createCertificate(serviceName, pubKey) + // Assume key password = store password. + keyStore.addOrReplaceCertificate(pubKeyAlias, certPath.certificates.first()) + keyStore.save(storePath, storePassword) + } + + // Delegate methods to keystore. Sadly keystore doesn't have an interface. + fun containsAlias(alias: String) = keyStore.containsAlias(alias) + + fun getX509Certificate(alias: String) = keyStore.getX509Certificate(alias) + + fun getCertificateChain(alias: String): Array = keyStore.getCertificateChain(alias) + + fun getCertificate(alias: String): Certificate = keyStore.getCertificate(alias) + + fun certificateAndKeyPair(alias: String): CertificateAndKeyPair = keyStore.getCertificateAndKeyPair(alias, storePassword) +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt similarity index 76% rename from node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt index 4dfa07ec09..def4ee9879 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt @@ -1,9 +1,11 @@ -package net.corda.node.utilities +package net.corda.nodeapi.internal.crypto import net.corda.core.crypto.Crypto import net.corda.core.crypto.SignatureScheme import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.read +import net.corda.core.internal.write import net.corda.core.internal.x500Name import net.corda.core.utilities.days import net.corda.core.utilities.millis @@ -25,10 +27,10 @@ import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder import org.bouncycastle.util.io.pem.PemReader -import java.io.FileReader import java.io.FileWriter import java.io.InputStream import java.math.BigInteger +import java.nio.file.Files import java.nio.file.Path import java.security.KeyPair import java.security.PublicKey @@ -52,6 +54,7 @@ object X509Utilities { const val CORDA_CLIENT_CA_CN = "Corda Client CA Certificate" private val DEFAULT_VALIDITY_WINDOW = Pair(0.millis, 3650.days) + /** * Helper function to return the latest out of an instant and an optional date. */ @@ -89,7 +92,9 @@ object X509Utilities { * Create a de novo root self-signed X509 v3 CA cert. */ @JvmStatic - fun createSelfSignedCACertificate(subject: CordaX500Name, keyPair: KeyPair, validityWindow: Pair = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder { + fun createSelfSignedCACertificate(subject: CordaX500Name, + keyPair: KeyPair, + validityWindow: Pair = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder { val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second) return createCertificate(CertificateType.ROOT_CA, subject.x500Name, keyPair, subject.x500Name, keyPair.public, window) } @@ -114,8 +119,9 @@ object X509Utilities { subject: CordaX500Name, subjectPublicKey: PublicKey, validityWindow: Pair = DEFAULT_VALIDITY_WINDOW, - nameConstraints: NameConstraints? = null): X509CertificateHolder - = createCertificate(certificateType, issuerCertificate, issuerKeyPair, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints) + nameConstraints: NameConstraints? = null): X509CertificateHolder { + return createCertificate(certificateType, issuerCertificate, issuerKeyPair, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints) + } /** * Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the @@ -145,10 +151,9 @@ object X509Utilities { @Throws(CertPathValidatorException::class) fun validateCertificateChain(trustedRoot: X509Certificate, vararg certificates: Certificate) { require(certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" } - val certFactory = CertificateFactory.getInstance("X509") val params = PKIXParameters(setOf(TrustAnchor(trustedRoot, null))) params.isRevocationEnabled = false - val certPath = certFactory.generateCertPath(certificates.toList()) + val certPath = X509CertificateFactory().delegate.generateCertPath(certificates.toList()) val pathValidator = CertPathValidator.getInstance("PKIX") pathValidator.validate(certPath, params) } @@ -156,30 +161,29 @@ object X509Utilities { /** * Helper method to store a .pem/.cer format file copy of a certificate if required for import into a PC/Mac, or for inspection. * @param x509Certificate certificate to save. - * @param filename Target filename. + * @param file Target file. */ @JvmStatic - fun saveCertificateAsPEMFile(x509Certificate: X509CertificateHolder, filename: Path) { - FileWriter(filename.toFile()).use { - JcaPEMWriter(it).use { - it.writeObject(x509Certificate) - } + fun saveCertificateAsPEMFile(x509Certificate: X509CertificateHolder, file: Path) { + JcaPEMWriter(file.toFile().writer()).use { + it.writeObject(x509Certificate) } } /** * Helper method to load back a .pem/.cer format file copy of a certificate. - * @param filename Source filename. + * @param file Source file. * @return The X509Certificate that was encoded in the file. */ @JvmStatic - fun loadCertificateFromPEMFile(filename: Path): X509CertificateHolder { - val reader = PemReader(FileReader(filename.toFile())) - val pemObject = reader.readPemObject() - val cert = X509CertificateHolder(pemObject.content) - return cert.apply { - isValidOn(Date()) + fun loadCertificateFromPEMFile(file: Path): X509CertificateHolder { + val cert = file.read { + val reader = PemReader(it.reader()) + val pemObject = reader.readPemObject() + X509CertificateHolder(pemObject.content) } + cert.isValidOn(Date()) + return cert } /** @@ -243,13 +247,13 @@ object X509Utilities { * @param validityWindow the time period the certificate is valid for. * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. */ - internal fun createCertificate(certificateType: CertificateType, - issuer: X500Name, - issuerSigner: ContentSigner, - subject: CordaX500Name, - subjectPublicKey: PublicKey, - validityWindow: Pair, - nameConstraints: NameConstraints? = null): X509CertificateHolder { + fun createCertificate(certificateType: CertificateType, + issuer: X500Name, + issuerSigner: ContentSigner, + subject: CordaX500Name, + subjectPublicKey: PublicKey, + validityWindow: Pair, + nameConstraints: NameConstraints? = null): X509CertificateHolder { val builder = createCertificate(certificateType, issuer, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints) return builder.build(issuerSigner).apply { require(isValidOn(Date())) @@ -266,11 +270,13 @@ object X509Utilities { * @param validityWindow the time period the certificate is valid for. * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. */ - internal fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerKeyPair: KeyPair, - subject: X500Name, subjectPublicKey: PublicKey, - validityWindow: Pair, - nameConstraints: NameConstraints? = null): X509CertificateHolder { - + fun createCertificate(certificateType: CertificateType, + issuer: X500Name, + issuerKeyPair: KeyPair, + subject: X500Name, + subjectPublicKey: PublicKey, + validityWindow: Pair, + nameConstraints: NameConstraints? = null): X509CertificateHolder { val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private) val provider = Crypto.findProvider(signatureScheme.providerName) val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) @@ -285,28 +291,71 @@ object X509Utilities { /** * Create certificate signing request using provided information. */ - internal fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair, signatureScheme: SignatureScheme): PKCS10CertificationRequest { + private fun createCertificateSigningRequest(subject: CordaX500Name, + email: String, + keyPair: KeyPair, + signatureScheme: SignatureScheme): PKCS10CertificationRequest { val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName)) return JcaPKCS10CertificationRequestBuilder(subject.x500Name, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer) } - fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair) = createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME) + fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair): PKCS10CertificationRequest { + return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME) + } } - -class CertificateStream(val input: InputStream) { - private val certificateFactory = CertificateFactory.getInstance("X.509") - - fun nextCertificate(): X509Certificate = certificateFactory.generateCertificate(input) as X509Certificate +/** + * Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best + * so assume this class is not. + */ +class X509CertificateFactory { + val delegate: CertificateFactory = CertificateFactory.getInstance("X.509") + fun generateCertificate(input: InputStream): X509Certificate { + return delegate.generateCertificate(input) as X509Certificate + } } enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurposeId, val isCA: Boolean) { - ROOT_CA(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true), - INTERMEDIATE_CA(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true), - CLIENT_CA(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true), - TLS(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyEncipherment or KeyUsage.keyAgreement), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = false), + ROOT_CA( + KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), + KeyPurposeId.id_kp_serverAuth, + KeyPurposeId.id_kp_clientAuth, + KeyPurposeId.anyExtendedKeyUsage, + isCA = true + ), + + INTERMEDIATE_CA( + KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), + KeyPurposeId.id_kp_serverAuth, + KeyPurposeId.id_kp_clientAuth, + KeyPurposeId.anyExtendedKeyUsage, + isCA = true + ), + + CLIENT_CA( + KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), + KeyPurposeId.id_kp_serverAuth, + KeyPurposeId.id_kp_clientAuth, + KeyPurposeId.anyExtendedKeyUsage, + isCA = true + ), + + TLS( + KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyEncipherment or KeyUsage.keyAgreement), + KeyPurposeId.id_kp_serverAuth, + KeyPurposeId.id_kp_clientAuth, + KeyPurposeId.anyExtendedKeyUsage, + isCA = false + ), + // TODO: Identity certs should have only limited depth (i.e. 1) CA signing capability, with tight name constraints - IDENTITY(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true) + IDENTITY( + KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign), + KeyPurposeId.id_kp_serverAuth, + KeyPurposeId.id_kp_clientAuth, + KeyPurposeId.anyExtendedKeyUsage, + isCA = true + ) } data class CertificateAndKeyPair(val certificate: X509CertificateHolder, val keyPair: KeyPair) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CertificateSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CertificateSerializer.kt index a1e3c17b8f..2942153bba 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CertificateSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CertificateSerializer.kt @@ -1,9 +1,9 @@ package net.corda.nodeapi.internal.serialization.amqp.custom +import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.serialization.amqp.* import org.apache.qpid.proton.codec.Data import java.lang.reflect.Type -import java.security.cert.CertificateFactory import java.security.cert.X509Certificate object X509CertificateSerializer : CustomSerializer.Implements(X509Certificate::class.java) { @@ -22,6 +22,6 @@ object X509CertificateSerializer : CustomSerializer.Implements( override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): X509Certificate { val bits = input.readObject(obj, schema, ByteArray::class.java) as ByteArray - return CertificateFactory.getInstance("X.509").generateCertificate(bits.inputStream()) as X509Certificate + return X509CertificateFactory().generateCertificate(bits.inputStream()) } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt index d3927dd47d..18b598919c 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt @@ -22,6 +22,7 @@ import net.corda.core.serialization.SerializedBytes import net.corda.core.toFuture import net.corda.core.toObservable import net.corda.core.transactions.* +import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.serialization.CordaClassResolver import net.corda.nodeapi.internal.serialization.serializationContextKey import org.slf4j.Logger @@ -486,8 +487,7 @@ object CertPathSerializer : Serializer() { @ThreadSafe object X509CertificateSerializer : Serializer() { override fun read(kryo: Kryo, input: Input, type: Class): X509Certificate { - val factory = CertificateFactory.getInstance("X.509") - return factory.generateCertificate(input.readBytesWithLength().inputStream()) as X509Certificate + return X509CertificateFactory().generateCertificate(input.readBytesWithLength().inputStream()) } override fun write(kryo: Kryo, output: Output, obj: X509Certificate) { 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 2d7b09cf1d..bd579f4f17 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 @@ -2,11 +2,11 @@ package net.corda.services.messaging import net.corda.core.crypto.Crypto import net.corda.core.internal.* -import net.corda.node.utilities.* 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.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 diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 79583b374f..16d0f09f05 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -57,7 +57,10 @@ import net.corda.node.services.upgrade.ContractUpgradeServiceImpl import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.VaultSoftLockManager import net.corda.node.shell.InteractiveShell -import net.corda.node.utilities.* +import net.corda.node.utilities.AffinityExecutor +import net.corda.node.utilities.CordaPersistence +import net.corda.node.utilities.configureDatabase +import net.corda.nodeapi.internal.crypto.* import org.apache.activemq.artemis.utils.ReusableLatch import org.slf4j.Logger import rx.Observable @@ -66,7 +69,6 @@ import java.lang.reflect.InvocationTargetException import java.security.KeyPair import java.security.KeyStoreException import java.security.PublicKey -import java.security.cert.CertificateFactory import java.security.cert.X509Certificate import java.sql.Connection import java.time.Clock @@ -697,7 +699,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, throw ConfigurationException("The name '$singleName' for $id doesn't match what's in the key store: $subject") } - val certPath = CertificateFactory.getInstance("X509").generateCertPath(certificates) + val certPath = X509CertificateFactory().delegate.generateCertPath(certificates) return Pair(PartyAndCertificate(certPath), keyPair) } diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index 9ba6a09c9f..17c927f3b6 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt @@ -8,8 +8,8 @@ import net.corda.core.crypto.Crypto import net.corda.core.crypto.SignatureScheme import net.corda.core.identity.CordaX500Name import net.corda.core.internal.* -import net.corda.node.utilities.* import net.corda.nodeapi.config.SSLConfiguration +import net.corda.nodeapi.internal.crypto.* import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.NameConstraints diff --git a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt index 77997cd721..7fbbd810b8 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt @@ -10,6 +10,7 @@ import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.contextLogger import net.corda.core.utilities.trace +import net.corda.nodeapi.internal.crypto.X509CertificateFactory import org.bouncycastle.cert.X509CertificateHolder import java.security.InvalidAlgorithmParameterException import java.security.PublicKey @@ -63,7 +64,7 @@ class InMemoryIdentityService(identities: Iterable = emptyS log.error("Certificate validation failed for ${identity.name} against trusted root ${trustAnchor.trustedCert.subjectX500Principal}.") log.error("Certificate path :") identity.certPath.certificates.reversed().forEachIndexed { index, certificate -> - val space = (0 until index).map { " " }.joinToString("") + val space = (0 until index).joinToString("") { " " } log.error("$space${certificate.toX509CertHolder().subject}") } throw e @@ -78,8 +79,7 @@ class InMemoryIdentityService(identities: Iterable = emptyS if (firstCertWithThisName != identity.certificate) { val certificates = identity.certPath.certificates val idx = certificates.lastIndexOf(firstCertWithThisName) - val certFactory = CertificateFactory.getInstance("X509") - val firstPath = certFactory.generateCertPath(certificates.slice(idx..certificates.size - 1)) + val firstPath = X509CertificateFactory().delegate.generateCertPath(certificates.slice(idx until certificates.size)) verifyAndRegisterIdentity(PartyAndCertificate(firstPath)) } @@ -104,7 +104,7 @@ class InMemoryIdentityService(identities: Iterable = emptyS val candidate = partyFromKey(party.owningKey) // TODO: This should be done via the network map cache, which is the authoritative source of well known identities return if (candidate != null) { - require(party.nameOrNull() == null || party.nameOrNull() == candidate.name) { "Candidate party ${candidate} does not match expected ${party}" } + require(party.nameOrNull() == null || party.nameOrNull() == candidate.name) { "Candidate party $candidate does not match expected $party" } wellKnownPartyFromX500Name(candidate.name) } else { null diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index a108ae0744..282e519a38 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -9,13 +9,13 @@ import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.serialization.SingletonSerializeAsToken -import net.corda.core.utilities.debug import net.corda.core.utilities.MAX_HASH_HEX_SIZE import net.corda.core.utilities.contextLogger +import net.corda.core.utilities.debug import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.NODE_DATABASE_PREFIX +import net.corda.nodeapi.internal.crypto.X509CertificateFactory import org.bouncycastle.cert.X509CertificateHolder -import java.io.ByteArrayInputStream import java.security.InvalidAlgorithmParameterException import java.security.PublicKey import java.security.cert.* @@ -32,16 +32,15 @@ class PersistentIdentityService(override val trustRoot: X509Certificate, companion object { private val log = contextLogger() - private val certFactory: CertificateFactory = CertificateFactory.getInstance("X.509") fun createPKMap(): AppendOnlyPersistentMap { return AppendOnlyPersistentMap( toPersistentEntityKey = { it.toString() }, fromPersistentEntity = { - Pair(SecureHash.parse(it.publicKeyHash), - PartyAndCertificate(ByteArrayInputStream(it.identity).use { - certFactory.generateCertPath(it) - })) + Pair( + SecureHash.parse(it.publicKeyHash), + PartyAndCertificate(X509CertificateFactory().delegate.generateCertPath(it.identity.inputStream())) + ) }, toPersistentEntity = { key: SecureHash, value: PartyAndCertificate -> PersistentIdentity(key.toString(), value.certPath.encoded) @@ -135,8 +134,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate, if (firstCertWithThisName != identity.certificate) { val certificates = identity.certPath.certificates val idx = certificates.lastIndexOf(firstCertWithThisName) - val certFactory = CertificateFactory.getInstance("X509") - val firstPath = certFactory.generateCertPath(certificates.slice(idx until certificates.size)) + val firstPath = X509CertificateFactory().delegate.generateCertPath(certificates.slice(idx until certificates.size)) verifyAndRegisterIdentity(PartyAndCertificate(firstPath)) } diff --git a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt index 2e12b05351..6f520f37f1 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt @@ -6,14 +6,14 @@ import net.corda.core.internal.cert import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.utilities.days -import net.corda.node.utilities.CertificateType -import net.corda.node.utilities.ContentSignerBuilder -import net.corda.node.utilities.X509Utilities +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.ContentSignerBuilder +import net.corda.nodeapi.internal.crypto.X509CertificateFactory +import net.corda.nodeapi.internal.crypto.X509Utilities import org.bouncycastle.operator.ContentSigner import java.security.KeyPair import java.security.PublicKey import java.security.Security -import java.security.cert.CertificateFactory import java.security.cert.X509Certificate import java.time.Duration @@ -37,8 +37,7 @@ fun freshCertificate(identityService: IdentityService, val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert) val ourCertificate = X509Utilities.createCertificate(CertificateType.IDENTITY, issuerCert.subject, issuerSigner, issuer.name, subjectPublicKey, window) - val certFactory = CertificateFactory.getInstance("X509") - val ourCertPath = certFactory.generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates) + val ourCertPath = X509CertificateFactory().delegate.generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates) val anonymisedIdentity = PartyAndCertificate(ourCertPath) identityService.verifyAndRegisterIdentity(anonymisedIdentity) return anonymisedIdentity diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index 68e9b0f79c..5fa25f3d86 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -20,10 +20,10 @@ import net.corda.node.services.messaging.NodeLoginModule.Companion.NODE_ROLE import net.corda.node.services.messaging.NodeLoginModule.Companion.PEER_ROLE import net.corda.node.services.messaging.NodeLoginModule.Companion.RPC_ROLE import net.corda.node.services.messaging.NodeLoginModule.Companion.VERIFIER_ROLE -import net.corda.node.utilities.X509Utilities -import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_TLS -import net.corda.node.utilities.X509Utilities.CORDA_ROOT_CA -import net.corda.node.utilities.loadKeyStore +import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS +import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA +import net.corda.nodeapi.internal.crypto.loadKeyStore import net.corda.nodeapi.* import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt index 8b56a45e70..8a045265dd 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt @@ -10,23 +10,23 @@ import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize -import net.corda.core.utilities.* import net.corda.core.utilities.NetworkHostAndPort +import net.corda.core.utilities.contextLogger import net.corda.core.utilities.sequence import net.corda.core.utilities.trace import net.corda.node.VersionInfo import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.statemachine.StateMachineManagerImpl import net.corda.node.utilities.* +import net.corda.nodeapi.internal.ArtemisMessagingComponent +import net.corda.nodeapi.internal.ArtemisMessagingComponent.* import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_QUEUE -import net.corda.nodeapi.internal.ArtemisMessagingComponent.ArtemisAddress -import net.corda.nodeapi.internal.ArtemisMessagingComponent.NodeAddress -import net.corda.nodeapi.internal.ArtemisMessagingComponent.ServiceAddress import org.apache.activemq.artemis.api.core.ActiveMQObjectClosedException import org.apache.activemq.artemis.api.core.Message.* import org.apache.activemq.artemis.api.core.RoutingType import org.apache.activemq.artemis.api.core.SimpleString -import org.apache.activemq.artemis.api.core.client.* +import org.apache.activemq.artemis.api.core.client.ClientConsumer +import org.apache.activemq.artemis.api.core.client.ClientMessage import java.security.PublicKey import java.time.Instant import java.util.* diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/RPCMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/RPCMessagingClient.kt index 4aeb8339fa..c7033b6baf 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/RPCMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/RPCMessagingClient.kt @@ -5,14 +5,17 @@ import net.corda.core.messaging.RPCOps import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.services.RPCUserService -import net.corda.node.utilities.* -import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER import net.corda.nodeapi.config.SSLConfiguration +import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER +import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.getX509Certificate +import net.corda.nodeapi.internal.crypto.loadKeyStore import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl class RPCMessagingClient(private val config: SSLConfiguration, serverAddress: NetworkHostAndPort) : SingletonSerializeAsToken() { private val artemis = ArtemisMessagingClient(config, serverAddress) private var rpcServer: RPCServer? = null + fun start(rpcOps: RPCOps, userService: RPCUserService) = synchronized(this) { val locator = artemis.start().sessionFactory.serverLocator val myCert = loadKeyStore(config.sslKeystore, config.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_TLS) diff --git a/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt b/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt index 051f68c63c..a0aad8c6d2 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt @@ -8,6 +8,7 @@ import net.corda.core.internal.cert import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.utilities.trace +import net.corda.nodeapi.internal.crypto.* import org.slf4j.LoggerFactory import java.nio.file.Path diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt index 8461ff2e19..88d9230a61 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt @@ -2,7 +2,7 @@ package net.corda.node.utilities.registration import com.google.common.net.MediaType import net.corda.core.internal.openHttpConnection -import net.corda.node.utilities.CertificateStream +import net.corda.nodeapi.internal.crypto.X509CertificateFactory import org.apache.commons.io.IOUtils import org.bouncycastle.pkcs.PKCS10CertificationRequest import java.io.IOException @@ -32,9 +32,9 @@ class HTTPNetworkRegistrationService(compatibilityZoneURL: URL) : NetworkRegistr return when (conn.responseCode) { HTTP_OK -> ZipInputStream(conn.inputStream).use { val certificates = ArrayList() - val stream = CertificateStream(it) + val factory = X509CertificateFactory() while (it.nextEntry != null) { - certificates.add(stream.nextCertificate()) + certificates += factory.generateCertificate(it) } certificates.toTypedArray() } diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index bd00c4673f..c350c3dd97 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -5,10 +5,10 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.* import net.corda.core.utilities.seconds import net.corda.node.services.config.NodeConfiguration -import net.corda.node.utilities.* -import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_CA -import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_TLS -import net.corda.node.utilities.X509Utilities.CORDA_ROOT_CA +import net.corda.nodeapi.internal.crypto.* +import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA +import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS +import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA import org.bouncycastle.openssl.jcajce.JcaPEMWriter import org.bouncycastle.util.io.pem.PemObject import java.io.StringWriter 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 63d6a80e30..85857636dd 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 @@ -6,16 +6,15 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate +import net.corda.core.internal.cert import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.UnknownAnonymousPartyException -import net.corda.core.internal.cert -import net.corda.node.services.identity.InMemoryIdentityService -import net.corda.node.utilities.CertificateAndKeyPair -import net.corda.node.utilities.CertificateType -import net.corda.node.utilities.X509Utilities +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509CertificateFactory +import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.* import org.junit.Test -import java.security.cert.CertificateFactory import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertNull @@ -156,12 +155,11 @@ class InMemoryIdentityServiceTests { } private fun createParty(x500Name: CordaX500Name, ca: CertificateAndKeyPair): Pair { - val certFactory = CertificateFactory.getInstance("X509") val issuerKeyPair = generateKeyPair() val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca) val txKey = Crypto.generateKeyPair() val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public) - val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) + val txCertPath = X509CertificateFactory().delegate.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } 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 e4d57e09c2..04ac68ed84 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 @@ -6,20 +6,20 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate +import net.corda.core.internal.cert import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException -import net.corda.core.internal.cert -import net.corda.node.utilities.CertificateAndKeyPair -import net.corda.node.utilities.CertificateType import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.X509Utilities +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509CertificateFactory +import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.* import net.corda.testing.node.MockServices import org.junit.After import org.junit.Before import org.junit.Test -import java.security.cert.CertificateFactory import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertNull @@ -28,10 +28,9 @@ import kotlin.test.assertNull * Tests for the in memory identity service. */ class PersistentIdentityServiceTests { - - lateinit var database: CordaPersistence - lateinit var services: MockServices - lateinit var identityService: IdentityService + private lateinit var database: CordaPersistence + private lateinit var services: MockServices + private lateinit var identityService: IdentityService @Before fun setup() { @@ -254,12 +253,11 @@ class PersistentIdentityServiceTests { } private fun createParty(x500Name: CordaX500Name, ca: CertificateAndKeyPair): Pair { - val certFactory = CertificateFactory.getInstance("X509") val issuerKeyPair = generateKeyPair() val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca) val txKey = Crypto.generateKeyPair() val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public) - val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) + val txCertPath = X509CertificateFactory().delegate.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt index b973992489..a2bf02db4d 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt @@ -10,8 +10,8 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.seconds import net.corda.node.services.network.TestNodeInfoFactory.createNodeInfo -import net.corda.node.utilities.CertificateType -import net.corda.node.utilities.X509Utilities +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.SerializationEnvironmentRule import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.asn1.x500.X500Name diff --git a/node/src/test/kotlin/net/corda/node/services/network/TestNodeInfoFactory.kt b/node/src/test/kotlin/net/corda/node/services/network/TestNodeInfoFactory.kt index 78840967fc..13ac5c5657 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/TestNodeInfoFactory.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/TestNodeInfoFactory.kt @@ -8,15 +8,14 @@ import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.NodeInfo import net.corda.core.serialization.serialize import net.corda.core.utilities.NetworkHostAndPort -import net.corda.node.utilities.CertificateType -import net.corda.node.utilities.X509Utilities +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509CertificateFactory +import net.corda.nodeapi.internal.crypto.X509Utilities import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.cert.X509CertificateHolder -import java.io.ByteArrayInputStream import java.security.KeyPair import java.security.cert.CertPath import java.security.cert.Certificate -import java.security.cert.CertificateFactory import java.security.cert.X509Certificate object TestNodeInfoFactory { @@ -40,11 +39,11 @@ object TestNodeInfoFactory { } private fun buildCertPath(vararg certificates: Certificate): CertPath { - return CertificateFactory.getInstance("X509").generateCertPath(certificates.asList()) + return X509CertificateFactory().delegate.generateCertPath(certificates.asList()) } private fun X509CertificateHolder.toX509Certificate(): X509Certificate { - return CertificateFactory.getInstance("X509").generateCertificate(ByteArrayInputStream(encoded)) as X509Certificate + return X509CertificateFactory().generateCertificate(encoded.inputStream()) } } \ No newline at end of file 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 0d6eb5e8fc..837686aee6 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/TLSAuthenticationTests.kt @@ -5,6 +5,7 @@ import net.corda.core.crypto.SignatureScheme import net.corda.core.crypto.newSecureRandom import net.corda.core.identity.CordaX500Name import net.corda.core.internal.* +import net.corda.nodeapi.internal.crypto.* import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder diff --git a/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt b/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt index df81bef948..2ae3da9f74 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt @@ -13,10 +13,11 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.services.config.createKeystoreForCordaNode +import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.serialization.AllWhitelist -import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 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 @@ -41,7 +42,6 @@ import java.security.PrivateKey import java.security.SecureRandom import java.security.cert.CertPath import java.security.cert.Certificate -import java.security.cert.CertificateFactory import java.security.cert.X509Certificate import java.util.* import java.util.stream.Stream @@ -49,7 +49,6 @@ import javax.net.ssl.* import kotlin.concurrent.thread import kotlin.test.* - class X509UtilitiesTest { @Rule @JvmField @@ -360,10 +359,16 @@ class X509UtilitiesTest { trustStorePassword: String ): KeyStore { val rootCAKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", organisation = "R3CEV", locality = "London", country = "GB"), rootCAKey) + val baseName = CordaX500Name(organisation = "R3CEV", locality = "London", country = "GB") + val rootCACert = X509Utilities.createSelfSignedCACertificate(baseName.copy(commonName = "Corda Node Root CA"), rootCAKey) val intermediateCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, CordaX500Name(commonName = "Corda Node Intermediate CA", organisation = "R3CEV", locality = "London", country = "GB"), intermediateCAKeyPair.public) + val intermediateCACert = X509Utilities.createCertificate( + CertificateType.INTERMEDIATE_CA, + rootCACert, + rootCAKey, + baseName.copy(commonName = "Corda Node Intermediate CA"), + intermediateCAKeyPair.public) val keyPass = keyPassword.toCharArray() val keyStore = loadOrCreateKeyStore(keyStoreFilePath, storePassword) @@ -426,11 +431,10 @@ class X509UtilitiesTest { emptyMap(), true, SerializationContext.UseCase.P2P) - val certFactory = CertificateFactory.getInstance("X509") val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootCAKey) val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name.x500Name, BOB_PUBKEY) - val expected = certFactory.generateCertPath(listOf(certificate.cert, rootCACert.cert)) + val expected = X509CertificateFactory().delegate.generateCertPath(listOf(certificate.cert, rootCACert.cert)) val serialized = expected.serialize(factory, context).bytes val actual: CertPath = serialized.deserialize(factory, context) assertEquals(expected, actual) diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt index 8454a03cd9..65745b7f3d 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt @@ -5,9 +5,9 @@ import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name import net.corda.core.internal.* -import net.corda.node.utilities.X509Utilities -import net.corda.node.utilities.getX509Certificate -import net.corda.node.utilities.loadKeyStore +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 import net.corda.testing.rigorousMock import net.corda.testing.testNodeConfiguration 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 1f95993b37..f483a74f25 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 @@ -12,24 +12,22 @@ import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.cert import net.corda.core.node.NodeInfo -import net.corda.core.node.services.IdentityService import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.loggerFor import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER import net.corda.node.services.config.configureDevKeyAndTrustStores -import net.corda.node.services.identity.InMemoryIdentityService -import net.corda.node.utilities.CertificateAndKeyPair -import net.corda.node.utilities.CertificateType -import net.corda.node.utilities.X509Utilities import net.corda.nodeapi.config.SSLConfiguration +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509CertificateFactory +import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.serialization.amqp.AMQP_ENABLED import org.mockito.internal.stubbing.answers.ThrowsException import org.mockito.stubbing.Answer import java.nio.file.Files import java.security.KeyPair import java.security.PublicKey -import java.security.cert.CertificateFactory import java.util.concurrent.atomic.AtomicInteger /** @@ -131,9 +129,8 @@ fun configureTestSSL(legalName: CordaX500Name = MEGA_CORP.name): SSLConfiguratio } fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair = DEV_CA): PartyAndCertificate { - val certFactory = CertificateFactory.getInstance("X509") val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, party.name, party.owningKey) - val certPath = certFactory.generateCertPath(listOf(certHolder.cert, trustRoot.certificate.cert)) + val certPath = X509CertificateFactory().delegate.generateCertPath(listOf(certHolder.cert, trustRoot.certificate.cert)) return PartyAndCertificate(certPath) } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt index d4b2290b40..96f452e74c 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt @@ -10,10 +10,10 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.toX509CertHolder -import net.corda.node.utilities.CertificateAndKeyPair -import net.corda.node.utilities.X509Utilities -import net.corda.node.utilities.getCertificateAndKeyPair -import net.corda.node.utilities.loadKeyStore +import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair +import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.getCertificateAndKeyPair +import net.corda.nodeapi.internal.crypto.loadKeyStore import org.bouncycastle.cert.X509CertificateHolder import java.math.BigInteger import java.security.KeyPair From 4ca54b73fec0dcdb657183c37567888393b38bfd Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Fri, 24 Nov 2017 14:32:32 +0000 Subject: [PATCH 02/27] Added tests to make sure the platform version is correctly available --- build.gradle | 3 +- .../rpc/StandaloneCordaRPCJavaClientTest.java | 2 +- .../kotlin/rpc/StandaloneCordaRPClientTest.kt | 2 +- constants.properties | 1 + .../net/corda/core/NodeVersioningTest.kt | 75 ++++++++++++++++++ .../corda/core/cordapp/CordappSmokeTest.kt | 3 +- .../kotlin/net/corda/node/BootTests.kt | 2 +- .../node/services/messaging/Messaging.kt | 10 ++- .../messaging/ArtemisMessagingTests.kt | 78 +++++-------------- .../net/corda/testing/driver/DriverTests.kt | 2 +- .../net/corda/smoketesting/NodeProcess.kt | 30 +++---- .../common/internal}/ProjectStructure.kt | 2 +- 12 files changed, 125 insertions(+), 85 deletions(-) create mode 100644 core/src/smoke-test/kotlin/net/corda/core/NodeVersioningTest.kt rename testing/{test-utils/src/main/kotlin/net/corda/testing => test-common/src/main/kotlin/net/corda/testing/common/internal}/ProjectStructure.kt (89%) diff --git a/build.gradle b/build.gradle index 58c7c31ec2..d71ffe58cc 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,7 @@ buildscript { // Our version: bump this on release. ext.corda_release_version = "3.0-SNAPSHOT" // Increment this on any release that changes public APIs anywhere in the Corda platform - // TODO This is going to be difficult until we have a clear separation throughout the code of what is public and what is internal - ext.corda_platform_version = 2 + ext.corda_platform_version = constants.getProperty("platformVersion") ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion") // Dependency versions. Can run 'gradle dependencyUpdates' to find new versions of things. diff --git a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java index 31daccc9c2..692cfe381c 100644 --- a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java +++ b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java @@ -74,7 +74,7 @@ public class StandaloneCordaRPCJavaClientTest { } private void copyFinanceCordapp() { - Path cordappsDir = (factory.baseDirectory(notaryConfig).resolve("cordapps")); + Path cordappsDir = (factory.baseDirectory(notaryConfig).resolve(NodeProcess.CORDAPPS_DIR_NAME)); try { Files.createDirectories(cordappsDir); } catch (IOException ex) { diff --git a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt index 36323ec068..23018f7bfb 100644 --- a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt +++ b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt @@ -86,7 +86,7 @@ class StandaloneCordaRPClientTest { } private fun copyFinanceCordapp() { - val cordappsDir = (factory.baseDirectory(notaryConfig) / "cordapps").createDirectories() + val cordappsDir = (factory.baseDirectory(notaryConfig) / NodeProcess.CORDAPPS_DIR_NAME).createDirectories() // Find the finance jar file for the smoke tests of this module val financeJar = Paths.get("build", "resources", "smokeTest").list { it.filter { "corda-finance" in it.toString() }.toList().single() diff --git a/constants.properties b/constants.properties index f278032d63..211f29e563 100644 --- a/constants.properties +++ b/constants.properties @@ -1,5 +1,6 @@ gradlePluginsVersion=2.0.9 kotlinVersion=1.1.60 +platformVersion=2 guavaVersion=21.0 bouncycastleVersion=1.57 typesafeConfigVersion=1.3.1 \ No newline at end of file diff --git a/core/src/smoke-test/kotlin/net/corda/core/NodeVersioningTest.kt b/core/src/smoke-test/kotlin/net/corda/core/NodeVersioningTest.kt new file mode 100644 index 0000000000..f9844fbce0 --- /dev/null +++ b/core/src/smoke-test/kotlin/net/corda/core/NodeVersioningTest.kt @@ -0,0 +1,75 @@ +package net.corda.core + +import co.paralleluniverse.fibers.Suspendable +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.StartableByRPC +import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.* +import net.corda.core.messaging.startFlow +import net.corda.core.utilities.getOrThrow +import net.corda.nodeapi.User +import net.corda.smoketesting.NodeConfig +import net.corda.smoketesting.NodeProcess +import net.corda.testing.common.internal.ProjectStructure +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import java.nio.file.Paths +import java.util.* +import java.util.concurrent.atomic.AtomicInteger +import java.util.jar.JarFile +import kotlin.streams.toList + +class NodeVersioningTest { + private companion object { + val user = User("user1", "test", permissions = setOf("ALL")) + val port = AtomicInteger(15100) + + val expectedPlatformVersion = (ProjectStructure.projectRootDir / "constants.properties").read { + val constants = Properties() + constants.load(it) + constants.getProperty("platformVersion").toInt() + } + } + + private val factory = NodeProcess.Factory() + + private val aliceConfig = NodeConfig( + legalName = CordaX500Name(organisation = "Alice Corp", locality = "Madrid", country = "ES"), + p2pPort = port.andIncrement, + rpcPort = port.andIncrement, + webPort = port.andIncrement, + isNotary = false, + users = listOf(user) + ) + + @Test + fun `platform version in manifest file`() { + val manifest = JarFile(factory.cordaJar.toFile()).manifest + assertThat(manifest.mainAttributes.getValue("Corda-Platform-Version").toInt()).isEqualTo(expectedPlatformVersion) + } + + @Test + fun `platform version from RPC`() { + val cordappsDir = (factory.baseDirectory(aliceConfig) / NodeProcess.CORDAPPS_DIR_NAME).createDirectories() + // Find the jar file for the smoke tests of this module + val selfCordapp = Paths.get("build", "libs").list { + it.filter { "-smokeTests" in it.toString() }.toList().single() + } + selfCordapp.copyToDirectory(cordappsDir) + + factory.create(aliceConfig).use { alice -> + alice.connect().use { + val rpc = it.proxy + assertThat(rpc.protocolVersion).isEqualTo(expectedPlatformVersion) + assertThat(rpc.nodeInfo().platformVersion).isEqualTo(expectedPlatformVersion) + assertThat(rpc.startFlow(NodeVersioningTest::GetPlatformVersionFlow).returnValue.getOrThrow()).isEqualTo(expectedPlatformVersion) + } + } + } + + @StartableByRPC + class GetPlatformVersionFlow : FlowLogic() { + @Suspendable + override fun call(): Int = serviceHub.myInfo.platformVersion + } +} diff --git a/core/src/smoke-test/kotlin/net/corda/core/cordapp/CordappSmokeTest.kt b/core/src/smoke-test/kotlin/net/corda/core/cordapp/CordappSmokeTest.kt index 2f1991af12..2f826b5f54 100644 --- a/core/src/smoke-test/kotlin/net/corda/core/cordapp/CordappSmokeTest.kt +++ b/core/src/smoke-test/kotlin/net/corda/core/cordapp/CordappSmokeTest.kt @@ -14,6 +14,7 @@ import net.corda.core.utilities.unwrap import net.corda.nodeapi.User import net.corda.smoketesting.NodeConfig import net.corda.smoketesting.NodeProcess +import net.corda.smoketesting.NodeProcess.Companion.CORDAPPS_DIR_NAME import org.assertj.core.api.Assertions.assertThat import org.junit.Test import java.nio.file.Paths @@ -22,7 +23,6 @@ import kotlin.streams.toList class CordappSmokeTest { private companion object { - private const val CORDAPPS_DIR_NAME = "cordapps" val user = User("user1", "test", permissions = setOf("ALL")) val port = AtomicInteger(15100) } @@ -38,7 +38,6 @@ class CordappSmokeTest { users = listOf(user) ) - @Test fun `FlowContent appName returns the filename of the CorDapp jar`() { val cordappsDir = (factory.baseDirectory(aliceConfig) / CORDAPPS_DIR_NAME).createDirectories() 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 9973be0bf7..a7561d331f 100644 --- a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt @@ -10,7 +10,7 @@ import net.corda.node.internal.NodeStartup import net.corda.node.services.Permissions.Companion.startFlow import net.corda.nodeapi.User import net.corda.testing.ALICE -import net.corda.testing.ProjectStructure.projectRootDir +import net.corda.testing.common.internal.ProjectStructure.projectRootDir import net.corda.testing.driver.driver import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/Messaging.kt b/node/src/main/kotlin/net/corda/node/services/messaging/Messaging.kt index 4d91e00ec6..468de4d8f5 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/Messaging.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/Messaging.kt @@ -190,11 +190,13 @@ fun MessagingService.onNext(topic: String, sessionId: Long): CordaFutu return messageFuture } -fun MessagingService.send(topic: String, sessionID: Long, payload: Any, to: MessageRecipients, uuid: UUID = UUID.randomUUID()) - = send(TopicSession(topic, sessionID), payload, to, uuid) +fun MessagingService.send(topic: String, sessionID: Long, payload: Any, to: MessageRecipients, uuid: UUID = UUID.randomUUID()) { + send(TopicSession(topic, sessionID), payload, to, uuid) +} -fun MessagingService.send(topicSession: TopicSession, payload: Any, to: MessageRecipients, uuid: UUID = UUID.randomUUID(), retryId: Long? = null) - = send(createMessage(topicSession, payload.serialize().bytes, uuid), to, retryId) +fun MessagingService.send(topicSession: TopicSession, payload: Any, to: MessageRecipients, uuid: UUID = UUID.randomUUID(), retryId: Long? = null) { + send(createMessage(topicSession, payload.serialize().bytes, uuid), to, retryId) +} interface MessageHandlerRegistration 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 150234674a..a4ef0e5dd0 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 @@ -1,9 +1,6 @@ package net.corda.node.services.messaging -import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.generateKeyPair -import net.corda.core.internal.concurrent.doneFuture -import net.corda.core.internal.concurrent.openFuture import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.services.RPCUserService import net.corda.node.services.RPCUserServiceImpl @@ -27,13 +24,13 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import java.net.ServerSocket +import java.util.concurrent.BlockingQueue import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.TimeUnit.MILLISECONDS import kotlin.concurrent.thread import kotlin.test.assertEquals import kotlin.test.assertNull -//TODO This needs to be merged into P2PMessagingTest as that creates a more realistic environment class ArtemisMessagingTests { companion object { const val TOPIC = "platform.self" @@ -54,21 +51,19 @@ class ArtemisMessagingTests { private lateinit var config: NodeConfiguration private lateinit var database: CordaPersistence private lateinit var userService: RPCUserService - private lateinit var networkMapRegistrationFuture: CordaFuture private var messagingClient: P2PMessagingClient? = null private var messagingServer: ArtemisMessagingServer? = null private lateinit var networkMapCache: NetworkMapCacheImpl + @Before fun setUp() { - val baseDirectory = temporaryFolder.root.toPath() userService = RPCUserServiceImpl(emptyList()) config = testNodeConfiguration( - baseDirectory = baseDirectory, + baseDirectory = temporaryFolder.root.toPath(), myLegalName = ALICE.name) LogHelper.setLevel(PersistentUniquenessProvider::class) database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock()) - networkMapRegistrationFuture = doneFuture(Unit) networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database), rigorousMock()) } @@ -76,8 +71,6 @@ class ArtemisMessagingTests { fun cleanUp() { messagingClient?.stop() messagingServer?.stop() - messagingClient = null - messagingServer = null database.close() LogHelper.reset(PersistentUniquenessProvider::class) } @@ -120,9 +113,7 @@ class ArtemisMessagingTests { @Test fun `client should be able to send message to itself`() { - val receivedMessages = LinkedBlockingQueue() - - val messagingClient = createAndStartClientAndServer(receivedMessages) + val (messagingClient, receivedMessages) = createAndStartClientAndServer() val message = messagingClient.createMessage(TOPIC, data = "first msg".toByteArray()) messagingClient.send(message, messagingClient.myAddress) @@ -132,76 +123,45 @@ class ArtemisMessagingTests { } @Test - fun `client should be able to send message to itself before network map is available, and receive after`() { - val settableFuture = openFuture() - networkMapRegistrationFuture = settableFuture - - val receivedMessages = LinkedBlockingQueue() - - val messagingClient = createAndStartClientAndServer(receivedMessages) + fun `platform version is included in the message`() { + val (messagingClient, receivedMessages) = createAndStartClientAndServer(platformVersion = 3) val message = messagingClient.createMessage(TOPIC, data = "first msg".toByteArray()) messagingClient.send(message, messagingClient.myAddress) - settableFuture.set(Unit) - val firstActual: Message = receivedMessages.take() - assertEquals("first msg", String(firstActual.data)) - assertNull(receivedMessages.poll(200, MILLISECONDS)) - } - - @Test - fun `client should be able to send large numbers of messages to itself before network map is available and survive restart, then receive messages`() { - // Crank the iteration up as high as you want... just takes longer to run. - val iterations = 100 - networkMapRegistrationFuture = openFuture() - - val receivedMessages = LinkedBlockingQueue() - - val messagingClient = createAndStartClientAndServer(receivedMessages) - for (iter in 1..iterations) { - val message = messagingClient.createMessage(TOPIC, data = "first msg $iter".toByteArray()) - messagingClient.send(message, messagingClient.myAddress) - } - - // Stop client and server and create afresh. - messagingClient.stop() - messagingServer?.stop() - - networkMapRegistrationFuture = doneFuture(Unit) - - createAndStartClientAndServer(receivedMessages) - for (iter in 1..iterations) { - val firstActual: Message = receivedMessages.take() - assertThat(String(firstActual.data)).isEqualTo("first msg $iter") - } - assertNull(receivedMessages.poll(200, MILLISECONDS)) + val received = receivedMessages.take() + assertThat(received.platformVersion).isEqualTo(3) } private fun startNodeMessagingClient() { messagingClient!!.start() } - private fun createAndStartClientAndServer(receivedMessages: LinkedBlockingQueue): P2PMessagingClient { + private fun createAndStartClientAndServer(platformVersion: Int = 1): Pair> { + val receivedMessages = LinkedBlockingQueue() + createMessagingServer().start() - val messagingClient = createMessagingClient() + val messagingClient = createMessagingClient(platformVersion = platformVersion) startNodeMessagingClient() messagingClient.addMessageHandler(TOPIC) { message, _ -> receivedMessages.add(message) } // Run after the handlers are added, otherwise (some of) the messages get delivered and discarded / dead-lettered. - thread { messagingClient.run() } - return messagingClient + thread(isDaemon = true) { messagingClient.run() } + + return Pair(messagingClient, receivedMessages) } - private fun createMessagingClient(server: NetworkHostAndPort = NetworkHostAndPort("localhost", serverPort)): P2PMessagingClient { + private fun createMessagingClient(server: NetworkHostAndPort = NetworkHostAndPort("localhost", serverPort), platformVersion: Int = 1): P2PMessagingClient { return database.transaction { P2PMessagingClient( config, - MOCK_VERSION_INFO, + MOCK_VERSION_INFO.copy(platformVersion = platformVersion), server, identity.public, ServiceAffinityExecutor("ArtemisMessagingTests", 1), - database).apply { + database + ).apply { config.configureWithDevSSLCertificate() messagingClient = this } 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 39500c7834..e6d581f236 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 @@ -9,7 +9,7 @@ import net.corda.node.internal.NodeStartup import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_REGULATOR -import net.corda.testing.ProjectStructure.projectRootDir +import net.corda.testing.common.internal.ProjectStructure.projectRootDir import net.corda.testing.node.NotarySpec import org.assertj.core.api.Assertions.assertThat import org.junit.Test diff --git a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt index b6656977b7..17ccd5a689 100644 --- a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt +++ b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt @@ -21,7 +21,8 @@ class NodeProcess( private val node: Process, private val client: CordaRPCClient ) : AutoCloseable { - private companion object { + companion object { + const val CORDAPPS_DIR_NAME = "cordapps" private val log = contextLogger() } @@ -42,9 +43,11 @@ class NodeProcess( (nodeDir / "artemis").toFile().deleteRecursively() } + // TODO All use of this factory have duplicate code which is either bundling the calling module or a 3rd party module + // as a CorDapp for the nodes. class Factory( - private val buildDirectory: Path = Paths.get("build"), - private val cordaJar: Path = Paths.get(this::class.java.getResource("/corda.jar").toURI()) + val buildDirectory: Path = Paths.get("build"), + val cordaJar: Path = Paths.get(this::class.java.getResource("/corda.jar").toURI()) ) { private companion object { val javaPath: Path = Paths.get(System.getProperty("java.home"), "bin", "java") @@ -71,36 +74,37 @@ class NodeProcess( val process = startNode(nodeDir) val client = CordaRPCClient(NetworkHostAndPort("localhost", config.rpcPort)) - val user = config.users[0] + waitForNode(process, config, client) + return NodeProcess(config, nodeDir, process, client) + } - val setupExecutor = Executors.newSingleThreadScheduledExecutor() + private fun waitForNode(process: Process, config: NodeConfig, client: CordaRPCClient) { + val executor = Executors.newSingleThreadScheduledExecutor() try { - setupExecutor.scheduleWithFixedDelay({ + executor.scheduleWithFixedDelay({ try { if (!process.isAlive) { log.error("Node '${config.commonName}' has died.") return@scheduleWithFixedDelay } - val conn = client.start(user.username, user.password) - conn.close() + val rpcConnection = config.users[0].let { client.start(it.username, it.password) } + rpcConnection.close() // Cancel the "setup" task now that we've created the RPC client. - setupExecutor.shutdown() + executor.shutdown() } catch (e: Exception) { log.warn("Node '{}' not ready yet (Error: {})", config.commonName, e.message) } }, 5, 1, SECONDS) - val setupOK = setupExecutor.awaitTermination(120, SECONDS) + val setupOK = executor.awaitTermination(120, SECONDS) check(setupOK && process.isAlive) { "Failed to create RPC connection" } } catch (e: Exception) { process.destroyForcibly() throw e } finally { - setupExecutor.shutdownNow() + executor.shutdownNow() } - - return NodeProcess(config, nodeDir, process, client) } private fun startNode(nodeDir: Path): Process { diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/ProjectStructure.kt b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ProjectStructure.kt similarity index 89% rename from testing/test-utils/src/main/kotlin/net/corda/testing/ProjectStructure.kt rename to testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ProjectStructure.kt index 1809d4dc16..4b4d5cac34 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/ProjectStructure.kt +++ b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ProjectStructure.kt @@ -1,4 +1,4 @@ -package net.corda.testing +package net.corda.testing.common.internal import net.corda.core.internal.div import net.corda.core.internal.isDirectory From 4bd6fef0f92a09084b80ca53206c9c915fd29731 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Mon, 27 Nov 2017 17:55:08 +0000 Subject: [PATCH 03/27] StateMachineManager is no longer lateinit. (#2123) --- .../confidential/IdentitySyncFlowTests.kt | 6 +-- .../net/corda/core/flows/FlowsInJavaTest.java | 2 +- .../net/corda/core/flows/AttachmentTests.kt | 18 +++------ .../core/flows/CollectSignaturesFlowTests.kt | 2 +- .../net/corda/core/flows/FlowTestsUtils.kt | 4 +- .../internal/ResolveTransactionsFlowTest.kt | 4 +- .../AttachmentSerializationTest.kt | 2 +- .../net/corda/docs/CustomVaultQueryTest.kt | 2 +- .../docs/FxTransactionBuildTutorialTest.kt | 2 +- .../WorkflowTransactionBuildTutorialTest.kt | 2 +- .../services/messaging/MQSecurityTest.kt | 2 +- .../net/corda/node/internal/AbstractNode.kt | 40 ++++++++----------- .../net/corda/node/internal/StartedNode.kt | 18 ++++++++- .../node/messaging/TwoPartyTradeFlowTests.kt | 2 +- .../statemachine/FlowFrameworkTests.kt | 2 +- .../vault/VaultSoftLockManagerTest.kt | 2 +- .../corda/irs/api/NodeInterestRatesTest.kt | 4 +- .../corda/netmap/simulation/IRSSimulation.kt | 10 ++--- .../net/corda/traderdemo/TraderDemoTest.kt | 3 +- .../corda/testing/FlowStackSnapshotTest.kt | 2 +- 20 files changed, 63 insertions(+), 66 deletions(-) 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 7d05154ace..51b24e8bd1 100644 --- a/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt @@ -49,8 +49,7 @@ class IdentitySyncFlowTests { val alice: Party = aliceNode.info.singleIdentity() val bob: Party = bobNode.info.singleIdentity() val notary = mockNet.defaultNotaryIdentity - bobNode.internals.registerInitiatedFlow(Receive::class.java) - + bobNode.registerInitiatedFlow(Receive::class.java) // Alice issues then pays some cash to a new confidential identity that Bob doesn't know about val anonymous = true val ref = OpaqueBytes.of(0x01) @@ -80,8 +79,7 @@ class IdentitySyncFlowTests { val bob: Party = bobNode.info.singleIdentity() val charlie: Party = charlieNode.info.singleIdentity() val notary = mockNet.defaultNotaryIdentity - bobNode.internals.registerInitiatedFlow(Receive::class.java) - + bobNode.registerInitiatedFlow(Receive::class.java) // Charlie issues then pays some cash to a new confidential identity val anonymous = true val ref = OpaqueBytes.of(0x01) 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 27f15c91f6..1b69fc5a6c 100644 --- a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java +++ b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java @@ -38,7 +38,7 @@ public class FlowsInJavaTest { @Test public void suspendableActionInsideUnwrap() throws Exception { - bobNode.getInternals().registerInitiatedFlow(SendHelloAndThenReceive.class); + bobNode.registerInitiatedFlow(SendHelloAndThenReceive.class); Future result = startFlow(aliceNode.getServices(), new SendInUnwrapFlow(bob)).getResultFuture(); mockNet.runNetwork(); assertThat(result.get()).isEqualTo("Hello"); 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 147e135d4f..890836e35b 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -52,10 +52,8 @@ class AttachmentTests { val bobNode = mockNet.createPartyNode(BOB.name) val alice = aliceNode.info.singleIdentity() - - aliceNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) - bobNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) - + aliceNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java) + bobNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java) // Insert an attachment into node zero's store directly. val id = aliceNode.database.transaction { aliceNode.attachments.importAttachment(ByteArrayInputStream(fakeAttachment())) @@ -85,10 +83,8 @@ class AttachmentTests { fun `missing`() { val aliceNode = mockNet.createPartyNode(ALICE.name) val bobNode = mockNet.createPartyNode(BOB.name) - - aliceNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) - bobNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) - + aliceNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java) + bobNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java) // Get node one to fetch a non-existent attachment. val hash = SecureHash.randomSHA256() val alice = aliceNode.info.singleIdentity() @@ -108,10 +104,8 @@ class AttachmentTests { }) val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB.name)) val alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME) - - aliceNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) - bobNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) - + aliceNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java) + bobNode.registerInitiatedFlow(FetchAttachmentsResponse::class.java) val attachment = fakeAttachment() // Insert an attachment into node zero's store directly. val id = aliceNode.database.transaction { 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 6ad19b2e28..0c2a00331b 100644 --- a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt @@ -50,7 +50,7 @@ class CollectSignaturesFlowTests { private fun registerFlowOnAllNodes(flowClass: KClass>) { listOf(aliceNode, bobNode, charlieNode).forEach { - it.internals.registerInitiatedFlow(flowClass.java) + it.registerInitiatedFlow(flowClass.java) } } diff --git a/core/src/test/kotlin/net/corda/core/flows/FlowTestsUtils.kt b/core/src/test/kotlin/net/corda/core/flows/FlowTestsUtils.kt index 6b6f0492b3..4d2d12335d 100644 --- a/core/src/test/kotlin/net/corda/core/flows/FlowTestsUtils.kt +++ b/core/src/test/kotlin/net/corda/core/flows/FlowTestsUtils.kt @@ -38,14 +38,14 @@ class NoAnswer(private val closure: () -> Unit = {}) : FlowLogic() { * Allows to register a flow of type [R] against an initiating flow of type [I]. */ inline fun , reified R : FlowLogic<*>> StartedNode<*>.registerInitiatedFlow(initiatingFlowType: KClass, crossinline construct: (session: FlowSession) -> R) { - internals.internalRegisterFlowFactory(initiatingFlowType.java, InitiatedFlowFactory.Core { session -> construct(session) }, R::class.javaObjectType, true) + internalRegisterFlowFactory(initiatingFlowType.java, InitiatedFlowFactory.Core { session -> construct(session) }, R::class.javaObjectType, true) } /** * Allows to register a flow of type [Answer] against an initiating flow of type [I], returning a valure of type [R]. */ inline fun , reified R : Any> StartedNode<*>.registerAnswer(initiatingFlowType: KClass, value: R) { - internals.internalRegisterFlowFactory(initiatingFlowType.java, InitiatedFlowFactory.Core { session -> Answer(session, value) }, Answer::class.javaObjectType, true) + internalRegisterFlowFactory(initiatingFlowType.java, InitiatedFlowFactory.Core { session -> Answer(session, value) }, Answer::class.javaObjectType, true) } /** 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 f71470b406..b56c5add9a 100644 --- a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt @@ -42,8 +42,8 @@ class ResolveTransactionsFlowTest { notaryNode = mockNet.defaultNotaryNode megaCorpNode = mockNet.createPartyNode(MEGA_CORP.name) miniCorpNode = mockNet.createPartyNode(MINI_CORP.name) - megaCorpNode.internals.registerInitiatedFlow(TestResponseFlow::class.java) - miniCorpNode.internals.registerInitiatedFlow(TestResponseFlow::class.java) + megaCorpNode.registerInitiatedFlow(TestResponseFlow::class.java) + miniCorpNode.registerInitiatedFlow(TestResponseFlow::class.java) notary = mockNet.defaultNotaryIdentity megaCorp = megaCorpNode.info.singleIdentity() miniCorp = miniCorpNode.info.singleIdentity() 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 b2d9b80816..1dd630a2ff 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -148,7 +148,7 @@ class AttachmentSerializationTest { } private fun launchFlow(clientLogic: ClientLogic, rounds: Int, sendData: Boolean = false) { - server.internals.internalRegisterFlowFactory( + server.internalRegisterFlowFactory( ClientLogic::class.java, InitiatedFlowFactory.Core { ServerLogic(it, sendData) }, ServerLogic::class.java, 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 9c850288ed..ef2acb083b 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 @@ -28,7 +28,7 @@ class CustomVaultQueryTest { mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance", "net.corda.docs")) nodeA = mockNet.createPartyNode() nodeB = mockNet.createPartyNode() - nodeA.internals.registerInitiatedFlow(TopupIssuerFlow.TopupIssuer::class.java) + nodeA.registerInitiatedFlow(TopupIssuerFlow.TopupIssuer::class.java) notary = mockNet.defaultNotaryIdentity } 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 7fa6292568..c53fe8e175 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 @@ -27,7 +27,7 @@ class FxTransactionBuildTutorialTest { mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance")) nodeA = mockNet.createPartyNode() nodeB = mockNet.createPartyNode() - nodeB.internals.registerInitiatedFlow(ForeignExchangeRemoteFlow::class.java) + nodeB.registerInitiatedFlow(ForeignExchangeRemoteFlow::class.java) notary = mockNet.defaultNotaryIdentity } 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 45553176eb..d4fb476134 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 @@ -35,7 +35,7 @@ class WorkflowTransactionBuildTutorialTest { mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.docs")) val aliceNode = mockNet.createPartyNode(ALICE_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME) - aliceNode.internals.registerInitiatedFlow(RecordCompletionFlow::class.java) + aliceNode.registerInitiatedFlow(RecordCompletionFlow::class.java) aliceServices = aliceNode.services bobServices = bobNode.services alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME) 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 1ddeb4497f..ecaa0032a0 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 @@ -213,7 +213,7 @@ abstract class MQSecurityTest : NodeBasedTest() { private fun startBobAndCommunicateWithAlice(): Party { val bob = startNode(BOB.name) - bob.internals.registerInitiatedFlow(ReceiveFlow::class.java) + bob.registerInitiatedFlow(ReceiveFlow::class.java) val bobParty = bob.info.chooseIdentity() // Perform a protocol exchange to force the peer queue to be created alice.services.startFlow(SendFlow(bobParty, 0)).resultFuture.getOrThrow() diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 16d0f09f05..12ecaf9f94 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -124,7 +124,6 @@ abstract class AbstractNode(val configuration: NodeConfiguration, private lateinit var _services: ServiceHubInternalImpl protected var myNotaryIdentity: PartyAndCertificate? = null protected lateinit var checkpointStorage: CheckpointStorage - protected lateinit var smm: StateMachineManager private lateinit var tokenizableServices: List protected lateinit var attachments: NodeAttachmentService protected lateinit var network: MessagingService @@ -153,7 +152,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, @Volatile private var _started: StartedNode? = null /** The implementation of the [CordaRPCOps] interface used by this node. */ - open fun makeRPCOps(flowStarter: FlowStarter, database: CordaPersistence): CordaRPCOps { + open fun makeRPCOps(flowStarter: FlowStarter, database: CordaPersistence, smm: StateMachineManager): CordaRPCOps { return SecureCordaRPCOps(services, smm, database, flowStarter) } @@ -191,7 +190,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, val stateLoader = StateLoaderImpl(transactionStorage) val nodeServices = makeServices(keyPairs, schemaService, transactionStorage, stateLoader, database, info, identityService) val notaryService = makeNotaryService(nodeServices, database) - smm = makeStateMachineManager(database) + val smm = makeStateMachineManager(database) val flowStarter = FlowStarterImpl(serverThread, smm) val schedulerService = NodeSchedulerService( platformClock, @@ -208,13 +207,13 @@ abstract class AbstractNode(val configuration: NodeConfiguration, MoreExecutors.shutdownAndAwaitTermination(serverThread as ExecutorService, 50, SECONDS) } } - makeVaultObservers(schedulerService, database.hibernateConfig) - val rpcOps = makeRPCOps(flowStarter, database) + makeVaultObservers(schedulerService, database.hibernateConfig, smm) + val rpcOps = makeRPCOps(flowStarter, database, smm) startMessagingService(rpcOps) installCoreFlows() val cordaServices = installCordaServices(flowStarter) tokenizableServices = nodeServices + cordaServices + schedulerService - registerCordappFlows() + registerCordappFlows(smm) _services.rpcFlows += cordappLoader.cordapps.flatMap { it.rpcFlows } FlowLogicRefFactoryImpl.classloader = cordappLoader.appClassLoader startShell(rpcOps) @@ -397,11 +396,11 @@ abstract class AbstractNode(val configuration: NodeConfiguration, installCoreFlow(NotaryFlow.Client::class, service::createServiceFlow) } - private fun registerCordappFlows() { + private fun registerCordappFlows(smm: StateMachineManager) { cordappLoader.cordapps.flatMap { it.initiatedFlows } .forEach { try { - registerInitiatedFlowInternal(it, track = false) + registerInitiatedFlowInternal(smm, it, track = false) } catch (e: NoSuchMethodException) { log.error("${it.name}, as an initiated flow, must have a constructor with a single parameter " + "of type ${Party::class.java.name}") @@ -411,13 +410,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } } - /** - * Use this method to register your initiated flows in your tests. This is automatically done by the node when it - * starts up for all [FlowLogic] classes it finds which are annotated with [InitiatedBy]. - * @return An [Observable] of the initiated flows started by counter-parties. - */ - fun > registerInitiatedFlow(initiatedFlowClass: Class): Observable { - return registerInitiatedFlowInternal(initiatedFlowClass, track = true) + internal fun > registerInitiatedFlow(smm: StateMachineManager, initiatedFlowClass: Class): Observable { + return registerInitiatedFlowInternal(smm, initiatedFlowClass, track = true) } // TODO remove once not needed @@ -426,7 +420,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, "It should accept a ${FlowSession::class.java.simpleName} instead" } - private fun > registerInitiatedFlowInternal(initiatedFlow: Class, track: Boolean): Observable { + private fun > registerInitiatedFlowInternal(smm: StateMachineManager, initiatedFlow: Class, track: Boolean): Observable { val constructors = initiatedFlow.declaredConstructors.associateBy { it.parameterTypes.toList() } val flowSessionCtor = constructors[listOf(FlowSession::class.java)]?.apply { isAccessible = true } val ctor: (FlowSession) -> F = if (flowSessionCtor == null) { @@ -447,16 +441,16 @@ abstract class AbstractNode(val configuration: NodeConfiguration, "${InitiatedBy::class.java.name} must point to ${classWithAnnotation.name} and not ${initiatingFlow.name}" } val flowFactory = InitiatedFlowFactory.CorDapp(version, initiatedFlow.appName, ctor) - val observable = internalRegisterFlowFactory(initiatingFlow, flowFactory, initiatedFlow, track) + val observable = internalRegisterFlowFactory(smm, initiatingFlow, flowFactory, initiatedFlow, track) log.info("Registered ${initiatingFlow.name} to initiate ${initiatedFlow.name} (version $version)") return observable } - @VisibleForTesting - fun > internalRegisterFlowFactory(initiatingFlowClass: Class>, - flowFactory: InitiatedFlowFactory, - initiatedFlowClass: Class, - track: Boolean): Observable { + internal fun > internalRegisterFlowFactory(smm: StateMachineManager, + initiatingFlowClass: Class>, + flowFactory: InitiatedFlowFactory, + initiatedFlowClass: Class, + track: Boolean): Observable { val observable = if (track) { smm.changes.filter { it is StateMachineManager.Change.Add }.map { it.logic }.ofType(initiatedFlowClass) } else { @@ -519,7 +513,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } protected open fun makeTransactionStorage(database: CordaPersistence): WritableTransactionStorage = DBTransactionStorage() - private fun makeVaultObservers(schedulerService: SchedulerService, hibernateConfig: HibernateConfiguration) { + private fun makeVaultObservers(schedulerService: SchedulerService, hibernateConfig: HibernateConfiguration, smm: StateMachineManager) { VaultSoftLockManager.install(services.vaultService, smm) ScheduledActivityObserver.install(services.vaultService, schedulerService) HibernateObserver.install(services.vaultService.rawUpdates, hibernateConfig) diff --git a/node/src/main/kotlin/net/corda/node/internal/StartedNode.kt b/node/src/main/kotlin/net/corda/node/internal/StartedNode.kt index 88c09de5cc..5335ecd228 100644 --- a/node/src/main/kotlin/net/corda/node/internal/StartedNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/StartedNode.kt @@ -2,6 +2,8 @@ package net.corda.node.internal import net.corda.core.contracts.* import net.corda.core.flows.FlowLogic +import net.corda.core.flows.InitiatedBy +import net.corda.core.internal.VisibleForTesting import net.corda.core.messaging.CordaRPCOps import net.corda.core.node.NodeInfo import net.corda.core.node.StateLoader @@ -13,6 +15,7 @@ import net.corda.node.services.messaging.MessagingService import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.utilities.CordaPersistence +import rx.Observable interface StartedNode { val internals: N @@ -26,7 +29,20 @@ interface StartedNode { val rpcOps: CordaRPCOps val notaryService: NotaryService? fun dispose() = internals.stop() - fun > registerInitiatedFlow(initiatedFlowClass: Class) = internals.registerInitiatedFlow(initiatedFlowClass) + /** + * Use this method to register your initiated flows in your tests. This is automatically done by the node when it + * starts up for all [FlowLogic] classes it finds which are annotated with [InitiatedBy]. + * @return An [Observable] of the initiated flows started by counter-parties. + */ + fun > registerInitiatedFlow(initiatedFlowClass: Class) = internals.registerInitiatedFlow(smm, initiatedFlowClass) + + @VisibleForTesting + fun > internalRegisterFlowFactory(initiatingFlowClass: Class>, + flowFactory: InitiatedFlowFactory, + initiatedFlowClass: Class, + track: Boolean): Observable { + return internals.internalRegisterFlowFactory(smm, initiatingFlowClass, flowFactory, initiatedFlowClass, track) + } } class StateLoaderImpl(private val validatedTransactions: TransactionStorage) : StateLoader { 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 ac8bfbc3b0..5b17b6b368 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -519,7 +519,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { sellerNode: StartedNode, buyerNode: StartedNode, assetToSell: StateAndRef): RunResult { - val buyerFlows: Observable> = buyerNode.internals.registerInitiatedFlow(BuyerAcceptor::class.java) + val buyerFlows: Observable> = buyerNode.registerInitiatedFlow(BuyerAcceptor::class.java) val firstBuyerFiber = buyerFlows.toFuture().map { it.stateMachine } val seller = SellerInitiator(buyer, notary, assetToSell, 1000.DOLLARS, anonymous) val sellerResult = sellerNode.services.startFlow(seller).resultFuture 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 4174882130..083001d9c2 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 @@ -672,7 +672,7 @@ class FlowFrameworkTests { initiatingFlowClass: KClass>, initiatedFlowVersion: Int = 1, noinline flowFactory: (FlowSession) -> P): CordaFuture

{ - val observable = internals.internalRegisterFlowFactory( + val observable = internalRegisterFlowFactory( initiatingFlowClass.java, InitiatedFlowFactory.CorDapp(initiatedFlowVersion, "", flowFactory), P::class.java, 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 3647a62d2b..c6fd88d4ae 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 @@ -66,7 +66,7 @@ class NodePair(private val mockNet: MockNetwork) { private set fun communicate(clientLogic: AbstractClientLogic, rebootClient: Boolean): FlowStateMachine { - server.internals.internalRegisterFlowFactory(AbstractClientLogic::class.java, InitiatedFlowFactory.Core { ServerLogic(it, serverRunning) }, ServerLogic::class.java, false) + server.internalRegisterFlowFactory(AbstractClientLogic::class.java, InitiatedFlowFactory.Core { ServerLogic(it, serverRunning) }, ServerLogic::class.java, false) client.services.startFlow(clientLogic) while (!serverRunning.get()) mockNet.runNetwork(1) if (rebootClient) { 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 de27d5517e..27318e4598 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 @@ -209,8 +209,8 @@ class NodeInterestRatesTest { val mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts", "net.corda.irs")) val aliceNode = mockNet.createPartyNode(ALICE_NAME) val oracleNode = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME)).apply { - internals.registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java) - internals.registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java) + registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java) + registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java) database.transaction { services.cordaService(NodeInterestRates.Oracle::class.java).knownFixes = TEST_DATA } 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 41a0bc7710..21ac207f72 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 @@ -136,10 +136,8 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten .replace("oracleXXX", RatesOracleNode.RATES_SERVICE_NAME.toString())) irs.fixedLeg.fixedRatePayer = node1.info.chooseIdentity() irs.floatingLeg.floatingRatePayer = node2.info.chooseIdentity() - - node1.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) - node2.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) - + node1.registerInitiatedFlow(FixingFlow.Fixer::class.java) + node2.registerInitiatedFlow(FixingFlow.Fixer::class.java) @InitiatingFlow class StartDealFlow(val otherParty: Party, val payload: AutoOffer) : FlowLogic() { @@ -152,9 +150,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten @InitiatedBy(StartDealFlow::class) class AcceptDealFlow(otherSession: FlowSession) : Acceptor(otherSession) - - val acceptDealFlows: Observable = node2.internals.registerInitiatedFlow(AcceptDealFlow::class.java) - + val acceptDealFlows: Observable = node2.registerInitiatedFlow(AcceptDealFlow::class.java) val acceptorTxFuture = acceptDealFlows.toFuture().toCompletableFuture().thenCompose { uncheckedCast, FlowStateMachine>(it.stateMachine).resultFuture.toCompletableFuture() } 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 b741356548..af253af527 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 @@ -38,8 +38,7 @@ class TraderDemoTest { startNode(providedName = DUMMY_BANK_B.name, rpcUsers = listOf(demoUser)), startNode(providedName = BOC.name, rpcUsers = listOf(bankUser)) ).map { (it.getOrThrow() as NodeHandle.InProcess).node } - - nodeA.internals.registerInitiatedFlow(BuyerFlow::class.java) + nodeA.registerInitiatedFlow(BuyerFlow::class.java) val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map { val client = CordaRPCClient(it.internals.configuration.rpcAddress!!) client.start(demoUser.username, demoUser.password).proxy 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/FlowStackSnapshotTest.kt index e824c782f9..f33897854c 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/FlowStackSnapshotTest.kt @@ -291,7 +291,7 @@ class FlowStackSnapshotTest { fun `flowStackSnapshot object is serializable`() { val mockNet = MockNetwork(threadPerNode = true) val node = mockNet.createPartyNode() - node.internals.registerInitiatedFlow(DummyFlow::class.java) + node.registerInitiatedFlow(DummyFlow::class.java) node.services.startFlow(FlowStackSnapshotSerializationTestingFlow()).resultFuture.get() val thrown = try { mockNet.stopNodes() From f135d578202d6699b28f599b33fdb3b5189c968a Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Mon, 27 Nov 2017 13:43:30 +0000 Subject: [PATCH 04/27] CORDA-553 - Plumb the transform schema into the AMQP serialisation framework This change doesn't enable anything, it just changes the code to pass around both relevant schemas instead of a single one from the AMQP envelope. The actual evolver will build ontop of this --- .../amqp/AMQPPrimitiveSerializer.kt | 5 ++- .../serialization/amqp/AMQPSerializer.kt | 2 +- .../serialization/amqp/ArraySerializer.kt | 4 +- .../amqp/CollectionSerializer.kt | 4 +- .../serialization/amqp/CustomSerializer.kt | 10 ++--- .../amqp/DeserializationInput.kt | 12 +++--- .../serialization/amqp/EnumSerializer.kt | 2 +- .../serialization/amqp/EvolutionSerializer.kt | 8 ++-- .../serialization/amqp/MapSerializer.kt | 10 ++--- .../serialization/amqp/ObjectSerializer.kt | 11 +++-- .../serialization/amqp/PropertySerializer.kt | 10 ++--- .../serialization/amqp/SerializerFactory.kt | 18 +++++--- .../serialization/amqp/SingletonSerializer.kt | 2 +- .../serialization/amqp/TransformsSchema.kt | 3 ++ .../amqp/custom/InputStreamSerializer.kt | 4 +- .../amqp/custom/PrivateKeySerializer.kt | 4 +- .../amqp/custom/PublicKeySerializer.kt | 4 +- .../amqp/custom/X509CertificateSerializer.kt | 4 +- .../serialization/amqp/EnumEvolveTests.kt | 43 +++++++++++++++++++ .../amqp/OverridePKSerializerTest.kt | 2 +- 20 files changed, 110 insertions(+), 52 deletions(-) create mode 100644 node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPPrimitiveSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPPrimitiveSerializer.kt index 02a4379fc8..dc673940b1 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPPrimitiveSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPPrimitiveSerializer.kt @@ -26,5 +26,8 @@ class AMQPPrimitiveSerializer(clazz: Class<*>) : AMQPSerializer { } } - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): Any = (obj as? Binary)?.array ?: obj + override fun readObject( + obj: Any, + schemas: SerializationSchemas, + input: DeserializationInput): Any = (obj as? Binary)?.array ?: obj } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializer.kt index 707cefa44f..d4596c5ec0 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializer.kt @@ -35,5 +35,5 @@ interface AMQPSerializer { /** * Read the given object from the input. The envelope is provided in case the schema is required. */ - fun readObject(obj: Any, schema: Schema, input: DeserializationInput): T + fun readObject(obj: Any, schema: SerializationSchemas, input: DeserializationInput): T } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ArraySerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ArraySerializer.kt index 10f320a55a..732723a493 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ArraySerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ArraySerializer.kt @@ -56,9 +56,9 @@ open class ArraySerializer(override val type: Type, factory: SerializerFactory) } } - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): Any { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any { if (obj is List<*>) { - return obj.map { input.readObjectOrNull(it, schema, elementType) }.toArrayOfType(elementType) + return obj.map { input.readObjectOrNull(it, schemas, elementType) }.toArrayOfType(elementType) } else throw NotSerializableException("Expected a List but found $obj") } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CollectionSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CollectionSerializer.kt index fc176cfda7..d39456cb9e 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CollectionSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CollectionSerializer.kt @@ -77,8 +77,8 @@ class CollectionSerializer(val declaredType: ParameterizedType, factory: Seriali } } - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): Any = ifThrowsAppend({ declaredType.typeName }) { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any = ifThrowsAppend({ declaredType.typeName }) { // TODO: Can we verify the entries in the list? - concreteBuilder((obj as List<*>).map { input.readObjectOrNull(it, schema, declaredType.actualTypeArguments[0]) }) + concreteBuilder((obj as List<*>).map { input.readObjectOrNull(it, schemas, declaredType.actualTypeArguments[0]) }) } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt index e039911ec0..870bfbaccc 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt @@ -67,8 +67,8 @@ abstract class CustomSerializer : AMQPSerializer { superClassSerializer.writeDescribedObject(obj, data, type, output) } - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): T { - return superClassSerializer.readObject(obj, schema, input) + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): T { + return superClassSerializer.readObject(obj, schemas, input) } } @@ -133,8 +133,8 @@ abstract class CustomSerializer : AMQPSerializer { } } - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): T { - val proxy: P = uncheckedCast(proxySerializer.readObject(obj, schema, input)) + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): T { + val proxy: P = uncheckedCast(proxySerializer.readObject(obj, schemas, input)) return fromProxy(proxy) } } @@ -166,7 +166,7 @@ abstract class CustomSerializer : AMQPSerializer { data.putString(unmaker(obj)) } - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): T { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): T { val proxy = obj as String return maker(proxy) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializationInput.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializationInput.kt index 7269aa8c40..e260f5a891 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializationInput.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializationInput.kt @@ -97,21 +97,21 @@ class DeserializationInput(internal val serializerFactory: SerializerFactory) { @Throws(NotSerializableException::class) fun deserialize(bytes: ByteSequence, clazz: Class): T = des { val envelope = getEnvelope(bytes) - clazz.cast(readObjectOrNull(envelope.obj, envelope.schema, clazz)) + clazz.cast(readObjectOrNull(envelope.obj, SerializationSchemas(envelope.schema, envelope.transformsSchema), clazz)) } @Throws(NotSerializableException::class) fun deserializeAndReturnEnvelope(bytes: SerializedBytes, clazz: Class): ObjectAndEnvelope = des { val envelope = getEnvelope(bytes) // Now pick out the obj and schema from the envelope. - ObjectAndEnvelope(clazz.cast(readObjectOrNull(envelope.obj, envelope.schema, clazz)), envelope) + ObjectAndEnvelope(clazz.cast(readObjectOrNull(envelope.obj, SerializationSchemas(envelope.schema, envelope.transformsSchema), clazz)), envelope) } - internal fun readObjectOrNull(obj: Any?, schema: Schema, type: Type): Any? { + internal fun readObjectOrNull(obj: Any?, schema: SerializationSchemas, type: Type): Any? { return if (obj == null) null else readObject(obj, schema, type) } - internal fun readObject(obj: Any, schema: Schema, type: Type): Any = + internal fun readObject(obj: Any, schemas: SerializationSchemas, type: Type): Any = if (obj is DescribedType && ReferencedObject.DESCRIPTOR == obj.descriptor) { // It must be a reference to an instance that has already been read, cheaply and quickly returning it by reference. val objectIndex = (obj.described as UnsignedInteger).toInt() @@ -127,11 +127,11 @@ class DeserializationInput(internal val serializerFactory: SerializerFactory) { val objectRead = when (obj) { is DescribedType -> { // Look up serializer in factory by descriptor - val serializer = serializerFactory.get(obj.descriptor, schema) + val serializer = serializerFactory.get(obj.descriptor, schemas) if (SerializerFactory.AnyType != type && serializer.type != type && with(serializer.type) { !isSubClassOf(type) && !materiallyEquivalentTo(type) }) throw NotSerializableException("Described type with descriptor ${obj.descriptor} was " + "expected to be of type $type but was ${serializer.type}") - serializer.readObject(obj.described, schema, this) + serializer.readObject(obj.described, schemas, this) } is Binary -> obj.array else -> obj // this will be the case for primitive types like [boolean] et al. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt index a9e6d04cd4..6aaaf8701e 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt @@ -27,7 +27,7 @@ class EnumSerializer(declaredType: Type, declaredClass: Class<*>, factory: Seria output.writeTypeNotations(typeNotation) } - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): Any { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any { val enumName = (obj as List<*>)[0] as String val enumOrd = obj[1] as Int val fromOrd = type.asClass()!!.enumConstants[enumOrd] as Enum<*>? diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt index 2632b4b46d..59f22598a0 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt @@ -32,8 +32,8 @@ class EvolutionSerializer( * @param property object to read the actual property value */ data class OldParam(val type: Type, val idx: Int, val property: PropertySerializer) { - fun readProperty(paramValues: List<*>, schema: Schema, input: DeserializationInput) = - property.readProperty(paramValues[idx], schema, input) + fun readProperty(paramValues: List<*>, schemas: SerializationSchemas, input: DeserializationInput) = + property.readProperty(paramValues[idx], schemas, input) } companion object { @@ -121,10 +121,10 @@ class EvolutionSerializer( * * TODO: Object references */ - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): Any { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any { if (obj !is List<*>) throw NotSerializableException("Body of described type is unexpected $obj") - return construct(readers.map { it?.readProperty(obj, schema, input) }) + return construct(readers.map { it?.readProperty(obj, schemas, input) }) } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt index 932b641ad2..ae7f73e6c9 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt @@ -88,15 +88,15 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial } } - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): Any = ifThrowsAppend({ declaredType.typeName }) { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any = ifThrowsAppend({ declaredType.typeName }) { // TODO: General generics question. Do we need to validate that entries in Maps and Collections match the generic type? Is it a security hole? - val entries: Iterable> = (obj as Map<*, *>).map { readEntry(schema, input, it) } + val entries: Iterable> = (obj as Map<*, *>).map { readEntry(schemas, input, it) } concreteBuilder(entries.toMap()) } - private fun readEntry(schema: Schema, input: DeserializationInput, entry: Map.Entry) = - input.readObjectOrNull(entry.key, schema, declaredType.actualTypeArguments[0]) to - input.readObjectOrNull(entry.value, schema, declaredType.actualTypeArguments[1]) + private fun readEntry(schemas: SerializationSchemas, input: DeserializationInput, entry: Map.Entry) = + input.readObjectOrNull(entry.key, schemas, declaredType.actualTypeArguments[0]) to + input.readObjectOrNull(entry.value, schemas, declaredType.actualTypeArguments[1]) // Cannot use * as a bound for EnumMap and EnumSet since * is not an enum. So, we use a sample enum instead. // We don't actually care about the type, we just need to make the compiler happier. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ObjectSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ObjectSerializer.kt index 433f8731ba..b405b99a98 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ObjectSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ObjectSerializer.kt @@ -55,10 +55,15 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS } } - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): Any = ifThrowsAppend({ clazz.typeName }) { + override fun readObject( + obj: Any, + schemas: SerializationSchemas, + input: DeserializationInput): Any = ifThrowsAppend({ clazz.typeName }) { if (obj is List<*>) { - if (obj.size > propertySerializers.size) throw NotSerializableException("Too many properties in described type $typeName") - val params = obj.zip(propertySerializers).map { it.second.readProperty(it.first, schema, input) } + if (obj.size > propertySerializers.size) { + throw NotSerializableException("Too many properties in described type $typeName") + } + val params = obj.zip(propertySerializers).map { it.second.readProperty(it.first, schemas, input) } construct(params) } else throw NotSerializableException("Body of described type is unexpected $obj") } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializer.kt index 59c8b983fa..230a3eb4a8 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializer.kt @@ -14,7 +14,7 @@ import kotlin.reflect.jvm.javaGetter sealed class PropertySerializer(val name: String, val readMethod: Method?, val resolvedType: Type) { abstract fun writeClassInfo(output: SerializationOutput) abstract fun writeProperty(obj: Any?, data: Data, output: SerializationOutput) - abstract fun readProperty(obj: Any?, schema: Schema, input: DeserializationInput): Any? + abstract fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput): Any? val type: String = generateType() val requires: List = generateRequires() @@ -91,8 +91,8 @@ sealed class PropertySerializer(val name: String, val readMethod: Method?, val r } } - override fun readProperty(obj: Any?, schema: Schema, input: DeserializationInput): Any? = ifThrowsAppend({ nameForDebug }) { - input.readObjectOrNull(obj, schema, resolvedType) + override fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput): Any? = ifThrowsAppend({ nameForDebug }) { + input.readObjectOrNull(obj, schemas, resolvedType) } override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput) = ifThrowsAppend({ nameForDebug }) { @@ -108,7 +108,7 @@ sealed class PropertySerializer(val name: String, val readMethod: Method?, val r class AMQPPrimitivePropertySerializer(name: String, readMethod: Method?, resolvedType: Type) : PropertySerializer(name, readMethod, resolvedType) { override fun writeClassInfo(output: SerializationOutput) {} - override fun readProperty(obj: Any?, schema: Schema, input: DeserializationInput): Any? { + override fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput): Any? { return if (obj is Binary) obj.array else obj } @@ -131,7 +131,7 @@ sealed class PropertySerializer(val name: String, val readMethod: Method?, val r PropertySerializer(name, readMethod, Character::class.java) { override fun writeClassInfo(output: SerializationOutput) {} - override fun readProperty(obj: Any?, schema: Schema, input: DeserializationInput): Any? { + override fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput): Any? { return if (obj == null) null else (obj as Short).toChar() } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt index 5075593ace..5b114cc747 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt @@ -13,7 +13,8 @@ import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CopyOnWriteArrayList import javax.annotation.concurrent.ThreadSafe -data class FactorySchemaAndDescriptor(val schema: Schema, val typeDescriptor: Any) +data class SerializationSchemas (val schema: Schema, val transforms: TransformsSchema) +data class FactorySchemaAndDescriptor(val schemas: SerializationSchemas, val typeDescriptor: Any) /** * Factory of serializers designed to be shared across threads and invocations. @@ -40,7 +41,10 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { val classloader: ClassLoader get() = classCarpenter.classloader - private fun getEvolutionSerializer(typeNotation: TypeNotation, newSerializer: AMQPSerializer): AMQPSerializer { + private fun getEvolutionSerializer( + typeNotation: TypeNotation, + newSerializer: AMQPSerializer, + transforms : TransformsSchema ): AMQPSerializer { return serializersByDescriptor.computeIfAbsent(typeNotation.descriptor.name!!) { when (typeNotation) { is CompositeType -> EvolutionSerializer.make(typeNotation, newSerializer as ObjectSerializer, this) @@ -168,7 +172,7 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { * contained in the [Schema]. */ @Throws(NotSerializableException::class) - fun get(typeDescriptor: Any, schema: Schema): AMQPSerializer { + fun get(typeDescriptor: Any, schema: SerializationSchemas): AMQPSerializer { return serializersByDescriptor[typeDescriptor] ?: { processSchema(FactorySchemaAndDescriptor(schema, typeDescriptor)) serializersByDescriptor[typeDescriptor] ?: throw NotSerializableException( @@ -194,9 +198,9 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { * Iterate over an AMQP schema, for each type ascertain weather it's on ClassPath of [classloader] amd * if not use the [ClassCarpenter] to generate a class to use in it's place */ - private fun processSchema(schema: FactorySchemaAndDescriptor, sentinel: Boolean = false) { + private fun processSchema(schemaAndDescriptor: FactorySchemaAndDescriptor, sentinel: Boolean = false) { val metaSchema = CarpenterMetaSchema.newInstance() - for (typeNotation in schema.schema.types) { + for (typeNotation in schemaAndDescriptor.schemas.schema.types) { try { val serialiser = processSchemaEntry(typeNotation) @@ -204,7 +208,7 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { // doesn't match that of the serialised object then we are dealing with different // instance of the class, as such we need to build an EvolutionSerialiser if (serialiser.typeDescriptor != typeNotation.descriptor.name) { - getEvolutionSerializer(typeNotation, serialiser) + getEvolutionSerializer(typeNotation, serialiser, schemaAndDescriptor.schemas.transforms) } } catch (e: ClassNotFoundException) { if (sentinel) throw e @@ -215,7 +219,7 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { if (metaSchema.isNotEmpty()) { val mc = MetaCarpenter(metaSchema, classCarpenter) mc.build() - processSchema(schema, true) + processSchema(schemaAndDescriptor, true) } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SingletonSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SingletonSerializer.kt index e38914cd4d..ac226008f7 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SingletonSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SingletonSerializer.kt @@ -28,7 +28,7 @@ class SingletonSerializer(override val type: Class<*>, val singleton: Any, facto } } - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): Any { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any { return singleton } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt index 2bde766c2c..974de134b1 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt @@ -85,6 +85,9 @@ class UnknownTransform : Transform() { override val name: String get() = typeName } +/** + * Used by the unit testing framework + */ class UnknownTestTransform(val a: Int, val b: Int, val c: Int) : Transform() { companion object : DescribedTypeConstructor { val typeName = "UnknownTest" diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/InputStreamSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/InputStreamSerializer.kt index 772ab65771..47c5f0b539 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/InputStreamSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/InputStreamSerializer.kt @@ -34,8 +34,8 @@ object InputStreamSerializer : CustomSerializer.Implements(InputStr } } - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): InputStream { - val bits = input.readObject(obj, schema, ByteArray::class.java) as ByteArray + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): InputStream { + val bits = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray return ByteArrayInputStream(bits) } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PrivateKeySerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PrivateKeySerializer.kt index f9a2d1817d..9609c926fe 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PrivateKeySerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PrivateKeySerializer.kt @@ -20,8 +20,8 @@ object PrivateKeySerializer : CustomSerializer.Implements(PrivateKey output.writeObject(obj.encoded, data, clazz) } - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): PrivateKey { - val bits = input.readObject(obj, schema, ByteArray::class.java) as ByteArray + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): PrivateKey { + val bits = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray return Crypto.decodePrivateKey(bits) } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PublicKeySerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PublicKeySerializer.kt index 95a5907cc6..13faad17a6 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PublicKeySerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PublicKeySerializer.kt @@ -17,8 +17,8 @@ object PublicKeySerializer : CustomSerializer.Implements(PublicKey::c output.writeObject(obj.encoded, data, clazz) } - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): PublicKey { - val bits = input.readObject(obj, schema, ByteArray::class.java) as ByteArray + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): PublicKey { + val bits = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray return Crypto.decodePublicKey(bits) } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CertificateSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CertificateSerializer.kt index 2942153bba..dbbd6c6598 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CertificateSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CertificateSerializer.kt @@ -20,8 +20,8 @@ object X509CertificateSerializer : CustomSerializer.Implements( output.writeObject(obj.encoded, data, clazz) } - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): X509Certificate { - val bits = input.readObject(obj, schema, ByteArray::class.java) as ByteArray + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): X509Certificate { + val bits = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray return X509CertificateFactory().generateCertificate(bits.inputStream()) } } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt new file mode 100644 index 0000000000..d984cff766 --- /dev/null +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt @@ -0,0 +1,43 @@ +package net.corda.nodeapi.internal.serialization.amqp + +import net.corda.core.serialization.SerializedBytes +import org.assertj.core.api.Assertions +import org.junit.Test +import java.io.File +import java.io.NotSerializableException +import java.net.URI + +// NOTE: To recreate the test files used by these tests uncomment the original test classes and comment +// the new ones out, then change each test to write out the serialized bytes rather than read +// the file. +class EnumEvolveTests { + var localPath = "file:///path/to/corda/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp" + + // Version of the class as it was serialised + // + // @CordaSerializationTransformEnumDefault("D", "C") + // enum class DeserializeNewerSetToUnknown { + // A, B, C, D + // } + // + // Version of the class as it's used in the test + enum class DeserializeNewerSetToUnknown { + A, B, C + } + + @Test + fun deserialiseNewerSetToUnknown() { + val resource = "${this.javaClass.simpleName}.${testName()}" + val sf = testDefaultFactory() + + data class C (val e : DeserializeNewerSetToUnknown) + + // Uncomment to re-generate test files + //File(URI("$localPath/$resource")).writeBytes( + // SerializationOutput(sf).serialize(C(DeserializeNewerSetToUnknown.D)).bytes) + + Assertions.assertThatThrownBy { + DeserializationInput(sf).deserialize(SerializedBytes(File(URI("$localPath/$resource")).readBytes())) + }.isInstanceOf(NotSerializableException::class.java) + } +} \ No newline at end of file diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/OverridePKSerializerTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/OverridePKSerializerTest.kt index 308394c5c5..108f2328f4 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/OverridePKSerializerTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/OverridePKSerializerTest.kt @@ -17,7 +17,7 @@ class OverridePKSerializerTest { throw SerializerTestException("Custom write call") } - override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): PublicKey { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): PublicKey { throw SerializerTestException("Custom read call") } From 4c2f0d7913bc0d26c0ae3f3af9976b26d3c6ba97 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Tue, 28 Nov 2017 10:03:01 +0000 Subject: [PATCH 05/27] Fix broken unit test At the moment the test just asserts we can't evolve enums, it's a placeholder for the next phase of the changes --- .../serialization/amqp/EnumEvolveTests.kt | 14 ++++++-------- .../EnumEvolveTests.deserialiseNewerSetToUnknown | Bin 0 -> 818 bytes 2 files changed, 6 insertions(+), 8 deletions(-) create mode 100644 node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserialiseNewerSetToUnknown diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt index d984cff766..30bc7a990b 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt @@ -1,5 +1,6 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.core.serialization.CordaSerializationTransformEnumDefault import net.corda.core.serialization.SerializedBytes import org.assertj.core.api.Assertions import org.junit.Test @@ -16,14 +17,10 @@ class EnumEvolveTests { // Version of the class as it was serialised // // @CordaSerializationTransformEnumDefault("D", "C") - // enum class DeserializeNewerSetToUnknown { - // A, B, C, D - // } + // enum class DeserializeNewerSetToUnknown { A, B, C, D } // // Version of the class as it's used in the test - enum class DeserializeNewerSetToUnknown { - A, B, C - } + enum class DeserializeNewerSetToUnknown { A, B, C } @Test fun deserialiseNewerSetToUnknown() { @@ -33,11 +30,12 @@ class EnumEvolveTests { data class C (val e : DeserializeNewerSetToUnknown) // Uncomment to re-generate test files - //File(URI("$localPath/$resource")).writeBytes( + // File(URI("$localPath/$resource")).writeBytes( // SerializationOutput(sf).serialize(C(DeserializeNewerSetToUnknown.D)).bytes) Assertions.assertThatThrownBy { - DeserializationInput(sf).deserialize(SerializedBytes(File(URI("$localPath/$resource")).readBytes())) + DeserializationInput(sf).deserialize(SerializedBytes( + File(EvolvabilityTests::class.java.getResource(resource).toURI()).readBytes())) }.isInstanceOf(NotSerializableException::class.java) } } \ No newline at end of file diff --git a/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserialiseNewerSetToUnknown b/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.deserialiseNewerSetToUnknown new file mode 100644 index 0000000000000000000000000000000000000000..f559d20e26356aba7052a81459b5f7a12a5ba6b5 GIT binary patch literal 818 zcmcIi%SyvQ6rD->z;@xrh2TmlZiJz>T0|(MZBi(xMNFU+HD;xePqna`IKBRHcX)etFl^N80>N)Q|1oNs9XDvZM~*vkDqZiU*1MFvoW`MP>q%dnKBF7~y*>}!Y^s-cV^c@7Eh|IeiPWpeh@ zq-7Y5rV`AFkK8)d42x$1KVeER{Vl)6Yl!oV8p?~j47 Date: Tue, 28 Nov 2017 10:32:30 +0000 Subject: [PATCH 06/27] ENT-1128 fix node restarts when using AzureSQLServer (#2139) --- .../network/PersistentNetworkMapCache.kt | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index 982f4da070..0b04b0eff3 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -172,13 +172,13 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence) : S if (previousNode == null) { logger.info("No previous node found") database.transaction { - updateInfoDB(node) + updateInfoDB(node, session) changePublisher.onNext(MapChange.Added(node)) } } else if (previousNode != node) { logger.info("Previous node was found as: $previousNode") database.transaction { - updateInfoDB(node) + updateInfoDB(node, session) changePublisher.onNext(MapChange.Modified(node, previousNode)) } } else { @@ -233,25 +233,14 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence) : S } } - private fun updateInfoDB(nodeInfo: NodeInfo) { - // TODO Temporary workaround to force isolated transaction (otherwise it causes race conditions when processing - // network map registration on network map node) - database.dataSource.connection.use { - val session = database.entityManagerFactory.withOptions().connection(it.apply { - transactionIsolation = 1 - }).openSession() - session.use { - val tx = session.beginTransaction() - // TODO For now the main legal identity is left in NodeInfo, this should be set comparision/come up with index for NodeInfo? - val info = findByIdentityKey(session, nodeInfo.legalIdentitiesAndCerts.first().owningKey) - val nodeInfoEntry = generateMappedObject(nodeInfo) - if (info.isNotEmpty()) { - nodeInfoEntry.id = info[0].id - } - session.merge(nodeInfoEntry) - tx.commit() - } + private fun updateInfoDB(nodeInfo: NodeInfo, session: Session) { + // TODO For now the main legal identity is left in NodeInfo, this should be set comparision/come up with index for NodeInfo? + val info = findByIdentityKey(session, nodeInfo.legalIdentitiesAndCerts.first().owningKey) + val nodeInfoEntry = generateMappedObject(nodeInfo) + if (info.isNotEmpty()) { + nodeInfoEntry.id = info.first().id } + session.merge(nodeInfoEntry) } private fun removeInfoDB(session: Session, nodeInfo: NodeInfo) { From ff9e7474b14203b72d29839e05aba9cf78e662fe Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Tue, 28 Nov 2017 11:35:59 +0000 Subject: [PATCH 07/27] CORDA-654 Make MOCK_IDENTITIES less special (#2114) * Make rigorousMock usable from Java * Show args in mock failure message --- .../services/vault/VaultQueryJavaTests.java | 19 +++++++++--------- .../persistence/HibernateConfigurationTest.kt | 20 +++++++++++++------ .../net/corda/testing/node/MockServices.kt | 9 ++------- .../kotlin/net/corda/testing/CoreTestUtils.kt | 11 +++++----- 4 files changed, 31 insertions(+), 28 deletions(-) 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 9b2a1de3c0..93c4ae5da4 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 @@ -20,6 +20,7 @@ import net.corda.finance.contracts.DealState; import net.corda.finance.contracts.asset.Cash; import net.corda.finance.contracts.asset.CashUtilities; import net.corda.finance.schemas.CashSchemaV1; +import net.corda.node.services.identity.InMemoryIdentityService; import net.corda.node.utilities.CordaPersistence; import net.corda.node.utilities.DatabaseTransaction; import net.corda.testing.SerializationEnvironmentRule; @@ -36,7 +37,6 @@ import rx.Observable; import java.io.IOException; import java.lang.reflect.Field; import java.security.InvalidAlgorithmParameterException; -import java.security.KeyPair; import java.security.cert.CertificateException; import java.util.*; import java.util.stream.Collectors; @@ -50,7 +50,6 @@ import static net.corda.finance.contracts.asset.CashUtilities.*; import static net.corda.testing.CoreTestUtils.*; import static net.corda.testing.TestConstants.*; import static net.corda.testing.node.MockServices.makeTestDatabaseAndMockServices; -import static net.corda.testing.node.MockServices.makeTestIdentityService; import static org.assertj.core.api.Assertions.assertThat; public class VaultQueryJavaTests { @@ -64,18 +63,18 @@ 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()); - ArrayList keys = new ArrayList<>(); - keys.add(getMEGA_CORP_KEY()); - keys.add(getDUMMY_NOTARY_KEY()); - IdentityService identitySvc = makeTestIdentityService(); - @SuppressWarnings("unchecked") - Pair databaseAndServices = makeTestDatabaseAndMockServices(keys, identitySvc, cordappPackages); + IdentityService identitySvc = new InMemoryIdentityService( + Arrays.asList(getMEGA_CORP_IDENTITY(), getDUMMY_CASH_ISSUER_IDENTITY(), getDUMMY_NOTARY_IDENTITY()), + Collections.emptySet(), + getDEV_TRUST_ROOT()); + Pair databaseAndServices = makeTestDatabaseAndMockServices( + Arrays.asList(getMEGA_CORP_KEY(), getDUMMY_NOTARY_KEY()), + identitySvc, + cordappPackages); issuerServices = new MockServices(cordappPackages, getDUMMY_CASH_ISSUER_NAME(), getDUMMY_CASH_ISSUER_KEY(), getBOC_KEY()); database = databaseAndServices.getFirst(); services = databaseAndServices.getSecond(); vaultService = services.getVaultService(); - services.getIdentityService().verifyAndRegisterIdentity(getDUMMY_CASH_ISSUER_IDENTITY()); - services.getIdentityService().verifyAndRegisterIdentity(getDUMMY_NOTARY_IDENTITY()); } @After 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 1715bf6c23..24d872b0f2 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 @@ -1,12 +1,17 @@ package net.corda.node.services.persistence +import com.nhaarman.mockito_kotlin.any +import com.nhaarman.mockito_kotlin.doReturn +import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateRef import net.corda.core.contracts.TransactionState import net.corda.core.crypto.SecureHash import net.corda.core.crypto.generateKeyPair +import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party import net.corda.core.node.StatesToRecord +import net.corda.core.node.services.IdentityService import net.corda.core.node.services.Vault import net.corda.core.node.services.VaultService import net.corda.core.schemas.CommonSchemaV1 @@ -17,10 +22,7 @@ import net.corda.core.utilities.toBase58String import net.corda.finance.DOLLARS import net.corda.finance.POUNDS import net.corda.finance.SWISS_FRANCS -import net.corda.finance.contracts.asset.Cash -import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY -import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_NAME -import net.corda.finance.contracts.asset.DummyFungibleContract +import net.corda.finance.contracts.asset.* import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.schemas.SampleCashSchemaV2 import net.corda.finance.schemas.SampleCashSchemaV3 @@ -37,7 +39,6 @@ import net.corda.testing.contracts.fillWithSomeTestDeals import net.corda.testing.contracts.fillWithSomeTestLinearStates import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties -import net.corda.testing.node.MockServices.Companion.makeTestIdentityService import net.corda.testing.schemas.DummyLinearStateSchemaV1 import net.corda.testing.schemas.DummyLinearStateSchemaV2 import org.assertj.core.api.Assertions @@ -84,7 +85,14 @@ class HibernateConfigurationTest { issuerServices = MockServices(cordappPackages, DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY) notaryServices = MockServices(cordappPackages, DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) val dataSourceProps = makeTestDataSourceProperties() - database = configureDatabase(dataSourceProps, DatabaseConfig(), makeTestIdentityService()) + val identityService = rigorousMock().also { mock -> + doReturn(null).whenever(mock).wellKnownPartyFromAnonymous(any()) + listOf(DUMMY_CASH_ISSUER_IDENTITY.party, DUMMY_NOTARY).forEach { + doReturn(it).whenever(mock).wellKnownPartyFromAnonymous(it) + doReturn(it).whenever(mock).wellKnownPartyFromX500Name(it.name) + } + } + database = configureDatabase(dataSourceProps, DatabaseConfig(), identityService) database.transaction { hibernateConfig = database.hibernateConfig // `consumeCash` expects we can self-notarise transactions 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 1c88471207..30b6735913 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 @@ -77,12 +77,7 @@ open class MockServices( return props } - /** - * Creates an instance of [InMemoryIdentityService] with [MOCK_IDENTITIES]. - */ - @JvmStatic - fun makeTestIdentityService() = InMemoryIdentityService(MOCK_IDENTITIES, trustRoot = DEV_TRUST_ROOT) - + private fun makeTestIdentityService() = InMemoryIdentityService(MOCK_IDENTITIES, trustRoot = DEV_TRUST_ROOT) /** * Makes database and mock services appropriate for unit tests. * @param keys a list of [KeyPair] instances to be used by [MockServices]. Defaults to [MEGA_CORP_KEY] @@ -149,7 +144,7 @@ open class MockServices( final override val attachments = MockAttachmentStorage() val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage = MockStateMachineRecordedTransactionMappingStorage() - override val identityService: IdentityService = InMemoryIdentityService(MOCK_IDENTITIES, trustRoot = DEV_TRUST_ROOT) + override val identityService: IdentityService = makeTestIdentityService() override val keyManagementService: KeyManagementService by lazy { MockKeyManagementService(identityService, *keys) } override val vaultService: VaultService get() = throw UnsupportedOperationException() 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 f483a74f25..2fdb34673d 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 @@ -3,7 +3,6 @@ package net.corda.testing -import com.nhaarman.mockito_kotlin.mock import net.corda.core.contracts.StateRef import net.corda.core.crypto.SecureHash import net.corda.core.crypto.generateKeyPair @@ -23,11 +22,12 @@ import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.serialization.amqp.AMQP_ENABLED +import org.mockito.Mockito.mock import org.mockito.internal.stubbing.answers.ThrowsException -import org.mockito.stubbing.Answer import java.nio.file.Files import java.security.KeyPair import java.security.PublicKey +import java.util.* import java.util.concurrent.atomic.AtomicInteger /** @@ -178,12 +178,13 @@ fun NodeInfo.singleIdentity(): Party = singleIdentityAndCert().party */ class UndefinedMockBehaviorException(message: String) : RuntimeException(message) +inline fun rigorousMock() = rigorousMock(T::class.java) /** * Create a Mockito mock that has [UndefinedMockBehaviorException] as the default behaviour of all methods. * @param T the type to mock. Note if you want to use [com.nhaarman.mockito_kotlin.doCallRealMethod] on a Kotlin interface, * it won't work unless you mock a (trivial) abstract implementation of that interface instead. */ -inline fun rigorousMock() = mock(Answer { +fun rigorousMock(clazz: Class): T = mock(clazz) { // Use ThrowsException to hack the stack trace, and lazily so we can customise the message: - ThrowsException(UndefinedMockBehaviorException("Please specify what should happen when '${it.method}' is called, or don't call it.")).answer(it) -}) + ThrowsException(UndefinedMockBehaviorException("Please specify what should happen when '${it.method}' is called, or don't call it. Args: ${Arrays.toString(it.arguments)}")).answer(it) +} From 74bf00c15547b785a402d98622d5a526b34969bd Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Tue, 28 Nov 2017 14:11:22 +0000 Subject: [PATCH 08/27] Show origin test in ThreadLeakException. (#2143) and downgrade an error to warn --- .../net/corda/core/internal/ToggleField.kt | 16 ++++++-------- .../internal/SerializationEnvironment.kt | 2 +- .../corda/core/internal/ToggleFieldTest.kt | 14 +++++++------ .../corda/testing/SerializationTestHelpers.kt | 21 ++++++++++++------- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/internal/ToggleField.kt b/core/src/main/kotlin/net/corda/core/internal/ToggleField.kt index 032d0d8802..fbfd51c235 100644 --- a/core/src/main/kotlin/net/corda/core/internal/ToggleField.kt +++ b/core/src/main/kotlin/net/corda/core/internal/ToggleField.kt @@ -43,7 +43,7 @@ class ThreadLocalToggleField(name: String) : ToggleField(name) { } /** The named thread has leaked from a previous test. */ -class ThreadLeakException : RuntimeException("Leaked thread detected: ${Thread.currentThread().name}") +class ThreadLeakException(valueToString: String) : RuntimeException("Leaked thread '${Thread.currentThread().name}' detected, value was: $valueToString") /** @param isAGlobalThreadBeingCreated whether a global thread (that should not inherit any value) is being created. */ class InheritableThreadLocalToggleField(name: String, @@ -54,16 +54,12 @@ class InheritableThreadLocalToggleField(name: String, } private inner class Holder(value: T) : AtomicReference(value) { - fun valueOrDeclareLeak() = get() ?: throw ThreadLeakException() + private val valueToString = value.toString() // We never set another non-null value. + fun valueOrDeclareLeak() = get() ?: throw ThreadLeakException(valueToString) fun childValue(): Holder? { - val e = ThreadLeakException() // Expensive, but so is starting the new thread. - return if (isAGlobalThreadBeingCreated(e.stackTrace)) { - get() ?: log.warn(e.message) - null - } else { - get() ?: log.error(e.message) - this - } + val e = ThreadLeakException(valueToString) // Expensive, but so is starting the new thread. + get() ?: log.warn(e.message) + return if (isAGlobalThreadBeingCreated(e.stackTrace)) null else this } } diff --git a/core/src/main/kotlin/net/corda/core/serialization/internal/SerializationEnvironment.kt b/core/src/main/kotlin/net/corda/core/serialization/internal/SerializationEnvironment.kt index 06531d0c81..a8cbfd5724 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/internal/SerializationEnvironment.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/internal/SerializationEnvironment.kt @@ -16,7 +16,7 @@ interface SerializationEnvironment { val checkpointContext: SerializationContext } -class SerializationEnvironmentImpl( +open class SerializationEnvironmentImpl( override val serializationFactory: SerializationFactory, override val p2pContext: SerializationContext, rpcServerContext: SerializationContext? = null, diff --git a/core/src/test/kotlin/net/corda/core/internal/ToggleFieldTest.kt b/core/src/test/kotlin/net/corda/core/internal/ToggleFieldTest.kt index 0967e93b8a..dd60591af3 100644 --- a/core/src/test/kotlin/net/corda/core/internal/ToggleFieldTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/ToggleFieldTest.kt @@ -134,6 +134,7 @@ class ToggleFieldTest { assertThatThrownBy { future.getOrThrow() } .isInstanceOf(ThreadLeakException::class.java) .hasMessageContaining(threadName) + .hasMessageContaining("hello") } } withSingleThreadExecutor { @@ -141,9 +142,9 @@ class ToggleFieldTest { } } - /** We log an error rather than failing-fast as the new thread may be an undetected global. */ + /** We log a warning rather than failing-fast as the new thread may be an undetected global. */ @Test - fun `leaked thread propagates holder to non-global thread, with error`() { + fun `leaked thread propagates holder to non-global thread, with warning`() { val field = inheritableThreadLocalToggleField() field.set("hello") withSingleThreadExecutor { @@ -153,17 +154,18 @@ class ToggleFieldTest { val leakedThreadName = Thread.currentThread().name verifyNoMoreInteractions(log) withSingleThreadExecutor { - // If ThreadLeakException is seen in practice, these errors form a trail of where the holder has been: - verify(log).error(argThat { contains(leakedThreadName) }) + // If ThreadLeakException is seen in practice, these warnings form a trail of where the holder has been: + verify(log).warn(argThat { contains(leakedThreadName) && contains("hello") }) val newThreadName = fork { Thread.currentThread().name }.getOrThrow() val future = fork(field::get) assertThatThrownBy { future.getOrThrow() } .isInstanceOf(ThreadLeakException::class.java) .hasMessageContaining(newThreadName) + .hasMessageContaining("hello") fork { verifyNoMoreInteractions(log) withSingleThreadExecutor { - verify(log).error(argThat { contains(newThreadName) }) + verify(log).warn(argThat { contains(newThreadName) && contains("hello") }) } }.getOrThrow() } @@ -183,7 +185,7 @@ class ToggleFieldTest { globalThreadCreationMethod { verifyNoMoreInteractions(log) withSingleThreadExecutor { - verify(log).warn(argThat { contains(leakedThreadName) }) + verify(log).warn(argThat { contains(leakedThreadName) && contains("hello") }) // In practice the new thread is for example a static thread we can't get rid of: assertNull(fork(field::get).getOrThrow()) } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt index 4920b9d940..c1a28a4f17 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt @@ -15,10 +15,13 @@ import org.junit.runners.model.Statement /** @param inheritable whether new threads inherit the environment, use sparingly. */ class SerializationEnvironmentRule(private val inheritable: Boolean = false) : TestRule { - val env: SerializationEnvironment = createTestSerializationEnv() - override fun apply(base: Statement, description: Description?) = object : Statement() { - override fun evaluate() = env.asContextEnv(inheritable) { - base.evaluate() + lateinit var env: SerializationEnvironment + override fun apply(base: Statement, description: Description): Statement { + env = createTestSerializationEnv(description.toString()) + return object : Statement() { + override fun evaluate() = env.asContextEnv(inheritable) { + base.evaluate() + } } } } @@ -30,7 +33,7 @@ interface GlobalSerializationEnvironment : SerializationEnvironment { /** @param inheritable whether new threads inherit the environment, use sparingly. */ fun withTestSerialization(inheritable: Boolean = false, callable: (SerializationEnvironment) -> T): T { - return createTestSerializationEnv().asContextEnv(inheritable, callable) + return createTestSerializationEnv("").asContextEnv(inheritable, callable) } /** @@ -53,7 +56,7 @@ fun withoutTestSerialization(callable: () -> T): T { */ fun setGlobalSerialization(armed: Boolean): GlobalSerializationEnvironment { return if (armed) { - object : GlobalSerializationEnvironment, SerializationEnvironment by createTestSerializationEnv() { + object : GlobalSerializationEnvironment, SerializationEnvironment by createTestSerializationEnv("") { override fun unset() { _globalSerializationEnv.set(null) } @@ -67,7 +70,7 @@ fun setGlobalSerialization(armed: Boolean): GlobalSerializationEnvironment { } } -private fun createTestSerializationEnv() = SerializationEnvironmentImpl( +private fun createTestSerializationEnv(label: String) = object : SerializationEnvironmentImpl( SerializationFactoryImpl().apply { registerScheme(KryoClientSerializationScheme()) registerScheme(KryoServerSerializationScheme()) @@ -78,7 +81,9 @@ private fun createTestSerializationEnv() = SerializationEnvironmentImpl( KRYO_RPC_SERVER_CONTEXT, KRYO_RPC_CLIENT_CONTEXT, if (isAmqpEnabled()) AMQP_STORAGE_CONTEXT else KRYO_STORAGE_CONTEXT, - KRYO_CHECKPOINT_CONTEXT) + KRYO_CHECKPOINT_CONTEXT) { + override fun toString() = "testSerializationEnv($label)" +} private const val AMQP_ENABLE_PROP_NAME = "net.corda.testing.amqp.enable" From c88c6202b928ec60f14a4487dcd057c40b67a1a8 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Tue, 28 Nov 2017 17:27:53 +0000 Subject: [PATCH 09/27] CORDA-553 - Review comments --- .../internal/serialization/amqp/SerializerFactory.kt | 8 +++++--- .../internal/serialization/amqp/EnumEvolveTests.kt | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt index 5b114cc747..d310f5a6bb 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt @@ -4,7 +4,9 @@ import com.google.common.primitives.Primitives import com.google.common.reflect.TypeResolver import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.ClassWhitelist -import net.corda.nodeapi.internal.serialization.carpenter.* +import net.corda.nodeapi.internal.serialization.carpenter.CarpenterMetaSchema +import net.corda.nodeapi.internal.serialization.carpenter.ClassCarpenter +import net.corda.nodeapi.internal.serialization.carpenter.MetaCarpenter import org.apache.qpid.proton.amqp.* import java.io.NotSerializableException import java.lang.reflect.* @@ -13,7 +15,7 @@ import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CopyOnWriteArrayList import javax.annotation.concurrent.ThreadSafe -data class SerializationSchemas (val schema: Schema, val transforms: TransformsSchema) +data class SerializationSchemas(val schema: Schema, val transforms: TransformsSchema) data class FactorySchemaAndDescriptor(val schemas: SerializationSchemas, val typeDescriptor: Any) /** @@ -44,7 +46,7 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { private fun getEvolutionSerializer( typeNotation: TypeNotation, newSerializer: AMQPSerializer, - transforms : TransformsSchema ): AMQPSerializer { + transforms: TransformsSchema): AMQPSerializer { return serializersByDescriptor.computeIfAbsent(typeNotation.descriptor.name!!) { when (typeNotation) { is CompositeType -> EvolutionSerializer.make(typeNotation, newSerializer as ObjectSerializer, this) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt index 30bc7a990b..265f4b9a0c 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt @@ -2,6 +2,7 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.serialization.CordaSerializationTransformEnumDefault import net.corda.core.serialization.SerializedBytes +import net.corda.testing.common.internal.ProjectStructure.projectRootDir import org.assertj.core.api.Assertions import org.junit.Test import java.io.File @@ -12,7 +13,8 @@ import java.net.URI // the new ones out, then change each test to write out the serialized bytes rather than read // the file. class EnumEvolveTests { - var localPath = "file:///path/to/corda/node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp" + var localPath = projectRootDir.toUri().resolve( + "node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp") // Version of the class as it was serialised // From cb1fa2e0173ce59eace5c9bd2e85f35fcf93e1a7 Mon Sep 17 00:00:00 2001 From: Michele Sollecito Date: Tue, 28 Nov 2017 17:33:02 +0000 Subject: [PATCH 10/27] Corda now works with H2 without the need to allow Hibernate to create the database automatically. (#2124) [CORDA-815]: Corda now instructs Hibernate to either adjust or validate the schema based on `devMode` property. Also renamed property `database.initDatabase` to `database.createSchemaAutomatically`. * [CORDA-815]: Renamed database.initDatabase to database.adjustSchemas. * Code review changes: removed property `database.initDatabase` altogether. * Code review changes: removed property `database.initDatabase` altogether. * Code review changes: removed property `database.initDatabase` altogether. * Code review changes: removed property `database.initDatabase` altogether. --- docs/source/changelog.rst | 3 ++ docs/source/corda-configuration-file.rst | 5 +-- .../test/node/NodeStatePersistenceTests.kt | 34 +++++++++++++++++++ .../node/internal/cordapp/CordappLoader.kt | 4 ++- .../node/services/config/NodeConfiguration.kt | 11 +++--- .../persistence/HibernateConfiguration.kt | 2 +- .../corda/node/services/vault/VaultSchema.kt | 1 + node/src/main/resources/reference.conf | 1 - .../config/NodeConfigurationImplTest.kt | 1 - 9 files changed, 50 insertions(+), 12 deletions(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 8404bd64c0..d84fd4eff7 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -6,6 +6,9 @@ from the previous milestone release. UNRELEASED ---------- +* Removed confusing property database.initDatabase, enabling its guarded behaviour with the dev-mode. + In devMode Hibernate will try to create or update database schemas, otherwise it will expect relevant schemas to be present + in the database (pre configured via DDL scripts or equivalent), and validate these are correct. * ``AttachmentStorage`` now allows providing metadata on attachments upload - username and filename, currently as plain strings. Those can be then used for querying, utilizing ``queryAttachments`` method of the same interface. diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index 3d346683ff..42af820033 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -70,7 +70,6 @@ path to the node's base directory. :database: Database configuration: - :initDatabase: Boolean on whether to initialise the database or just validate the schema. Defaults to true. :serverNameTablePrefix: Prefix string to apply to all the database tables. The default is no prefix. :transactionIsolationLevel: Transaction isolation level as defined by the ``TRANSACTION_`` constants in ``java.sql.Connection``, but without the "TRANSACTION_" prefix. Defaults to REPEATABLE_READ. @@ -141,7 +140,9 @@ path to the node's base directory. :devMode: This flag sets the node to run in development mode. On startup, if the keystore ``/certificates/sslkeystore.jks`` does not exist, a developer keystore will be used if ``devMode`` is true. The node will exit if ``devMode`` is false and the keystore does not exist. ``devMode`` also turns on background checking of flow checkpoints to shake out any - bugs in the checkpointing process. + bugs in the checkpointing process. Also, if ``devMode`` is true, Hibernate will try to automatically create the schema required by Corda + or update an existing schema in the SQL database; if ``devMode`` is false, Hibernate will simply validate an existing schema + failing on node start if this schema is either not present or not compatible. :detectPublicIp: This flag toggles the auto IP detection behaviour, it is enabled by default. On startup the node will attempt to discover its externally visible IP address first by looking for any public addresses on its network diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index 7c7e09073f..5a2b531cef 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -65,6 +65,40 @@ class NodeStatePersistenceTests { val retrievedMessage = stateAndRef!!.state.data.message assertEquals(message, retrievedMessage) } + + @Test + fun `persistent state survives node restart without reinitialising database schema`() { + // Temporary disable this test when executed on Windows. It is known to be sporadically failing. + // More investigation is needed to establish why. + assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win")) + + val user = User("mark", "dadada", setOf(startFlow(), invokeRpc("vaultQuery"))) + val message = Message("Hello world!") + val stateAndRef: StateAndRef? = driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) { + val nodeName = { + val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow() + val nodeName = nodeHandle.nodeInfo.chooseIdentity().name + // Ensure the notary node has finished starting up, before starting a flow that needs a notary + defaultNotaryNode.getOrThrow() + nodeHandle.rpcClientToNode().start(user.username, user.password).use { + it.proxy.startFlow(::SendMessageFlow, message, defaultNotaryIdentity).returnValue.getOrThrow() + } + nodeHandle.stop() + nodeName + }() + + val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user), customOverrides = mapOf("devMode" to "false")).getOrThrow() + val result = nodeHandle.rpcClientToNode().start(user.username, user.password).use { + val page = it.proxy.vaultQuery(MessageState::class.java) + page.states.singleOrNull() + } + nodeHandle.stop() + result + } + assertNotNull(stateAndRef) + val retrievedMessage = stateAndRef!!.state.data.message + assertEquals(message, retrievedMessage) + } } fun isQuasarAgentSpecified(): Boolean { diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt index 0e5c186af1..0719da9830 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt @@ -79,7 +79,9 @@ class CordappLoader private constructor(private val cordappJarPaths: List): CordappLoader { - check(configuration.devMode) { "Package scanning can only occur in dev mode" } + if (!configuration.devMode) { + logger.warn("Package scanning should only occur in dev mode!") + } val paths = getCordappsInDirectory(getCordappsPath(configuration.baseDirectory)) + testPackages.flatMap(this::createScanPackage) return cordappLoadersCache.computeIfAbsent(paths, { CordappLoader(paths) }) } diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index aecd630021..e3a915f6b1 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -19,7 +19,6 @@ interface NodeConfiguration : NodeSSLConfiguration { val emailAddress: String val exportJMXto: String val dataSourceProperties: Properties - val database: DatabaseConfig val rpcUsers: List val devMode: Boolean val devModeOptions: DevModeOptions? @@ -38,12 +37,13 @@ interface NodeConfiguration : NodeSSLConfiguration { val useTestClock: Boolean get() = false val detectPublicIp: Boolean get() = true val sshd: SSHDConfiguration? + val database: DatabaseConfig } data class DevModeOptions(val disableCheckpointChecker: Boolean = false) data class DatabaseConfig( - val initDatabase: Boolean = true, + val initialiseSchema: Boolean = true, val serverNameTablePrefix: String = "", val transactionIsolationLevel: TransactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ ) @@ -108,7 +108,6 @@ data class NodeConfigurationImpl( override val keyStorePassword: String, override val trustStorePassword: String, override val dataSourceProperties: Properties, - override val database: DatabaseConfig = DatabaseConfig(), override val compatibilityZoneURL: URL? = null, override val rpcUsers: List, override val verifierType: VerifierType, @@ -130,9 +129,9 @@ data class NodeConfigurationImpl( override val activeMQServer: ActiveMqServerConfiguration, // TODO See TODO above. Rename this to nodeInfoPollingFrequency and make it of type Duration override val additionalNodeInfoPollingFrequencyMsec: Long = 5.seconds.toMillis(), - override val sshd: SSHDConfiguration? = null - -) : NodeConfiguration { + override val sshd: SSHDConfiguration? = null, + override val database: DatabaseConfig = DatabaseConfig(initialiseSchema = devMode) + ) : NodeConfiguration { override val exportJMXto: String get() = "http" init { diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/HibernateConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/persistence/HibernateConfiguration.kt index d48c301301..89c04f7016 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/HibernateConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/HibernateConfiguration.kt @@ -54,7 +54,7 @@ class HibernateConfiguration(val schemaService: SchemaService, private val datab // necessarily remain and would likely be replaced by something like Liquibase. For now it is very convenient though. // TODO: replace auto schema generation as it isn't intended for production use, according to Hibernate docs. val config = Configuration(metadataSources).setProperty("hibernate.connection.provider_class", NodeDatabaseConnectionProvider::class.java.name) - .setProperty("hibernate.hbm2ddl.auto", if (databaseConfig.initDatabase) "update" else "validate") + .setProperty("hibernate.hbm2ddl.auto", if (databaseConfig.initialiseSchema) "update" else "validate") .setProperty("hibernate.format_sql", "true") .setProperty("hibernate.connection.isolation", databaseConfig.transactionIsolationLevel.jdbcValue.toString()) diff --git a/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt b/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt index bd1912ac22..78d5a3ac60 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt @@ -90,6 +90,7 @@ object VaultSchemaV1 : MappedSchema(schemaFamily = VaultSchema.javaClass, versio var externalId: String?, @Column(name = "uuid", nullable = false) + @Type(type = "uuid-char") var uuid: UUID ) : PersistentState() { constructor(uid: UniqueIdentifier, _participants: List) : diff --git a/node/src/main/resources/reference.conf b/node/src/main/resources/reference.conf index ac62cce4b3..dc91c3d1c0 100644 --- a/node/src/main/resources/reference.conf +++ b/node/src/main/resources/reference.conf @@ -11,7 +11,6 @@ dataSourceProperties = { } database = { transactionIsolationLevel = "REPEATABLE_READ" - initDatabase = true } devMode = true useHTTPS = false 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 6af672ceae..45ff894ae2 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 @@ -38,7 +38,6 @@ class NodeConfigurationImplTest { keyStorePassword = "cordacadevpass", trustStorePassword = "trustpass", dataSourceProperties = makeTestDataSourceProperties(ALICE.name.organisation), - database = DatabaseConfig(), rpcUsers = emptyList(), verifierType = VerifierType.InMemory, useHTTPS = false, From 1b5eeaaad0d4bbe304f3720ffdb89235f9f9d63e Mon Sep 17 00:00:00 2001 From: Viktor Kolomeyko Date: Tue, 28 Nov 2017 17:51:30 +0000 Subject: [PATCH 11/27] Create a dedicated Log4J2 file for NodeExplorer and output logging configuration used. (#2142) --- .../kotlin/net/corda/testing/driver/Driver.kt | 3 ++- tools/explorer/src/main/resources/log4j2.xml | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 tools/explorer/src/main/resources/log4j2.xml 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 b00bebad91..d062cc9163 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 @@ -979,7 +979,8 @@ class DriverDSL( "name" to nodeConf.myLegalName, "visualvm.display.name" to "corda-${nodeConf.myLegalName}", Node.scanPackagesSystemProperty to cordappPackages.joinToString(Node.scanPackagesSeparator), - "java.io.tmpdir" to System.getProperty("java.io.tmpdir") // Inherit from parent process + "java.io.tmpdir" to System.getProperty("java.io.tmpdir"), // Inherit from parent process + "log4j2.debug" to if(debugPort != null) "true" else "false" ) // See experimental/quasar-hook/README.md for how to generate. val excludePattern = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;" + diff --git a/tools/explorer/src/main/resources/log4j2.xml b/tools/explorer/src/main/resources/log4j2.xml new file mode 100644 index 0000000000..76a04c60b1 --- /dev/null +++ b/tools/explorer/src/main/resources/log4j2.xml @@ -0,0 +1,20 @@ + + + + info + + + + + + + + + + + + + + + + \ No newline at end of file From dbe2dca7b92a8f6dc0e1da7dba3313d01eae4380 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Wed, 29 Nov 2017 09:49:34 +0000 Subject: [PATCH 12/27] CORDA-654 Make VaultFiller a class so I can change its hardcoded bits (#2141) --- .../finance/contracts/CommercialPaperTests.kt | 6 +- .../finance/contracts/asset/CashTests.kt | 13 +- .../services/vault/VaultQueryJavaTests.java | 74 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 8 +- .../persistence/HibernateConfigurationTest.kt | 107 ++- .../services/vault/NodeVaultServiceTest.kt | 42 +- .../node/services/vault/VaultQueryTests.kt | 723 ++++++++---------- .../node/services/vault/VaultWithCashTest.kt | 18 +- .../corda/traderdemo/TraderDemoClientApi.kt | 2 +- .../corda/testing/contracts/VaultFiller.kt | 445 ++++++----- 10 files changed, 660 insertions(+), 778 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 5f925966e0..b64b9ccc88 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt @@ -13,7 +13,7 @@ import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.* import net.corda.testing.* -import net.corda.testing.contracts.fillWithSomeTestCash +import net.corda.testing.contracts.VaultFiller import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices import org.junit.Ignore @@ -240,7 +240,7 @@ class CommercialPaperTestsGeneric { aliceVaultService = aliceServices.vaultService databaseAlice.transaction { - alicesVault = aliceServices.fillWithSomeTestCash(9000.DOLLARS, issuerServices, atLeastThisManyStates = 1, atMostThisManyStates = 1, issuedBy = DUMMY_CASH_ISSUER) + alicesVault = VaultFiller(aliceServices).fillWithSomeTestCash(9000.DOLLARS, issuerServices, atLeastThisManyStates = 1, atMostThisManyStates = 1, issuedBy = DUMMY_CASH_ISSUER) aliceVaultService = aliceServices.vaultService } @@ -250,7 +250,7 @@ class CommercialPaperTestsGeneric { bigCorpVaultService = bigCorpServices.vaultService databaseBigCorp.transaction { - bigCorpVault = bigCorpServices.fillWithSomeTestCash(13000.DOLLARS, issuerServices, atLeastThisManyStates = 1, atMostThisManyStates = 1, issuedBy = DUMMY_CASH_ISSUER) + bigCorpVault = VaultFiller(bigCorpServices).fillWithSomeTestCash(13000.DOLLARS, issuerServices, atLeastThisManyStates = 1, atMostThisManyStates = 1, issuedBy = 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 db35907d50..86f15f5bfa 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 @@ -19,7 +19,7 @@ import net.corda.node.services.vault.NodeVaultService import net.corda.node.utilities.CordaPersistence import net.corda.testing.* import net.corda.testing.contracts.DummyState -import net.corda.testing.contracts.fillWithSomeTestCash +import net.corda.testing.contracts.VaultFiller import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices import org.junit.After @@ -81,14 +81,15 @@ class CashTests { } // Create some cash. Any attempt to spend >$500 will require multiple issuers to be involved. - database.transaction { - ourServices.fillWithSomeTestCash(howMuch = 100.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1, + database.transaction { + val vaultFiller = VaultFiller(ourServices) + vaultFiller.fillWithSomeTestCash(howMuch = 100.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1, owner = ourIdentity, issuedBy = MEGA_CORP.ref(1), issuerServices = megaCorpServices) - ourServices.fillWithSomeTestCash(howMuch = 400.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1, + vaultFiller.fillWithSomeTestCash(howMuch = 400.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1, owner = ourIdentity, issuedBy = MEGA_CORP.ref(1), issuerServices = megaCorpServices) - ourServices.fillWithSomeTestCash(howMuch = 80.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1, + vaultFiller.fillWithSomeTestCash(howMuch = 80.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1, owner = ourIdentity, issuedBy = MINI_CORP.ref(1), issuerServices = miniCorpServices) - ourServices.fillWithSomeTestCash(howMuch = 80.SWISS_FRANCS, atLeastThisManyStates = 1, atMostThisManyStates = 1, + vaultFiller.fillWithSomeTestCash(howMuch = 80.SWISS_FRANCS, atLeastThisManyStates = 1, atMostThisManyStates = 1, owner = ourIdentity, issuedBy = MINI_CORP.ref(1), issuerServices = miniCorpServices) } database.transaction { 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 93c4ae5da4..4ade7ae074 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 @@ -55,7 +55,7 @@ import static org.assertj.core.api.Assertions.assertThat; public class VaultQueryJavaTests { @Rule public final SerializationEnvironmentRule testSerialization = new SerializationEnvironmentRule(); - private MockServices services; + private VaultFiller vaultFiller; private MockServices issuerServices; private VaultService vaultService; private CordaPersistence database; @@ -73,7 +73,8 @@ public class VaultQueryJavaTests { cordappPackages); issuerServices = new MockServices(cordappPackages, getDUMMY_CASH_ISSUER_NAME(), getDUMMY_CASH_ISSUER_KEY(), getBOC_KEY()); database = databaseAndServices.getFirst(); - services = databaseAndServices.getSecond(); + MockServices services = databaseAndServices.getSecond(); + vaultFiller = new VaultFiller(services); vaultService = services.getVaultService(); } @@ -93,7 +94,7 @@ public class VaultQueryJavaTests { @Test public void unconsumedLinearStates() throws VaultQueryException { database.transaction(tx -> { - VaultFiller.fillWithSomeTestLinearStates(services, 3); + vaultFiller.fillWithSomeTestLinearStates(3); return tx; }); database.transaction(tx -> { @@ -111,8 +112,8 @@ public class VaultQueryJavaTests { public void unconsumedStatesForStateRefsSortedByTxnId() { Vault issuedStates = database.transaction(tx -> { - VaultFiller.fillWithSomeTestLinearStates(services, 8); - return VaultFiller.fillWithSomeTestLinearStates(services, 2); + vaultFiller.fillWithSomeTestLinearStates(8); + return vaultFiller.fillWithSomeTestLinearStates(2); }); database.transaction(tx -> { Stream stateRefsStream = StreamSupport.stream(issuedStates.getStates().spliterator(), false).map(StateAndRef::getRef); @@ -137,7 +138,7 @@ public class VaultQueryJavaTests { public void consumedCashStates() { Amount amount = new Amount<>(100, Currency.getInstance("USD")); database.transaction(tx -> { - VaultFiller.fillWithSomeTestCash(services, + vaultFiller.fillWithSomeTestCash( new Amount(100, Currency.getInstance("USD")), issuerServices, TestConstants.getDUMMY_NOTARY(), @@ -149,7 +150,7 @@ public class VaultQueryJavaTests { return tx; }); database.transaction(tx -> { - VaultFiller.consumeCash(services, amount, getDUMMY_NOTARY()); + vaultFiller.consumeCash(amount, getDUMMY_NOTARY()); return tx; }); database.transaction(tx -> { @@ -170,17 +171,16 @@ public class VaultQueryJavaTests { @SuppressWarnings("unchecked") Triple, UniqueIdentifier, Vault> ids = database.transaction((DatabaseTransaction tx) -> { - Vault states = VaultFiller.fillWithSomeTestLinearStates(services, 10, null); + Vault states = vaultFiller.fillWithSomeTestLinearStates(10, null); StateAndRef linearState = states.getStates().iterator().next(); UniqueIdentifier uid = linearState.component1().getData().getLinearId(); - - Vault dealStates = VaultFiller.fillWithSomeTestDeals(services, dealIds); + Vault dealStates = vaultFiller.fillWithSomeTestDeals(dealIds); return new Triple(linearState, uid, dealStates); }); database.transaction(tx -> { // consume states - VaultFiller.consumeDeals(services, (List>) ids.getThird().getStates(), getDUMMY_NOTARY()); - VaultFiller.consumeLinearStates(services, Collections.singletonList(ids.getFirst()), getDUMMY_NOTARY()); + vaultFiller.consumeDeals((List>) ids.getThird().getStates(), getDUMMY_NOTARY()); + vaultFiller.consumeLinearStates(Collections.singletonList(ids.getFirst()), getDUMMY_NOTARY()); return tx; }); database.transaction(tx -> { @@ -219,11 +219,10 @@ public class VaultQueryJavaTests { Amount dollars100 = new Amount<>(100, Currency.getInstance("USD")); Amount dollars10 = new Amount<>(10, Currency.getInstance("USD")); Amount dollars1 = new Amount<>(1, Currency.getInstance("USD")); - - VaultFiller.fillWithSomeTestCash(services, pounds, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); - VaultFiller.fillWithSomeTestCash(services, dollars100, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); - VaultFiller.fillWithSomeTestCash(services, dollars10, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); - VaultFiller.fillWithSomeTestCash(services, dollars1, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(pounds, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars100, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars10, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars1, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); return tx; }); database.transaction(tx -> { @@ -260,7 +259,7 @@ public class VaultQueryJavaTests { @Test public void trackCashStates() { database.transaction(tx -> { - VaultFiller.fillWithSomeTestCash(services, + vaultFiller.fillWithSomeTestCash( new Amount<>(100, Currency.getInstance("USD")), issuerServices, TestConstants.getDUMMY_NOTARY(), @@ -294,10 +293,9 @@ public class VaultQueryJavaTests { List dealIds = Arrays.asList("123", "456", "789"); UniqueIdentifier uid = database.transaction(tx -> { - Vault states = VaultFiller.fillWithSomeTestLinearStates(services, 10, null); + Vault states = vaultFiller.fillWithSomeTestLinearStates(10, null); UniqueIdentifier _uid = states.getStates().iterator().next().component1().getData().getLinearId(); - - VaultFiller.fillWithSomeTestDeals(services, dealIds); + vaultFiller.fillWithSomeTestDeals(dealIds); return _uid; }); database.transaction(tx -> { @@ -341,13 +339,11 @@ public class VaultQueryJavaTests { Amount dollars300 = new Amount<>(300, Currency.getInstance("USD")); Amount pounds = new Amount<>(400, Currency.getInstance("GBP")); Amount swissfrancs = new Amount<>(500, Currency.getInstance("CHF")); - - VaultFiller.fillWithSomeTestCash(services, dollars100, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); - VaultFiller.fillWithSomeTestCash(services, dollars200, issuerServices, TestConstants.getDUMMY_NOTARY(), 2, 2, new Random(0L), null, getDUMMY_CASH_ISSUER()); - VaultFiller.fillWithSomeTestCash(services, dollars300, issuerServices, TestConstants.getDUMMY_NOTARY(), 3, 3, new Random(0L), null, getDUMMY_CASH_ISSUER()); - VaultFiller.fillWithSomeTestCash(services, pounds, issuerServices, TestConstants.getDUMMY_NOTARY(), 4, 4, new Random(0L), null, getDUMMY_CASH_ISSUER()); - VaultFiller.fillWithSomeTestCash(services, swissfrancs, issuerServices, TestConstants.getDUMMY_NOTARY(), 5, 5, new Random(0L), null, getDUMMY_CASH_ISSUER()); - + vaultFiller.fillWithSomeTestCash(dollars100, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars200, issuerServices, TestConstants.getDUMMY_NOTARY(), 2, 2, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars300, issuerServices, TestConstants.getDUMMY_NOTARY(), 3, 3, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(pounds, issuerServices, TestConstants.getDUMMY_NOTARY(), 4, 4, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(swissfrancs, issuerServices, TestConstants.getDUMMY_NOTARY(), 5, 5, new Random(0L), null, getDUMMY_CASH_ISSUER()); return tx; }); database.transaction(tx -> { @@ -389,13 +385,11 @@ public class VaultQueryJavaTests { Amount dollars300 = new Amount<>(300, Currency.getInstance("USD")); Amount pounds = new Amount<>(400, Currency.getInstance("GBP")); Amount swissfrancs = new Amount<>(500, Currency.getInstance("CHF")); - - VaultFiller.fillWithSomeTestCash(services, dollars100, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); - VaultFiller.fillWithSomeTestCash(services, dollars200, issuerServices, TestConstants.getDUMMY_NOTARY(), 2, 2, new Random(0L), null, getDUMMY_CASH_ISSUER()); - VaultFiller.fillWithSomeTestCash(services, dollars300, issuerServices, TestConstants.getDUMMY_NOTARY(), 3, 3, new Random(0L), null, getDUMMY_CASH_ISSUER()); - VaultFiller.fillWithSomeTestCash(services, pounds, issuerServices, TestConstants.getDUMMY_NOTARY(), 4, 4, new Random(0L), null, getDUMMY_CASH_ISSUER()); - VaultFiller.fillWithSomeTestCash(services, swissfrancs, issuerServices, TestConstants.getDUMMY_NOTARY(), 5, 5, new Random(0L), null, getDUMMY_CASH_ISSUER()); - + vaultFiller.fillWithSomeTestCash(dollars100, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars200, issuerServices, TestConstants.getDUMMY_NOTARY(), 2, 2, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars300, issuerServices, TestConstants.getDUMMY_NOTARY(), 3, 3, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(pounds, issuerServices, TestConstants.getDUMMY_NOTARY(), 4, 4, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(swissfrancs, issuerServices, TestConstants.getDUMMY_NOTARY(), 5, 5, new Random(0L), null, getDUMMY_CASH_ISSUER()); return tx; }); database.transaction(tx -> { @@ -452,12 +446,10 @@ public class VaultQueryJavaTests { Amount dollars200 = new Amount<>(200, Currency.getInstance("USD")); Amount pounds300 = new Amount<>(300, Currency.getInstance("GBP")); Amount pounds400 = new Amount<>(400, Currency.getInstance("GBP")); - - VaultFiller.fillWithSomeTestCash(services, dollars100, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); - VaultFiller.fillWithSomeTestCash(services, dollars200, issuerServices, TestConstants.getDUMMY_NOTARY(), 2, 2, new Random(0L), null, getBOC().ref(new OpaqueBytes("1".getBytes()))); - VaultFiller.fillWithSomeTestCash(services, pounds300, issuerServices, TestConstants.getDUMMY_NOTARY(), 3, 3, new Random(0L), null, getDUMMY_CASH_ISSUER()); - VaultFiller.fillWithSomeTestCash(services, pounds400, issuerServices, TestConstants.getDUMMY_NOTARY(), 4, 4, new Random(0L), null, getBOC().ref(new OpaqueBytes("1".getBytes()))); - + vaultFiller.fillWithSomeTestCash(dollars100, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars200, issuerServices, TestConstants.getDUMMY_NOTARY(), 2, 2, new Random(0L), null, getBOC().ref(new OpaqueBytes("1".getBytes()))); + vaultFiller.fillWithSomeTestCash(pounds300, issuerServices, TestConstants.getDUMMY_NOTARY(), 3, 3, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(pounds400, issuerServices, TestConstants.getDUMMY_NOTARY(), 4, 4, new Random(0L), null, getBOC().ref(new OpaqueBytes("1".getBytes()))); return tx; }); database.transaction(tx -> { 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 5b17b6b368..16b2947647 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -38,7 +38,7 @@ import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.persistence.checkpoints import net.corda.node.utilities.CordaPersistence import net.corda.testing.* -import net.corda.testing.contracts.fillWithSomeTestCash +import net.corda.testing.contracts.VaultFiller import net.corda.testing.node.* import org.assertj.core.api.Assertions.assertThat import org.junit.After @@ -106,7 +106,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { bobNode.internals.disableDBCloseOnStop() bobNode.database.transaction { - bobNode.services.fillWithSomeTestCash(2000.DOLLARS, bankNode.services, outputNotary = notary, + VaultFiller(bobNode.services).fillWithSomeTestCash(2000.DOLLARS, bankNode.services, outputNotary = notary, issuedBy = cashIssuer) } @@ -156,7 +156,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { bobNode.internals.disableDBCloseOnStop() val cashStates = bobNode.database.transaction { - bobNode.services.fillWithSomeTestCash(2000.DOLLARS, bankNode.services, notary, 3, 3, + VaultFiller(bobNode.services).fillWithSomeTestCash(2000.DOLLARS, bankNode.services, notary, 3, 3, issuedBy = issuer) } @@ -216,7 +216,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { val issuer = bank.ref(1, 2, 3) bobNode.database.transaction { - bobNode.services.fillWithSomeTestCash(2000.DOLLARS, bankNode.services, outputNotary = notary, + VaultFiller(bobNode.services).fillWithSomeTestCash(2000.DOLLARS, bankNode.services, outputNotary = notary, issuedBy = issuer) } val alicesFakePaper = aliceNode.database.transaction { 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 24d872b0f2..8a399b394b 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 @@ -33,10 +33,7 @@ import net.corda.node.services.vault.VaultSchemaV1 import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.configureDatabase import net.corda.testing.* -import net.corda.testing.contracts.consumeCash -import net.corda.testing.contracts.fillWithSomeTestCash -import net.corda.testing.contracts.fillWithSomeTestDeals -import net.corda.testing.contracts.fillWithSomeTestLinearStates +import net.corda.testing.contracts.* import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.schemas.DummyLinearStateSchemaV1 @@ -57,6 +54,7 @@ class HibernateConfigurationTest { @JvmField val testSerialization = SerializationEnvironmentRule() lateinit var services: MockServices + private lateinit var vaultFiller: VaultFiller lateinit var bankServices: MockServices lateinit var issuerServices: MockServices lateinit var notaryServices: MockServices @@ -108,6 +106,7 @@ class HibernateConfigurationTest { override fun jdbcSession() = database.createSession() } + vaultFiller = VaultFiller(services) hibernatePersister = services.hibernatePersister } @@ -117,7 +116,7 @@ class HibernateConfigurationTest { database.transaction { val numStates = 10 - cashStates = services.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, numStates, numStates, Random(0L), issuedBy = issuer.ref(1)) + cashStates = vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, numStates, numStates, Random(0L), issuedBy = issuer.ref(1)) .states.toList() } @@ -148,7 +147,7 @@ class HibernateConfigurationTest { @Test fun `consumed states`() { database.transaction { - services.consumeCash(50.DOLLARS, notary = notary) + vaultFiller.consumeCash(50.DOLLARS, notary = notary) } // structure query @@ -160,7 +159,7 @@ class HibernateConfigurationTest { // execute query val queryResults = entityManager.createQuery(criteriaQuery).resultList val coins = queryResults.map { - (services.loadState(toStateRef(it.stateRef!!)) as TransactionState).data + services.loadState(toStateRef(it.stateRef!!)).data }.sumCash() assertThat(coins.toDecimal() >= BigDecimal("50.00")) } @@ -169,8 +168,8 @@ class HibernateConfigurationTest { fun `select by composite primary key`() { val issuedStates = database.transaction { - services.fillWithSomeTestLinearStates(8) - services.fillWithSomeTestLinearStates(2) + vaultFiller.fillWithSomeTestLinearStates(8) + vaultFiller.fillWithSomeTestLinearStates(2) } val persistentStateRefs = issuedStates.states.map { PersistentStateRef(it.ref) }.toList() @@ -194,8 +193,8 @@ class HibernateConfigurationTest { fun `distinct contract types`() { database.transaction { // add 2 more contract types - services.fillWithSomeTestLinearStates(10) - services.fillWithSomeTestDeals(listOf("123", "456", "789")) + vaultFiller.fillWithSomeTestLinearStates(10) + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) } // structure query @@ -229,11 +228,11 @@ class HibernateConfigurationTest { fun `with sorting by state ref desc and asc`() { // generate additional state ref indexes database.transaction { - services.consumeCash(1.DOLLARS, notary = notary) - services.consumeCash(2.DOLLARS, notary = notary) - services.consumeCash(3.DOLLARS, notary = notary) - services.consumeCash(4.DOLLARS, notary = notary) - services.consumeCash(5.DOLLARS, notary = notary) + vaultFiller.consumeCash(1.DOLLARS, notary = notary) + vaultFiller.consumeCash(2.DOLLARS, notary = notary) + vaultFiller.consumeCash(3.DOLLARS, notary = notary) + vaultFiller.consumeCash(4.DOLLARS, notary = notary) + vaultFiller.consumeCash(5.DOLLARS, notary = notary) } // structure query @@ -259,11 +258,11 @@ class HibernateConfigurationTest { fun `with sorting by state ref index and txId desc and asc`() { // generate additional state ref indexes database.transaction { - services.consumeCash(1.DOLLARS, notary = notary) - services.consumeCash(2.DOLLARS, notary = notary) - services.consumeCash(3.DOLLARS, notary = notary) - services.consumeCash(4.DOLLARS, notary = notary) - services.consumeCash(5.DOLLARS, notary = notary) + vaultFiller.consumeCash(1.DOLLARS, notary = notary) + vaultFiller.consumeCash(2.DOLLARS, notary = notary) + vaultFiller.consumeCash(3.DOLLARS, notary = notary) + vaultFiller.consumeCash(4.DOLLARS, notary = notary) + vaultFiller.consumeCash(5.DOLLARS, notary = notary) } // structure query @@ -290,7 +289,7 @@ class HibernateConfigurationTest { fun `with pagination`() { // add 100 additional cash entries database.transaction { - services.fillWithSomeTestCash(1000.POUNDS, issuerServices, notary, 100, 100, Random(0L), issuedBy = issuer.ref(1)) + vaultFiller.fillWithSomeTestCash(1000.POUNDS, issuerServices, notary, 100, 100, Random(0L), issuedBy = issuer.ref(1)) } // structure query @@ -321,7 +320,7 @@ class HibernateConfigurationTest { @Test fun `select by composite primary key on LinearStates`() { database.transaction { - services.fillWithSomeTestLinearStates(10) + vaultFiller.fillWithSomeTestLinearStates(10) } // structure query @@ -372,8 +371,7 @@ class HibernateConfigurationTest { @Test fun `select and join by composite primary key on CashStates`() { database.transaction { - services.fillWithSomeTestLinearStates(5) - + vaultFiller.fillWithSomeTestLinearStates(5) // structure query val criteriaQuery = criteriaBuilder.createQuery(VaultSchemaV1.VaultStates::class.java) val vaultStates = criteriaQuery.from(VaultSchemaV1.VaultStates::class.java) @@ -391,12 +389,11 @@ class HibernateConfigurationTest { @Test fun `calculate cash balances`() { database.transaction { - - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, 10, issuer.ref(1)) // +$100 = $200 - services.fillWithSomeTestCash(50.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // £50 = £50 - services.fillWithSomeTestCash(25.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // +£25 = £175 - services.fillWithSomeTestCash(500.SWISS_FRANCS, issuerServices, notary, 10, issuer.ref(1)) // CHF500 = CHF500 - services.fillWithSomeTestCash(250.SWISS_FRANCS, issuerServices, notary, 5, issuer.ref(1)) // +CHF250 = CHF750 + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, 10, issuer.ref(1)) // +$100 = $200 + vaultFiller.fillWithSomeTestCash(50.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // £50 = £50 + vaultFiller.fillWithSomeTestCash(25.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // +£25 = £175 + vaultFiller.fillWithSomeTestCash(500.SWISS_FRANCS, issuerServices, notary, 10, issuer.ref(1)) // CHF500 = CHF500 + vaultFiller.fillWithSomeTestCash(250.SWISS_FRANCS, issuerServices, notary, 5, issuer.ref(1)) // +CHF250 = CHF750 } // structure query @@ -425,8 +422,8 @@ class HibernateConfigurationTest { @Test fun `calculate cash balance for single currency`() { database.transaction { - services.fillWithSomeTestCash(50.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // £50 = £50 - services.fillWithSomeTestCash(25.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // +£25 = £175 + vaultFiller.fillWithSomeTestCash(50.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // £50 = £50 + vaultFiller.fillWithSomeTestCash(25.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // +£25 = £175 } // structure query @@ -456,9 +453,9 @@ class HibernateConfigurationTest { fun `calculate and order by cash balance for owner and currency`() { database.transaction { val bank = bankServices.myInfo.legalIdentities.single() - services.fillWithSomeTestCash(200.DOLLARS, bankServices, notary, 2, bank.ref(1)) - services.fillWithSomeTestCash(300.POUNDS, issuerServices, notary, 3, issuer.ref(1)) - services.fillWithSomeTestCash(400.POUNDS, bankServices, notary, 4, bank.ref(2)) + vaultFiller.fillWithSomeTestCash(200.DOLLARS, bankServices, notary, 2, bank.ref(1)) + vaultFiller.fillWithSomeTestCash(300.POUNDS, issuerServices, notary, 3, issuer.ref(1)) + vaultFiller.fillWithSomeTestCash(400.POUNDS, bankServices, notary, 4, bank.ref(2)) } // structure query @@ -518,8 +515,7 @@ class HibernateConfigurationTest { @Test fun `select by composite primary key on CashStates in V2`() { database.transaction { - services.fillWithSomeTestLinearStates(5) - + vaultFiller.fillWithSomeTestLinearStates(5) // persist cash states explicitly with V2 schema cashStates.forEach { val cashState = it.state.data @@ -554,9 +550,9 @@ class HibernateConfigurationTest { @Test fun `select by composite primary between VaultStates, VaultLinearStates and DummyLinearStates`() { database.transaction { - services.fillWithSomeTestLinearStates(8) - services.fillWithSomeTestDeals(listOf("123", "456", "789")) - services.fillWithSomeTestLinearStates(2) + vaultFiller.fillWithSomeTestLinearStates(8) + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) + vaultFiller.fillWithSomeTestLinearStates(2) } val sessionFactory = sessionFactoryForSchemas(VaultSchemaV1, DummyLinearStateSchemaV1) val criteriaBuilder = sessionFactory.criteriaBuilder @@ -585,9 +581,9 @@ class HibernateConfigurationTest { @Test fun `three way join by composite primary between VaultStates, VaultLinearStates and DummyLinearStates`() { database.transaction { - services.fillWithSomeTestLinearStates(8) - services.fillWithSomeTestDeals(listOf("123", "456", "789")) - services.fillWithSomeTestLinearStates(2) + vaultFiller.fillWithSomeTestLinearStates(8) + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) + vaultFiller.fillWithSomeTestLinearStates(2) } val sessionFactory = sessionFactoryForSchemas(VaultSchemaV1, DummyLinearStateSchemaV2) val criteriaBuilder = sessionFactory.criteriaBuilder @@ -644,10 +640,9 @@ class HibernateConfigurationTest { val dummyFungibleState = DummyFungibleContract.State(cashState.amount, cashState.owner) hibernatePersister.persistStateWithSchema(dummyFungibleState, it.ref, SampleCashSchemaV3) } - - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, 2, 2, Random(0L), + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, 2, 2, Random(0L), issuedBy = issuer.ref(1), owner = ALICE) - val cashStates = services.fillWithSomeTestCash(100.DOLLARS, services, notary, 2, identity.ref(0)).states + val cashStates = vaultFiller.fillWithSomeTestCash(100.DOLLARS, services, notary, 2, identity.ref(0)).states // persist additional cash states explicitly with V3 schema cashStates.forEach { val cashState = it.state.data @@ -723,8 +718,7 @@ class HibernateConfigurationTest { val dummyFungibleState = DummyFungibleContract.State(cashState.amount, cashState.owner) hibernatePersister.persistStateWithSchema(dummyFungibleState, it.ref, SampleCashSchemaV3) } - - val moreCash = services.fillWithSomeTestCash(100.DOLLARS, services, notary, 2, 2, Random(0L), + val moreCash = vaultFiller.fillWithSomeTestCash(100.DOLLARS, services, notary, 2, 2, Random(0L), issuedBy = identity.ref(0), owner = identity).states // persist additional cash states explicitly with V3 schema moreCash.forEach { @@ -732,8 +726,7 @@ class HibernateConfigurationTest { val dummyFungibleState = DummyFungibleContract.State(cashState.amount, cashState.owner) hibernatePersister.persistStateWithSchema(dummyFungibleState, it.ref, SampleCashSchemaV3) } - - val cashStates = services.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, 2, 2, Random(0L), owner = ALICE, issuedBy = issuer.ref(1)).states + val cashStates = vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, 2, 2, Random(0L), owner = ALICE, issuedBy = issuer.ref(1)).states // persist additional cash states explicitly with V3 schema cashStates.forEach { val cashState = it.state.data @@ -777,9 +770,9 @@ class HibernateConfigurationTest { fun `with sorting on attribute from common table`() { database.transaction { - services.fillWithSomeTestLinearStates(1, externalId = "111") - services.fillWithSomeTestLinearStates(2, externalId = "222") - services.fillWithSomeTestLinearStates(3, externalId = "333") + vaultFiller.fillWithSomeTestLinearStates(1, externalId = "111") + vaultFiller.fillWithSomeTestLinearStates(2, externalId = "222") + vaultFiller.fillWithSomeTestLinearStates(3, externalId = "333") } val sessionFactory = sessionFactoryForSchemas(VaultSchemaV1, DummyLinearStateSchemaV2) val criteriaBuilder = sessionFactory.criteriaBuilder @@ -829,9 +822,9 @@ class HibernateConfigurationTest { fun `with sorting on attribute from custom table`() { database.transaction { - services.fillWithSomeTestLinearStates(1, externalId = "111") - services.fillWithSomeTestLinearStates(2, externalId = "222") - services.fillWithSomeTestLinearStates(3, externalId = "333") + vaultFiller.fillWithSomeTestLinearStates(1, externalId = "111") + vaultFiller.fillWithSomeTestLinearStates(2, externalId = "222") + vaultFiller.fillWithSomeTestLinearStates(3, externalId = "333") } val sessionFactory = sessionFactoryForSchemas(VaultSchemaV1, DummyLinearStateSchemaV1) val criteriaBuilder = sessionFactory.criteriaBuilder 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 123335a1de..c24f563b85 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 @@ -36,7 +36,7 @@ import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.utils.sumCash import net.corda.node.utilities.CordaPersistence import net.corda.testing.* -import net.corda.testing.contracts.fillWithSomeTestCash +import net.corda.testing.contracts.VaultFiller import net.corda.testing.node.MockServices import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatExceptionOfType @@ -62,6 +62,7 @@ class NodeVaultServiceTest { @JvmField val testSerialization = SerializationEnvironmentRule() private lateinit var services: MockServices + private lateinit var vaultFiller: VaultFiller private lateinit var identity: PartyAndCertificate private lateinit var issuerServices: MockServices private lateinit var bocServices: MockServices @@ -74,6 +75,7 @@ class NodeVaultServiceTest { val databaseAndServices = MockServices.makeTestDatabaseAndMockServices(cordappPackages = cordappPackages) database = databaseAndServices.first services = databaseAndServices.second + vaultFiller = VaultFiller(services) // This is safe because MockServices only ever have a single identity identity = services.myInfo.singleIdentityAndCert() issuerServices = MockServices(cordappPackages, DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY) @@ -111,7 +113,7 @@ class NodeVaultServiceTest { @Test fun `states not local to instance`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) } database.transaction { val w1 = vaultService.queryBy().states @@ -136,7 +138,7 @@ class NodeVaultServiceTest { @Test fun `states for refs`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) } database.transaction { val w1 = vaultService.queryBy().states @@ -150,7 +152,7 @@ class NodeVaultServiceTest { @Test fun `states soft locking reserve and release`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) } database.transaction { @@ -203,7 +205,7 @@ class NodeVaultServiceTest { val vaultStates = database.transaction { assertEquals(0.DOLLARS, services.getCashBalance(USD)) - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) } val stateRefsToSoftLock = (vaultStates.states.map { it.ref }).toNonEmptySet() println("State Refs:: $stateRefsToSoftLock") @@ -258,7 +260,7 @@ class NodeVaultServiceTest { val vaultStates = database.transaction { assertEquals(0.DOLLARS, services.getCashBalance(USD)) - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) } val stateRefsToSoftLock = vaultStates.states.map { it.ref } println("State Refs:: $stateRefsToSoftLock") @@ -286,7 +288,7 @@ class NodeVaultServiceTest { val vaultStates = database.transaction { assertEquals(0.DOLLARS, services.getCashBalance(USD)) - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) } val stateRefsToSoftLock = (vaultStates.states.map { it.ref }).toNonEmptySet() println("State Refs:: $stateRefsToSoftLock") @@ -313,7 +315,7 @@ class NodeVaultServiceTest { val vaultStates = database.transaction { assertEquals(0.DOLLARS, services.getCashBalance(USD)) - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) } val stateRefsToSoftLock = vaultStates.states.map { it.ref } println("State Refs:: $stateRefsToSoftLock") @@ -334,7 +336,7 @@ class NodeVaultServiceTest { @Test fun `unconsumedStatesForSpending exact amount`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L)) } database.transaction { @@ -353,8 +355,8 @@ class NodeVaultServiceTest { @Test fun `unconsumedStatesForSpending from two issuer parties`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = DUMMY_CASH_ISSUER) - services.fillWithSomeTestCash(100.DOLLARS, bocServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(1)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, bocServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(1)) } database.transaction { val spendableStatesUSD = vaultService.unconsumedCashStatesForSpending(200.DOLLARS, @@ -370,10 +372,10 @@ class NodeVaultServiceTest { @Test fun `unconsumedStatesForSpending from specific issuer party and refs`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = DUMMY_CASH_ISSUER) - services.fillWithSomeTestCash(100.DOLLARS, bocServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(1)) - services.fillWithSomeTestCash(100.DOLLARS, bocServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(2)) - services.fillWithSomeTestCash(100.DOLLARS, bocServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(3)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, bocServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(1)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, bocServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(2)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, bocServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(3)) } database.transaction { val unconsumedStates = vaultService.queryBy().states @@ -392,7 +394,7 @@ class NodeVaultServiceTest { @Test fun `unconsumedStatesForSpending insufficient amount`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L)) } database.transaction { val unconsumedStates = vaultService.queryBy().states @@ -409,7 +411,7 @@ class NodeVaultServiceTest { @Test fun `unconsumedStatesForSpending small amount`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L)) } database.transaction { val unconsumedStates = vaultService.queryBy().states @@ -427,9 +429,9 @@ class NodeVaultServiceTest { @Test fun `states soft locking query granularity`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L)) - services.fillWithSomeTestCash(100.POUNDS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L)) } database.transaction { var unlockedStates = 30 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 70629c705c..20ea02690f 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 @@ -63,6 +63,7 @@ class VaultQueryTests { CashSchemaV1::class.packageName, DummyLinearStateSchemaV1::class.packageName) private lateinit var services: MockServices + private lateinit var vaultFiller: VaultFiller private lateinit var notaryServices: MockServices private val vaultService: VaultService get() = services.vaultService private lateinit var identitySvc: IdentityService @@ -80,6 +81,7 @@ class VaultQueryTests { cordappPackages = cordappPackages) database = databaseAndServices.first services = databaseAndServices.second + vaultFiller = VaultFiller(services) notaryServices = MockServices(cordappPackages, DUMMY_NOTARY.name, DUMMY_NOTARY_KEY, DUMMY_CASH_ISSUER_KEY, BOC_KEY, MEGA_CORP_KEY) identitySvc = services.identityService // Register all of the identities we're going to use @@ -108,19 +110,19 @@ class VaultQueryTests { private fun setUpDb(_database: CordaPersistence, delay: Long = 0) { _database.transaction { // create new states - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 10, 10, Random(0L)) - val linearStatesXYZ = services.fillWithSomeTestLinearStates(1, "XYZ") - val linearStatesJKL = services.fillWithSomeTestLinearStates(2, "JKL") - services.fillWithSomeTestLinearStates(3, "ABC") - val dealStates = services.fillWithSomeTestDeals(listOf("123", "456", "789")) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 10, 10, Random(0L)) + val linearStatesXYZ = vaultFiller.fillWithSomeTestLinearStates(1, "XYZ") + val linearStatesJKL = vaultFiller.fillWithSomeTestLinearStates(2, "JKL") + vaultFiller.fillWithSomeTestLinearStates(3, "ABC") + val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) // Total unconsumed states = 10 + 1 + 2 + 3 + 3 = 19 sleep(delay) // consume some states - services.consumeLinearStates(linearStatesXYZ.states.toList(), DUMMY_NOTARY) - services.consumeLinearStates(linearStatesJKL.states.toList(), DUMMY_NOTARY) - services.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }, DUMMY_NOTARY) - services.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) + vaultFiller.consumeLinearStates(linearStatesXYZ.states.toList(), DUMMY_NOTARY) + vaultFiller.consumeLinearStates(linearStatesJKL.states.toList(), DUMMY_NOTARY) + vaultFiller.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }, DUMMY_NOTARY) + vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) // Total unconsumed states = 4 + 3 + 2 + 1 (new cash change) = 10 // Total consumed states = 6 + 1 + 2 + 1 = 10 } @@ -145,10 +147,9 @@ class VaultQueryTests { @Test fun `unconsumed states simple`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestLinearStates(10) - services.fillWithSomeTestDeals(listOf("123", "456", "789")) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestLinearStates(10) + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) // DOCSTART VaultQueryExample1 val result = vaultService.queryBy() @@ -171,10 +172,9 @@ class VaultQueryTests { @Test fun `unconsumed states verbose`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestLinearStates(10) - services.fillWithSomeTestDeals(listOf("123", "456", "789")) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestLinearStates(10) + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) val criteria = VaultQueryCriteria() // default is UNCONSUMED val result = vaultService.queryBy(criteria) @@ -186,19 +186,16 @@ class VaultQueryTests { @Test fun `unconsumed states with count`() { database.transaction { - services.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val paging = PageSpecification(DEFAULT_PAGE_NUM, 10) val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) val resultsBeforeConsume = vaultService.queryBy(criteria, paging) assertThat(resultsBeforeConsume.states).hasSize(4) assertThat(resultsBeforeConsume.totalStatesAvailable).isEqualTo(4) - - services.consumeCash(75.DOLLARS, notary = DUMMY_NOTARY) - + vaultFiller.consumeCash(75.DOLLARS, notary = DUMMY_NOTARY) val consumedCriteria = VaultQueryCriteria(status = Vault.StateStatus.UNCONSUMED) val resultsAfterConsume = vaultService.queryBy(consumedCriteria, paging) assertThat(resultsAfterConsume.states).hasSize(1) @@ -209,10 +206,9 @@ class VaultQueryTests { @Test fun `unconsumed cash states simple`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestLinearStates(10) - services.fillWithSomeTestDeals(listOf("123", "456", "789")) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestLinearStates(10) + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) val result = vaultService.queryBy() assertThat(result.states).hasSize(3) @@ -223,10 +219,9 @@ class VaultQueryTests { @Test fun `unconsumed cash states verbose`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestLinearStates(10) - services.fillWithSomeTestDeals(listOf("123", "456", "789")) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestLinearStates(10) + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) val criteria = VaultQueryCriteria() // default is UNCONSUMED val result = vaultService.queryBy(criteria) @@ -239,12 +234,12 @@ class VaultQueryTests { fun `unconsumed cash states sorted by state ref`() { val stateRefs: MutableList = mutableListOf() database.transaction { - val issuedStates = services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 10, 10, Random(0L)) + val issuedStates = vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 10, 10, Random(0L)) val issuedStateRefs = issuedStates.states.map { it.ref }.toList() stateRefs.addAll(issuedStateRefs) } database.transaction { - val spentStates = services.consumeCash(25.DOLLARS, notary = DUMMY_NOTARY) + val spentStates = vaultFiller.consumeCash(25.DOLLARS, notary = DUMMY_NOTARY) val consumedStateRefs = spentStates.consumed.map { it.ref }.toList() val producedStateRefs = spentStates.produced.map { it.ref }.toList() stateRefs.addAll(consumedStateRefs.plus(producedStateRefs)) @@ -270,12 +265,11 @@ class VaultQueryTests { fun `unconsumed cash states sorted by state ref txnId and index`() { val consumed = mutableSetOf() database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 10, 10, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 10, 10, Random(0L)) } database.transaction { - services.consumeCash(10.DOLLARS, notary = DUMMY_NOTARY).consumed.forEach { consumed += it.ref.txhash } - services.consumeCash(10.DOLLARS, notary = DUMMY_NOTARY).consumed.forEach { consumed += it.ref.txhash } - + vaultFiller.consumeCash(10.DOLLARS, notary = DUMMY_NOTARY).consumed.forEach { consumed += it.ref.txhash } + vaultFiller.consumeCash(10.DOLLARS, notary = DUMMY_NOTARY).consumed.forEach { consumed += it.ref.txhash } val sortAttributeTxnId = SortAttribute.Standard(Sort.CommonStateAttribute.STATE_REF_TXN_ID) val sortAttributeIndex = SortAttribute.Standard(Sort.CommonStateAttribute.STATE_REF_INDEX) val sortBy = Sort(setOf(Sort.SortColumn(sortAttributeTxnId, Sort.Direction.ASC), @@ -296,8 +290,8 @@ class VaultQueryTests { @Test fun `unconsumed states for state refs`() { database.transaction { - services.fillWithSomeTestLinearStates(8) - val issuedStates = services.fillWithSomeTestLinearStates(2) + vaultFiller.fillWithSomeTestLinearStates(8) + val issuedStates = vaultFiller.fillWithSomeTestLinearStates(2) val stateRefs = issuedStates.states.map { it.ref }.toList() // DOCSTART VaultQueryExample2 @@ -317,10 +311,9 @@ class VaultQueryTests { @Test fun `unconsumed states for contract state types`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestLinearStates(10) - services.fillWithSomeTestDeals(listOf("123", "456", "789")) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestLinearStates(10) + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) // default State.Status is UNCONSUMED // DOCSTART VaultQueryExample3 val criteria = VaultQueryCriteria(contractStateTypes = setOf(Cash.State::class.java, DealState::class.java)) @@ -333,15 +326,13 @@ class VaultQueryTests { @Test fun `consumed states`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - val linearStates = services.fillWithSomeTestLinearStates(2, "TEST") // create 2 states with same externalId - services.fillWithSomeTestLinearStates(8) - val dealStates = services.fillWithSomeTestDeals(listOf("123", "456", "789")) - - services.consumeLinearStates(linearStates.states.toList(), DUMMY_NOTARY) - services.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }, DUMMY_NOTARY) - services.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + val linearStates = vaultFiller.fillWithSomeTestLinearStates(2, "TEST") // create 2 states with same externalId + vaultFiller.fillWithSomeTestLinearStates(8) + val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) + vaultFiller.consumeLinearStates(linearStates.states.toList(), DUMMY_NOTARY) + vaultFiller.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }, DUMMY_NOTARY) + vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) val criteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED) val results = vaultService.queryBy(criteria) assertThat(results.states).hasSize(5) @@ -351,19 +342,16 @@ class VaultQueryTests { @Test fun `consumed states with count`() { database.transaction { - services.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val paging = PageSpecification(DEFAULT_PAGE_NUM, 10) val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) val resultsBeforeConsume = vaultService.queryBy(criteria, paging) assertThat(resultsBeforeConsume.states).hasSize(4) assertThat(resultsBeforeConsume.totalStatesAvailable).isEqualTo(4) - - services.consumeCash(75.DOLLARS, notary = DUMMY_NOTARY) - + vaultFiller.consumeCash(75.DOLLARS, notary = DUMMY_NOTARY) val consumedCriteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED) val resultsAfterConsume = vaultService.queryBy(consumedCriteria, paging) assertThat(resultsAfterConsume.states).hasSize(3) @@ -374,15 +362,13 @@ class VaultQueryTests { @Test fun `all states`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - val linearStates = services.fillWithSomeTestLinearStates(2, "TEST") // create 2 results with same UID - services.fillWithSomeTestLinearStates(8) - val dealStates = services.fillWithSomeTestDeals(listOf("123", "456", "789")) - - services.consumeLinearStates(linearStates.states.toList(), DUMMY_NOTARY) - services.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }, DUMMY_NOTARY) - services.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) // generates a new change state! - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + val linearStates = vaultFiller.fillWithSomeTestLinearStates(2, "TEST") // create 2 results with same UID + vaultFiller.fillWithSomeTestLinearStates(8) + val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) + vaultFiller.consumeLinearStates(linearStates.states.toList(), DUMMY_NOTARY) + vaultFiller.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }, DUMMY_NOTARY) + vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) // generates a new change state! val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) val results = vaultService.queryBy(criteria) assertThat(results.states).hasSize(17) @@ -392,17 +378,14 @@ class VaultQueryTests { @Test fun `all states with count`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) val paging = PageSpecification(DEFAULT_PAGE_NUM, 10) val resultsBeforeConsume = vaultService.queryBy(criteria, paging) assertThat(resultsBeforeConsume.states).hasSize(1) assertThat(resultsBeforeConsume.totalStatesAvailable).isEqualTo(1) - - services.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) // consumed 100 (spent), produced 50 (change) - + vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) // consumed 100 (spent), produced 50 (change) val resultsAfterConsume = vaultService.queryBy(criteria, paging) assertThat(resultsAfterConsume.states).hasSize(2) assertThat(resultsAfterConsume.totalStatesAvailable).isEqualTo(2) @@ -412,10 +395,9 @@ class VaultQueryTests { @Test fun `unconsumed states by notary`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestLinearStates(10) - services.fillWithSomeTestDeals(listOf("123", "456", "789")) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestLinearStates(10) + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) // DOCSTART VaultQueryExample4 val criteria = VaultQueryCriteria(notary = listOf(CASH_NOTARY)) val results = vaultService.queryBy(criteria) @@ -428,11 +410,9 @@ class VaultQueryTests { fun `unconsumed linear states for single participant`() { database.transaction { identitySvc.verifyAndRegisterIdentity(BIG_CORP_IDENTITY) - - services.fillWithSomeTestLinearStates(2, "TEST", participants = listOf(MEGA_CORP, MINI_CORP)) - services.fillWithSomeTestDeals(listOf("456"), participants = listOf(MEGA_CORP, BIG_CORP)) - services.fillWithSomeTestDeals(listOf("123", "789"), participants = listOf(BIG_CORP)) - + vaultFiller.fillWithSomeTestLinearStates(2, "TEST", participants = listOf(MEGA_CORP, MINI_CORP)) + vaultFiller.fillWithSomeTestDeals(listOf("456"), participants = listOf(MEGA_CORP, BIG_CORP)) + vaultFiller.fillWithSomeTestDeals(listOf("123", "789"), participants = listOf(BIG_CORP)) val criteria = LinearStateQueryCriteria(participants = listOf(BIG_CORP)) val results = vaultService.queryBy(criteria) assertThat(results.states).hasSize(3) @@ -443,11 +423,9 @@ class VaultQueryTests { fun `unconsumed linear states for two participants`() { database.transaction { identitySvc.verifyAndRegisterIdentity(BIG_CORP_IDENTITY) - - services.fillWithSomeTestLinearStates(2, "TEST", participants = listOf(MEGA_CORP, MINI_CORP)) - services.fillWithSomeTestDeals(listOf("456"), participants = listOf(MEGA_CORP, BIG_CORP)) - services.fillWithSomeTestDeals(listOf("123", "789"), participants = listOf(MEGA_CORP)) - + vaultFiller.fillWithSomeTestLinearStates(2, "TEST", participants = listOf(MEGA_CORP, MINI_CORP)) + vaultFiller.fillWithSomeTestDeals(listOf("456"), participants = listOf(MEGA_CORP, BIG_CORP)) + vaultFiller.fillWithSomeTestDeals(listOf("123", "789"), participants = listOf(MEGA_CORP)) // DOCSTART VaultQueryExample5 val criteria = LinearStateQueryCriteria(participants = listOf(BIG_CORP, MINI_CORP)) val results = vaultService.queryBy(criteria) @@ -460,7 +438,7 @@ class VaultQueryTests { @Test fun `unconsumed states with soft locking`() { database.transaction { - val issuedStates = services.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 10, 10, Random(0L)).states.toList() + val issuedStates = vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 10, 10, Random(0L)).states.toList() vaultService.softLockReserve(UUID.randomUUID(), NonEmptySet.of(issuedStates[1].ref, issuedStates[2].ref, issuedStates[3].ref)) val lockId1 = UUID.randomUUID() vaultService.softLockReserve(lockId1, NonEmptySet.of(issuedStates[4].ref, issuedStates[5].ref)) @@ -503,10 +481,9 @@ class VaultQueryTests { @Test fun `logical operator EQUAL`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.equal(GBP.currencyCode) } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -517,10 +494,9 @@ class VaultQueryTests { @Test fun `logical operator NOT EQUAL`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.notEqual(GBP.currencyCode) } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -531,10 +507,9 @@ class VaultQueryTests { @Test fun `logical operator GREATER_THAN`() { database.transaction { - services.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(10.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(10.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val logicalExpression = builder { CashSchemaV1.PersistentCashState::pennies.greaterThan(1000L) } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -545,10 +520,9 @@ class VaultQueryTests { @Test fun `logical operator GREATER_THAN_OR_EQUAL`() { database.transaction { - services.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(10.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(10.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val logicalExpression = builder { CashSchemaV1.PersistentCashState::pennies.greaterThanOrEqual(1000L) } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -559,10 +533,9 @@ class VaultQueryTests { @Test fun `logical operator LESS_THAN`() { database.transaction { - services.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(10.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(10.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val logicalExpression = builder { CashSchemaV1.PersistentCashState::pennies.lessThan(1000L) } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -573,10 +546,9 @@ class VaultQueryTests { @Test fun `logical operator LESS_THAN_OR_EQUAL`() { database.transaction { - services.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(10.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(10.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val logicalExpression = builder { CashSchemaV1.PersistentCashState::pennies.lessThanOrEqual(1000L) } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -587,10 +559,9 @@ class VaultQueryTests { @Test fun `logical operator BETWEEN`() { database.transaction { - services.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(10.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(10.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val logicalExpression = builder { CashSchemaV1.PersistentCashState::pennies.between(500L, 1500L) } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -601,10 +572,9 @@ class VaultQueryTests { @Test fun `logical operator IN`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val currencies = listOf(CHF.currencyCode, GBP.currencyCode) val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.`in`(currencies) } val criteria = VaultCustomQueryCriteria(logicalExpression) @@ -616,10 +586,9 @@ class VaultQueryTests { @Test fun `logical operator NOT IN`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val currencies = listOf(CHF.currencyCode, GBP.currencyCode) val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.notIn(currencies) } val criteria = VaultCustomQueryCriteria(logicalExpression) @@ -631,10 +600,9 @@ class VaultQueryTests { @Test fun `logical operator LIKE`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.like("%BP") } // GPB val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -645,10 +613,9 @@ class VaultQueryTests { @Test fun `logical operator NOT LIKE`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.notLike("%BP") } // GPB val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -659,10 +626,9 @@ class VaultQueryTests { @Test fun `logical operator IS_NULL`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val logicalExpression = builder { CashSchemaV1.PersistentCashState::issuerPartyHash.isNull() } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -673,10 +639,9 @@ class VaultQueryTests { @Test fun `logical operator NOT_NULL`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val logicalExpression = builder { CashSchemaV1.PersistentCashState::issuerPartyHash.notNull() } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -687,12 +652,11 @@ class VaultQueryTests { @Test fun `aggregate functions without group clause`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(200.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L)) - services.fillWithSomeTestCash(300.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestCash(400.POUNDS, notaryServices, DUMMY_NOTARY, 4, 4, Random(0L)) - services.fillWithSomeTestCash(500.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(200.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L)) + vaultFiller.fillWithSomeTestCash(300.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(400.POUNDS, notaryServices, DUMMY_NOTARY, 4, 4, Random(0L)) + vaultFiller.fillWithSomeTestCash(500.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) // DOCSTART VaultQueryExample21 val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum() } val sumCriteria = VaultCustomQueryCriteria(sum) @@ -728,12 +692,11 @@ class VaultQueryTests { @Test fun `aggregate functions with single group clause`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(200.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L)) - services.fillWithSomeTestCash(300.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestCash(400.POUNDS, notaryServices, DUMMY_NOTARY, 4, 4, Random(0L)) - services.fillWithSomeTestCash(500.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(200.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L)) + vaultFiller.fillWithSomeTestCash(300.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(400.POUNDS, notaryServices, DUMMY_NOTARY, 4, 4, Random(0L)) + vaultFiller.fillWithSomeTestCash(500.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) // DOCSTART VaultQueryExample22 val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) } val sumCriteria = VaultCustomQueryCriteria(sum) @@ -779,12 +742,10 @@ class VaultQueryTests { fun `aggregate functions sum by issuer and currency and sort by aggregate sum`() { database.transaction { identitySvc.verifyAndRegisterIdentity(BOC_IDENTITY) - - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = DUMMY_CASH_ISSUER) - services.fillWithSomeTestCash(200.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L), issuedBy = BOC.ref(1)) - services.fillWithSomeTestCash(300.POUNDS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L), issuedBy = DUMMY_CASH_ISSUER) - services.fillWithSomeTestCash(400.POUNDS, notaryServices, DUMMY_NOTARY, 4, 4, Random(0L), issuedBy = BOC.ref(2)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(200.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L), issuedBy = BOC.ref(1)) + vaultFiller.fillWithSomeTestCash(300.POUNDS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L), issuedBy = DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(400.POUNDS, notaryServices, DUMMY_NOTARY, 4, 4, Random(0L), issuedBy = BOC.ref(2)) // DOCSTART VaultQueryExample23 val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum(groupByColumns = listOf(CashSchemaV1.PersistentCashState::issuerPartyHash, @@ -816,12 +777,11 @@ class VaultQueryTests { fun `aggregate functions count by contract type`() { database.transaction { // create new states - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 10, 10, Random(0L)) - services.fillWithSomeTestLinearStates(1, "XYZ") - services.fillWithSomeTestLinearStates(2, "JKL") - services.fillWithSomeTestLinearStates(3, "ABC") - services.fillWithSomeTestDeals(listOf("123", "456", "789")) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 10, 10, Random(0L)) + vaultFiller.fillWithSomeTestLinearStates(1, "XYZ") + vaultFiller.fillWithSomeTestLinearStates(2, "JKL") + vaultFiller.fillWithSomeTestLinearStates(3, "ABC") + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) // count fungible assets val count = builder { VaultSchemaV1.VaultStates::recordedTime.count() } val countCriteria = QueryCriteria.VaultCustomQueryCriteria(count) @@ -842,12 +802,11 @@ class VaultQueryTests { fun `aggregate functions count by contract type and state status`() { database.transaction { // create new states - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 10, 10, Random(0L)) - val linearStatesXYZ = services.fillWithSomeTestLinearStates(1, "XYZ") - val linearStatesJKL = services.fillWithSomeTestLinearStates(2, "JKL") - services.fillWithSomeTestLinearStates(3, "ABC") - val dealStates = services.fillWithSomeTestDeals(listOf("123", "456", "789")) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 10, 10, Random(0L)) + val linearStatesXYZ = vaultFiller.fillWithSomeTestLinearStates(1, "XYZ") + val linearStatesJKL = vaultFiller.fillWithSomeTestLinearStates(2, "JKL") + vaultFiller.fillWithSomeTestLinearStates(3, "ABC") + val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) val count = builder { VaultSchemaV1.VaultStates::recordedTime.count() } // count fungible assets @@ -864,11 +823,10 @@ class VaultQueryTests { assertThat(dealStateCount).isEqualTo(3L) // consume some states - services.consumeLinearStates(linearStatesXYZ.states.toList(), DUMMY_NOTARY) - services.consumeLinearStates(linearStatesJKL.states.toList(), DUMMY_NOTARY) - services.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }, DUMMY_NOTARY) - val cashUpdates = services.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) - + vaultFiller.consumeLinearStates(linearStatesXYZ.states.toList(), DUMMY_NOTARY) + vaultFiller.consumeLinearStates(linearStatesJKL.states.toList(), DUMMY_NOTARY) + vaultFiller.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }, DUMMY_NOTARY) + val cashUpdates = vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) // UNCONSUMED states (default) // count fungible assets @@ -906,8 +864,7 @@ class VaultQueryTests { @Test fun `unconsumed states recorded between two time intervals`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 3, 3, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 3, 3, Random(0L)) // DOCSTART VaultQueryExample6 val start = TODAY val end = TODAY.plus(30, ChronoUnit.DAYS) @@ -931,13 +888,12 @@ class VaultQueryTests { @Test fun `states consumed after time`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestLinearStates(10) - services.fillWithSomeTestDeals(listOf("123", "456", "789")) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestLinearStates(10) + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) } database.transaction { - services.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) - + vaultFiller.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) val asOfDateTime = TODAY val consumedAfterExpression = TimeCondition( QueryCriteria.TimeInstantType.CONSUMED, ColumnPredicate.BinaryComparison(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL, asOfDateTime)) @@ -953,8 +909,7 @@ class VaultQueryTests { @Test fun `all states with paging specification - first page`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 100, 100, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 100, 100, Random(0L)) // DOCSTART VaultQueryExample7 val pagingSpec = PageSpecification(DEFAULT_PAGE_NUM, 10) val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) @@ -969,8 +924,7 @@ class VaultQueryTests { @Test fun `all states with paging specification - last`() { database.transaction { - services.fillWithSomeTestCash(95.DOLLARS, notaryServices, DUMMY_NOTARY, 95, 95, Random(0L)) - + vaultFiller.fillWithSomeTestCash(95.DOLLARS, notaryServices, DUMMY_NOTARY, 95, 95, Random(0L)) // Last page implies we need to perform a row count for the Query first, // and then re-query for a given offset defined by (count - pageSize) val pagingSpec = PageSpecification(10, 10) @@ -989,8 +943,7 @@ class VaultQueryTests { expectedEx.expectMessage("Page specification: invalid page number") database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 100, 100, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 100, 100, Random(0L)) val pagingSpec = PageSpecification(0, 10) val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) @@ -1005,8 +958,7 @@ class VaultQueryTests { expectedEx.expectMessage("Page specification: invalid page size") database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 100, 100, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 100, 100, Random(0L)) @Suppress("EXPECTED_CONDITION") val pagingSpec = PageSpecification(DEFAULT_PAGE_NUM, @Suppress("INTEGER_OVERFLOW") MAX_PAGE_SIZE + 1) // overflow = -2147483648 val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) @@ -1021,8 +973,7 @@ class VaultQueryTests { expectedEx.expectMessage("Please specify a `PageSpecification`") database.transaction { - services.fillWithSomeTestCash(201.DOLLARS, notaryServices, DUMMY_NOTARY, 201, 201, Random(0L)) - + vaultFiller.fillWithSomeTestCash(201.DOLLARS, notaryServices, DUMMY_NOTARY, 201, 201, Random(0L)) val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) vaultService.queryBy(criteria) } @@ -1060,10 +1011,9 @@ class VaultQueryTests { @Test fun `unconsumed fungible assets`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestCommodity(Amount(100, Commodity.getInstance("FCOJ")!!), notaryServices) - services.fillWithSomeTestLinearStates(10) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCommodity(Amount(100, Commodity.getInstance("FCOJ")!!), notaryServices) + vaultFiller.fillWithSomeTestLinearStates(10) val results = vaultService.queryBy>() assertThat(results.states).hasSize(4) } @@ -1072,14 +1022,12 @@ class VaultQueryTests { @Test fun `consumed fungible assets`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) } database.transaction { - services.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) - - services.fillWithSomeTestCommodity(Amount(100, Commodity.getInstance("FCOJ")!!), notaryServices) - services.fillWithSomeTestLinearStates(10) - + vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) + vaultFiller.fillWithSomeTestCommodity(Amount(100, Commodity.getInstance("FCOJ")!!), notaryServices) + vaultFiller.fillWithSomeTestLinearStates(10) val criteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED) val results = vaultService.queryBy>(criteria) assertThat(results.states).hasSize(2) @@ -1089,9 +1037,8 @@ class VaultQueryTests { @Test fun `unconsumed cash fungible assets`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestLinearStates(10) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestLinearStates(10) val results = vaultService.queryBy() assertThat(results.states).hasSize(3) } @@ -1100,10 +1047,10 @@ class VaultQueryTests { @Test fun `unconsumed cash fungible assets after spending`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) } database.transaction { - services.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) + vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) // should now have x2 CONSUMED + x2 UNCONSUMED (one spent + one change) val results = vaultService.queryBy(FungibleAssetQueryCriteria()) assertThat(results.statesMetadata).hasSize(2) @@ -1114,13 +1061,12 @@ class VaultQueryTests { @Test fun `consumed cash fungible assets`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) } database.transaction { - services.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) - - val linearStates = services.fillWithSomeTestLinearStates(10) - services.consumeLinearStates(linearStates.states.toList(), DUMMY_NOTARY) + vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) + val linearStates = vaultFiller.fillWithSomeTestLinearStates(10) + vaultFiller.consumeLinearStates(linearStates.states.toList(), DUMMY_NOTARY) val criteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED) val results = vaultService.queryBy(criteria) assertThat(results.states).hasSize(2) @@ -1130,10 +1076,9 @@ class VaultQueryTests { @Test fun `unconsumed linear heads`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestLinearStates(10) - services.fillWithSomeTestDeals(listOf("123", "456", "789")) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestLinearStates(10) + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) val results = vaultService.queryBy() assertThat(results.states).hasSize(13) } @@ -1142,15 +1087,13 @@ class VaultQueryTests { @Test fun `consumed linear heads`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - val linearStates = services.fillWithSomeTestLinearStates(2, "TEST") // create 2 states with same externalId - services.fillWithSomeTestLinearStates(8) - val dealStates = services.fillWithSomeTestDeals(listOf("123", "456", "789")) - - services.consumeLinearStates(linearStates.states.toList(), DUMMY_NOTARY) - services.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }, DUMMY_NOTARY) - services.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + val linearStates = vaultFiller.fillWithSomeTestLinearStates(2, "TEST") // create 2 states with same externalId + vaultFiller.fillWithSomeTestLinearStates(8) + val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) + vaultFiller.consumeLinearStates(linearStates.states.toList(), DUMMY_NOTARY) + vaultFiller.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }, DUMMY_NOTARY) + vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) val criteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED) val results = vaultService.queryBy(criteria) assertThat(results.states).hasSize(3) @@ -1162,8 +1105,7 @@ class VaultQueryTests { @Test fun `unconsumed linear heads for linearId without external Id`() { database.transaction { - val issuedStates = services.fillWithSomeTestLinearStates(10) - + val issuedStates = vaultFiller.fillWithSomeTestLinearStates(10) // DOCSTART VaultQueryExample8 val linearIds = issuedStates.states.map { it.state.data.linearId }.toList() val criteria = LinearStateQueryCriteria(linearId = listOf(linearIds.first(), linearIds.last())) @@ -1176,10 +1118,9 @@ class VaultQueryTests { @Test fun `unconsumed linear heads by linearId`() { database.transaction { - val linearState1 = services.fillWithSomeTestLinearStates(1, "ID1") - services.fillWithSomeTestLinearStates(1, "ID2") - val linearState3 = services.fillWithSomeTestLinearStates(1, "ID3") - + val linearState1 = vaultFiller.fillWithSomeTestLinearStates(1, "ID1") + vaultFiller.fillWithSomeTestLinearStates(1, "ID2") + val linearState3 = vaultFiller.fillWithSomeTestLinearStates(1, "ID3") val linearIds = listOf(linearState1.states.first().state.data.linearId, linearState3.states.first().state.data.linearId) val criteria = LinearStateQueryCriteria(linearId = linearIds) val results = vaultService.queryBy(criteria) @@ -1190,10 +1131,9 @@ class VaultQueryTests { @Test fun `unconsumed linear heads for linearId by external Id`() { database.transaction { - val linearState1 = services.fillWithSomeTestLinearStates(1, "ID1") - services.fillWithSomeTestLinearStates(1, "ID2") - val linearState3 = services.fillWithSomeTestLinearStates(1, "ID3") - + val linearState1 = vaultFiller.fillWithSomeTestLinearStates(1, "ID1") + vaultFiller.fillWithSomeTestLinearStates(1, "ID2") + val linearState3 = vaultFiller.fillWithSomeTestLinearStates(1, "ID3") val externalIds = listOf(linearState1.states.first().state.data.linearId.externalId!!, linearState3.states.first().state.data.linearId.externalId!!) val criteria = LinearStateQueryCriteria(externalId = externalIds) val results = vaultService.queryBy(criteria) @@ -1204,11 +1144,11 @@ class VaultQueryTests { @Test fun `all linear states for a given linear id`() { database.transaction { - val txns = services.fillWithSomeTestLinearStates(1, "TEST") + val txns = vaultFiller.fillWithSomeTestLinearStates(1, "TEST") val linearState = txns.states.first() - services.evolveLinearState(linearState, DUMMY_NOTARY) // consume current and produce new state reference - services.evolveLinearState(linearState, DUMMY_NOTARY) // consume current and produce new state reference - services.evolveLinearState(linearState, DUMMY_NOTARY) // consume current and produce new state reference + vaultFiller.evolveLinearState(linearState, DUMMY_NOTARY) // consume current and produce new state reference + vaultFiller.evolveLinearState(linearState, DUMMY_NOTARY) // consume current and produce new state reference + vaultFiller.evolveLinearState(linearState, DUMMY_NOTARY) // consume current and produce new state reference val linearId = linearState.state.data.linearId // should now have 1 UNCONSUMED & 3 CONSUMED state refs for Linear State with "TEST" @@ -1224,12 +1164,11 @@ class VaultQueryTests { @Test fun `all linear states for a given id sorted by uuid`() { database.transaction { - val txns = services.fillWithSomeTestLinearStates(2, "TEST") + val txns = vaultFiller.fillWithSomeTestLinearStates(2, "TEST") val linearStates = txns.states.toList() - services.evolveLinearStates(linearStates, DUMMY_NOTARY) // consume current and produce new state reference - services.evolveLinearStates(linearStates, DUMMY_NOTARY) // consume current and produce new state reference - services.evolveLinearStates(linearStates, DUMMY_NOTARY) // consume current and produce new state reference - + vaultFiller.evolveLinearStates(linearStates, DUMMY_NOTARY) // consume current and produce new state reference + vaultFiller.evolveLinearStates(linearStates, DUMMY_NOTARY) // consume current and produce new state reference + vaultFiller.evolveLinearStates(linearStates, DUMMY_NOTARY) // consume current and produce new state reference // should now have 1 UNCONSUMED & 3 CONSUMED state refs for Linear State with "TEST" val linearStateCriteria = LinearStateQueryCriteria(uuid = linearStates.map { it.state.data.linearId.id }, status = Vault.StateStatus.ALL) val vaultCriteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) @@ -1244,10 +1183,9 @@ class VaultQueryTests { @Test fun `unconsumed linear states sorted by external id`() { database.transaction { - services.fillWithSomeTestLinearStates(1, externalId = "111") - services.fillWithSomeTestLinearStates(2, externalId = "222") - services.fillWithSomeTestLinearStates(3, externalId = "333") - + vaultFiller.fillWithSomeTestLinearStates(1, externalId = "111") + vaultFiller.fillWithSomeTestLinearStates(2, externalId = "222") + vaultFiller.fillWithSomeTestLinearStates(3, externalId = "333") val vaultCriteria = VaultQueryCriteria() val sorting = Sort(setOf(Sort.SortColumn(SortAttribute.Standard(Sort.LinearStateAttribute.EXTERNAL_ID), Sort.Direction.DESC))) @@ -1260,8 +1198,8 @@ class VaultQueryTests { @Test fun `unconsumed deal states sorted`() { database.transaction { - val linearStates = services.fillWithSomeTestLinearStates(10) - services.fillWithSomeTestDeals(listOf("123", "456", "789")) + val linearStates = vaultFiller.fillWithSomeTestLinearStates(10) + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) val uid = linearStates.states.first().state.data.linearId.id val linearStateCriteria = LinearStateQueryCriteria(uuid = listOf(uid)) @@ -1279,10 +1217,9 @@ class VaultQueryTests { @Test fun `unconsumed linear states sorted by custom attribute`() { database.transaction { - services.fillWithSomeTestLinearStates(1, linearString = "111") - services.fillWithSomeTestLinearStates(2, linearString = "222") - services.fillWithSomeTestLinearStates(3, linearString = "333") - + vaultFiller.fillWithSomeTestLinearStates(1, linearString = "111") + vaultFiller.fillWithSomeTestLinearStates(2, linearString = "222") + vaultFiller.fillWithSomeTestLinearStates(3, linearString = "333") val vaultCriteria = VaultQueryCriteria() val sorting = Sort(setOf(Sort.SortColumn(SortAttribute.Custom(DummyLinearStateSchemaV1.PersistentDummyLinearState::class.java, "linearString"), Sort.Direction.DESC))) @@ -1295,14 +1232,11 @@ class VaultQueryTests { @Test fun `return consumed linear states for a given linear id`() { database.transaction { - val txns = services.fillWithSomeTestLinearStates(1, "TEST") + val txns = vaultFiller.fillWithSomeTestLinearStates(1, "TEST") val linearState = txns.states.first() - - val linearState2 =services.evolveLinearState(linearState, DUMMY_NOTARY) // consume current and produce new state reference - val linearState3 = services.evolveLinearState(linearState2, DUMMY_NOTARY) // consume current and produce new state reference - - services.evolveLinearState(linearState3, DUMMY_NOTARY) // consume current and produce new state reference - + val linearState2 = vaultFiller.evolveLinearState(linearState, DUMMY_NOTARY) // consume current and produce new state reference + val linearState3 = vaultFiller.evolveLinearState(linearState2, DUMMY_NOTARY) // consume current and produce new state reference + vaultFiller.evolveLinearState(linearState3, DUMMY_NOTARY) // consume current and produce new state reference // should now have 1 UNCONSUMED & 3 CONSUMED state refs for Linear State with "TEST" val linearStateCriteria = LinearStateQueryCriteria(linearId = txns.states.map { it.state.data.linearId }, status = Vault.StateStatus.CONSUMED) val vaultCriteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED) @@ -1318,8 +1252,7 @@ class VaultQueryTests { @Test fun `unconsumed deals`() { database.transaction { - services.fillWithSomeTestDeals(listOf("123", "456", "789")) - + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) val results = vaultService.queryBy() assertThat(results.states).hasSize(3) } @@ -1328,8 +1261,7 @@ class VaultQueryTests { @Test fun `unconsumed deals for ref`() { database.transaction { - services.fillWithSomeTestDeals(listOf("123", "456", "789")) - + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) // DOCSTART VaultQueryExample10 val criteria = LinearStateQueryCriteria(externalId = listOf("456", "789")) val results = vaultService.queryBy(criteria) @@ -1342,10 +1274,9 @@ class VaultQueryTests { @Test fun `latest unconsumed deals for ref`() { database.transaction { - services.fillWithSomeTestLinearStates(2, "TEST") - services.fillWithSomeTestDeals(listOf("456")) - services.fillWithSomeTestDeals(listOf("123", "789")) - + vaultFiller.fillWithSomeTestLinearStates(2, "TEST") + vaultFiller.fillWithSomeTestDeals(listOf("456")) + vaultFiller.fillWithSomeTestDeals(listOf("123", "789")) val all = vaultService.queryBy() all.states.forEach { println(it.state) } @@ -1359,10 +1290,9 @@ class VaultQueryTests { fun `latest unconsumed deals with party`() { val parties = listOf(MINI_CORP) database.transaction { - services.fillWithSomeTestLinearStates(2, "TEST") - services.fillWithSomeTestDeals(listOf("456"), participants = parties) - services.fillWithSomeTestDeals(listOf("123", "789")) - + vaultFiller.fillWithSomeTestLinearStates(2, "TEST") + vaultFiller.fillWithSomeTestDeals(listOf("456"), participants = parties) + vaultFiller.fillWithSomeTestDeals(listOf("123", "789")) // DOCSTART VaultQueryExample11 val criteria = LinearStateQueryCriteria(participants = parties) val results = vaultService.queryBy(criteria) @@ -1378,11 +1308,10 @@ class VaultQueryTests { fun `unconsumed fungible assets for specific issuer party and refs`() { database.transaction { identitySvc.verifyAndRegisterIdentity(BOC_IDENTITY) - - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = DUMMY_CASH_ISSUER) - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(1)) - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(2)) - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(3)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(1)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(2)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(3)) val criteria = FungibleAssetQueryCriteria(issuer = listOf(BOC), issuerRef = listOf(BOC.ref(1).reference, BOC.ref(2).reference)) val results = vaultService.queryBy>(criteria) @@ -1408,9 +1337,9 @@ class VaultQueryTests { services.identityService.verifyAndRegisterIdentity(identity) } database.transaction { - services.fillWithSomeTestCash(100.POUNDS, gbpCashIssuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = gbpCashIssuer.party.ref(1)) - services.fillWithSomeTestCash(100.DOLLARS, usdCashIssuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = usdCashIssuer.party.ref(1)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, chfCashIssuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = chfCashIssuer.party.ref(1)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, gbpCashIssuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = gbpCashIssuer.party.ref(1)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, usdCashIssuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = usdCashIssuer.party.ref(1)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, chfCashIssuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = chfCashIssuer.party.ref(1)) } database.transaction { val criteria = FungibleAssetQueryCriteria(issuer = listOf(gbpCashIssuer.party, usdCashIssuer.party)) @@ -1422,8 +1351,8 @@ class VaultQueryTests { @Test fun `unconsumed fungible assets by owner`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(1)) - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(1)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = MEGA_CORP.ref(0), owner = (MINI_CORP)) } database.transaction { @@ -1436,10 +1365,10 @@ class VaultQueryTests { @Test fun `unconsumed fungible states for owners`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = MEGA_CORP.ref(0), owner = (MEGA_CORP)) - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(0), owner = MINI_CORP) // irrelevant to this vault } database.transaction { @@ -1456,11 +1385,10 @@ class VaultQueryTests { @Test fun `unconsumed fungible assets for single currency`() { database.transaction { - services.fillWithSomeTestLinearStates(10) - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - + vaultFiller.fillWithSomeTestLinearStates(10) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) // DOCSTART VaultQueryExample12 val ccyIndex = builder { CashSchemaV1.PersistentCashState::currency.equal(USD.currencyCode) } val criteria = VaultCustomQueryCriteria(ccyIndex) @@ -1474,9 +1402,8 @@ class VaultQueryTests { @Test fun `unconsumed cash balance for single currency`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(200.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(200.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L)) val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) } val sumCriteria = VaultCustomQueryCriteria(sum) @@ -1494,13 +1421,12 @@ class VaultQueryTests { @Test fun `unconsumed cash balances for all currencies`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(200.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L)) - services.fillWithSomeTestCash(300.POUNDS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestCash(400.POUNDS, notaryServices, DUMMY_NOTARY, 4, 4, Random(0L)) - services.fillWithSomeTestCash(500.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) - services.fillWithSomeTestCash(600.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 6, 6, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(200.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L)) + vaultFiller.fillWithSomeTestCash(300.POUNDS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(400.POUNDS, notaryServices, DUMMY_NOTARY, 4, 4, Random(0L)) + vaultFiller.fillWithSomeTestCash(500.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) + vaultFiller.fillWithSomeTestCash(600.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 6, 6, Random(0L)) val ccyIndex = builder { CashSchemaV1.PersistentCashState::pennies.sum(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) } val criteria = VaultCustomQueryCriteria(ccyIndex) val results = vaultService.queryBy>(criteria) @@ -1518,11 +1444,10 @@ class VaultQueryTests { @Test fun `unconsumed fungible assets for quantity greater than`() { database.transaction { - services.fillWithSomeTestCash(10.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - services.fillWithSomeTestCash(25.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(50.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - + vaultFiller.fillWithSomeTestCash(10.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(25.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(50.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) // DOCSTART VaultQueryExample13 val fungibleAssetCriteria = FungibleAssetQueryCriteria(quantity = builder { greaterThan(2500L) }) val results = vaultService.queryBy(fungibleAssetCriteria) @@ -1536,10 +1461,8 @@ class VaultQueryTests { fun `unconsumed fungible assets for issuer party`() { database.transaction { identitySvc.verifyAndRegisterIdentity(BOC_IDENTITY) - - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (DUMMY_CASH_ISSUER)) - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (BOC.ref(1))) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (DUMMY_CASH_ISSUER)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (BOC.ref(1))) // DOCSTART VaultQueryExample14 val criteria = FungibleAssetQueryCriteria(issuer = listOf(BOC)) val results = vaultService.queryBy>(criteria) @@ -1552,11 +1475,10 @@ class VaultQueryTests { @Test fun `unconsumed fungible assets for single currency and quantity greater than`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(50.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(50.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) val ccyIndex = builder { CashSchemaV1.PersistentCashState::currency.equal(GBP.currencyCode) } val customCriteria = VaultCustomQueryCriteria(ccyIndex) val fungibleAssetCriteria = FungibleAssetQueryCriteria(quantity = builder { greaterThan(5000L) }) @@ -1656,10 +1578,9 @@ class VaultQueryTests { cordappPackages -= SampleCashSchemaV3::class.packageName setUp() database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) // CashSchemaV3 NOT registered with NodeSchemaService val logicalExpression = builder { SampleCashSchemaV3.PersistentCashState::currency.equal(GBP.currencyCode) } val criteria = VaultCustomQueryCriteria(logicalExpression) @@ -1676,11 +1597,10 @@ class VaultQueryTests { @Test fun `custom - all cash states with amount of currency greater or equal than`() { database.transaction { - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(10.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - services.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(10.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) // DOCSTART VaultQueryExample20 val generalCriteria = VaultQueryCriteria(Vault.StateStatus.ALL) @@ -1707,10 +1627,9 @@ class VaultQueryTests { val end = start.plus(1, ChronoUnit.SECONDS) database.transaction { - services.fillWithSomeTestLinearStates(1, "TEST") + vaultFiller.fillWithSomeTestLinearStates(1, "TEST") sleep(1000) - services.fillWithSomeTestLinearStates(1, "TEST") - + vaultFiller.fillWithSomeTestLinearStates(1, "TEST") // 2 unconsumed states with same external ID val recordedBetweenExpression = TimeCondition(TimeInstantType.RECORDED, builder { between(start, end) }) val basicCriteria = VaultQueryCriteria(timeCondition = recordedBetweenExpression) @@ -1725,9 +1644,8 @@ class VaultQueryTests { @Test fun `unconsumed linear heads for a given external id`() { database.transaction { - services.fillWithSomeTestLinearStates(1, "TEST1") - services.fillWithSomeTestLinearStates(1, "TEST2") - + vaultFiller.fillWithSomeTestLinearStates(1, "TEST1") + vaultFiller.fillWithSomeTestLinearStates(1, "TEST2") // 2 unconsumed states with same external ID val externalIdCondition = builder { VaultSchemaV1.VaultLinearStates::externalId.equal("TEST2") } val externalIdCustomCriteria = VaultCustomQueryCriteria(externalIdCondition) @@ -1745,11 +1663,10 @@ class VaultQueryTests { val end = start.plus(1, ChronoUnit.SECONDS) database.transaction { - services.fillWithSomeTestLinearStates(1, "TEST1") - services.fillWithSomeTestLinearStates(1, "TEST2") + vaultFiller.fillWithSomeTestLinearStates(1, "TEST1") + vaultFiller.fillWithSomeTestLinearStates(1, "TEST2") sleep(1000) - services.fillWithSomeTestLinearStates(1, "TEST3") - + vaultFiller.fillWithSomeTestLinearStates(1, "TEST3") // 2 unconsumed states with same external ID val results = builder { @@ -1771,11 +1688,10 @@ class VaultQueryTests { @Test fun `unconsumed linear heads for a given external id or uuid`() { database.transaction { - services.fillWithSomeTestLinearStates(1, "TEST1") - val aState = services.fillWithSomeTestLinearStates(1, "TEST2").states - services.consumeLinearStates(aState.toList(), DUMMY_NOTARY) - val uuid = services.fillWithSomeTestLinearStates(1, "TEST1").states.first().state.data.linearId.id - + vaultFiller.fillWithSomeTestLinearStates(1, "TEST1") + val aState = vaultFiller.fillWithSomeTestLinearStates(1, "TEST2").states + vaultFiller.consumeLinearStates(aState.toList(), DUMMY_NOTARY) + val uuid = vaultFiller.fillWithSomeTestLinearStates(1, "TEST1").states.first().state.data.linearId.id // 2 unconsumed states with same external ID, 1 consumed with different external ID val results = builder { val externalIdCondition = VaultSchemaV1.VaultLinearStates::externalId.equal("TEST1") @@ -1796,10 +1712,9 @@ class VaultQueryTests { fun `unconsumed linear heads for single participant`() { database.transaction { identitySvc.verifyAndRegisterIdentity(ALICE_IDENTITY) - services.fillWithSomeTestLinearStates(1, "TEST1", listOf(ALICE)) - services.fillWithSomeTestLinearStates(1) - services.fillWithSomeTestLinearStates(1, "TEST3") - + vaultFiller.fillWithSomeTestLinearStates(1, "TEST1", listOf(ALICE)) + vaultFiller.fillWithSomeTestLinearStates(1) + vaultFiller.fillWithSomeTestLinearStates(1, "TEST3") val linearStateCriteria = LinearStateQueryCriteria(participants = listOf(ALICE)) val results = vaultService.queryBy(linearStateCriteria) @@ -1814,11 +1729,9 @@ class VaultQueryTests { identitySvc.verifyAndRegisterIdentity(ALICE_IDENTITY) identitySvc.verifyAndRegisterIdentity(BOB_IDENTITY) identitySvc.verifyAndRegisterIdentity(CHARLIE_IDENTITY) - - services.fillWithSomeTestLinearStates(1, "TEST1", listOf(ALICE, BOB, CHARLIE)) - services.fillWithSomeTestLinearStates(1) - services.fillWithSomeTestLinearStates(1, "TEST3") - + vaultFiller.fillWithSomeTestLinearStates(1, "TEST1", listOf(ALICE, BOB, CHARLIE)) + vaultFiller.fillWithSomeTestLinearStates(1) + vaultFiller.fillWithSomeTestLinearStates(1, "TEST3") val linearStateCriteria = LinearStateQueryCriteria(participants = listOf(ALICE, BOB, CHARLIE)) val results = vaultService.queryBy(linearStateCriteria) @@ -1830,10 +1743,9 @@ class VaultQueryTests { @Test fun `unconsumed linear heads where external id is null`() { database.transaction { - services.fillWithSomeTestLinearStates(1, "TEST1") - services.fillWithSomeTestLinearStates(1) - services.fillWithSomeTestLinearStates(1, "TEST3") - + vaultFiller.fillWithSomeTestLinearStates(1, "TEST1") + vaultFiller.fillWithSomeTestLinearStates(1) + vaultFiller.fillWithSomeTestLinearStates(1, "TEST3") // 3 unconsumed states (one without an external ID) val results = builder { val externalIdCondition = VaultSchemaV1.VaultLinearStates::externalId.isNull() @@ -1848,10 +1760,9 @@ class VaultQueryTests { @Test fun `unconsumed linear heads where external id is not null`() { database.transaction { - services.fillWithSomeTestLinearStates(1, "TEST1") - services.fillWithSomeTestLinearStates(1) - services.fillWithSomeTestLinearStates(1, "TEST3") - + vaultFiller.fillWithSomeTestLinearStates(1, "TEST1") + vaultFiller.fillWithSomeTestLinearStates(1) + vaultFiller.fillWithSomeTestLinearStates(1, "TEST3") // 3 unconsumed states (two with an external ID) val results = builder { val externalIdCondition = VaultSchemaV1.VaultLinearStates::externalId.notNull() @@ -1866,11 +1777,10 @@ class VaultQueryTests { @Test fun `enriched and overridden composite query handles defaults correctly`() { database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L)) - services.fillWithSomeTestCommodity(Amount(100, Commodity.getInstance("FCOJ")!!), notaryServices) - services.fillWithSomeTestLinearStates(1, "ABC") - services.fillWithSomeTestDeals(listOf("123")) - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L)) + vaultFiller.fillWithSomeTestCommodity(Amount(100, Commodity.getInstance("FCOJ")!!), notaryServices) + vaultFiller.fillWithSomeTestLinearStates(1, "ABC") + vaultFiller.fillWithSomeTestDeals(listOf("123")) // Base criteria val baseCriteria = VaultQueryCriteria(notary = listOf(DUMMY_NOTARY), status = Vault.StateStatus.CONSUMED) @@ -1903,20 +1813,20 @@ class VaultQueryTests { } val (linearStates, dealStates) = database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) - val linearStates = services.fillWithSomeTestLinearStates(10).states - val dealStates = services.fillWithSomeTestDeals(listOf("123", "456", "789")).states + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) + val linearStates = vaultFiller.fillWithSomeTestLinearStates(10).states + val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")).states // add more cash - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) // add another deal - services.fillWithSomeTestDeals(listOf("SAMPLE DEAL")) + vaultFiller.fillWithSomeTestDeals(listOf("SAMPLE DEAL")) Pair(linearStates, dealStates) } database.transaction { // consume stuff - services.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) - services.consumeDeals(dealStates.toList(), DUMMY_NOTARY) - services.consumeLinearStates(linearStates.toList(), DUMMY_NOTARY) + vaultFiller.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) + vaultFiller.consumeDeals(dealStates.toList(), DUMMY_NOTARY) + vaultFiller.consumeLinearStates(linearStates.toList(), DUMMY_NOTARY) } updates.expectEvents { @@ -1944,25 +1854,24 @@ class VaultQueryTests { } val (linearStates, dealStates) = database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) - val linearStates = services.fillWithSomeTestLinearStates(10).states - val dealStates = services.fillWithSomeTestDeals(listOf("123", "456", "789")).states - + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) + val linearStates = vaultFiller.fillWithSomeTestLinearStates(10).states + val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")).states // add more cash - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) // add another deal - services.fillWithSomeTestDeals(listOf("SAMPLE DEAL")) + vaultFiller.fillWithSomeTestDeals(listOf("SAMPLE DEAL")) Pair(linearStates, dealStates) } database.transaction { // consume stuff - services.consumeCash(100.POUNDS, notary = DUMMY_NOTARY) + vaultFiller.consumeCash(100.POUNDS, notary = DUMMY_NOTARY) } database.transaction { // consume more stuff - services.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) - services.consumeDeals(dealStates.toList(), DUMMY_NOTARY) - services.consumeLinearStates(linearStates.toList(), DUMMY_NOTARY) + vaultFiller.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) + vaultFiller.consumeDeals(dealStates.toList(), DUMMY_NOTARY) + vaultFiller.consumeLinearStates(linearStates.toList(), DUMMY_NOTARY) } updates.expectEvents { @@ -1990,24 +1899,24 @@ class VaultQueryTests { } val (linearStates, dealStates) = database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) - val linearStates = services.fillWithSomeTestLinearStates(10).states - val dealStates = services.fillWithSomeTestDeals(listOf("123", "456", "789")).states + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) + val linearStates = vaultFiller.fillWithSomeTestLinearStates(10).states + val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")).states // add more cash - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) // add another deal - services.fillWithSomeTestDeals(listOf("SAMPLE DEAL")) + vaultFiller.fillWithSomeTestDeals(listOf("SAMPLE DEAL")) Pair(linearStates, dealStates) } database.transaction { // consume stuff - services.consumeCash(99.POUNDS, notary = DUMMY_NOTARY) + vaultFiller.consumeCash(99.POUNDS, notary = DUMMY_NOTARY) } database.transaction { // consume more stuff - services.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) - services.consumeDeals(dealStates.toList(), DUMMY_NOTARY) - services.consumeLinearStates(linearStates.toList(), DUMMY_NOTARY) + vaultFiller.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) + vaultFiller.consumeDeals(dealStates.toList(), DUMMY_NOTARY) + vaultFiller.consumeLinearStates(linearStates.toList(), DUMMY_NOTARY) } updates.expectEvents { @@ -2048,20 +1957,20 @@ class VaultQueryTests { } val (linearStates, dealStates) = database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - val linearStates = services.fillWithSomeTestLinearStates(10).states - val dealStates = services.fillWithSomeTestDeals(listOf("123", "456", "789")).states + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + val linearStates = vaultFiller.fillWithSomeTestLinearStates(10).states + val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")).states // add more cash - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) // add another deal - services.fillWithSomeTestDeals(listOf("SAMPLE DEAL")) + vaultFiller.fillWithSomeTestDeals(listOf("SAMPLE DEAL")) Pair(linearStates, dealStates) } database.transaction { // consume stuff - services.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) - services.consumeDeals(dealStates.toList(), DUMMY_NOTARY) - services.consumeLinearStates(linearStates.toList(), DUMMY_NOTARY) + vaultFiller.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) + vaultFiller.consumeDeals(dealStates.toList(), DUMMY_NOTARY) + vaultFiller.consumeLinearStates(linearStates.toList(), DUMMY_NOTARY) } updates.expectEvents { @@ -2097,20 +2006,20 @@ class VaultQueryTests { } val (linearStates, dealStates) = database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - val linearStates = services.fillWithSomeTestLinearStates(10).states - val dealStates = services.fillWithSomeTestDeals(listOf("123", "456", "789")).states + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + val linearStates = vaultFiller.fillWithSomeTestLinearStates(10).states + val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")).states // add more cash - services.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) // add another deal - services.fillWithSomeTestDeals(listOf("SAMPLE DEAL")) + vaultFiller.fillWithSomeTestDeals(listOf("SAMPLE DEAL")) Pair(linearStates, dealStates) } database.transaction { // consume stuff - services.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) - services.consumeDeals(dealStates.toList(), DUMMY_NOTARY) - services.consumeLinearStates(linearStates.toList(), DUMMY_NOTARY) + vaultFiller.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) + vaultFiller.consumeDeals(dealStates.toList(), DUMMY_NOTARY) + vaultFiller.consumeLinearStates(linearStates.toList(), DUMMY_NOTARY) } updates.expectEvents { 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 34b5672736..1146b79b96 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 @@ -51,6 +51,7 @@ class VaultWithCashTest { @JvmField val testSerialization = SerializationEnvironmentRule(true) lateinit var services: MockServices + private lateinit var vaultFiller: VaultFiller lateinit var issuerServices: MockServices val vaultService: VaultService get() = services.vaultService lateinit var database: CordaPersistence @@ -63,6 +64,7 @@ class VaultWithCashTest { val databaseAndServices = makeTestDatabaseAndMockServices(cordappPackages = cordappPackages, keys = listOf(generateKeyPair(), DUMMY_NOTARY_KEY)) database = databaseAndServices.first services = databaseAndServices.second + vaultFiller = VaultFiller(services) issuerServices = MockServices(cordappPackages, DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY, MEGA_CORP_KEY) notaryServices = MockServices(cordappPackages, DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) notary = notaryServices.myInfo.legalIdentitiesAndCerts.single().party @@ -78,7 +80,7 @@ class VaultWithCashTest { fun splits() { database.transaction { // Fix the PRNG so that we get the same splits every time. - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L), issuedBy = DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L), issuedBy = DUMMY_CASH_ISSUER) } database.transaction { val w = vaultService.queryBy().states @@ -148,7 +150,7 @@ class VaultWithCashTest { database.transaction { // A tx that sends us money. - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L), owner = AnonymousParty(freshKey), + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L), owner = AnonymousParty(freshKey), issuedBy = MEGA_CORP.ref(1)) println("Cash balance: ${services.getCashBalance(USD)}") } @@ -298,16 +300,16 @@ class VaultWithCashTest { val freshKey = services.keyManagementService.freshKey() database.transaction { - services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L), owner = AnonymousParty(freshKey)) - services.fillWithSomeTestCash(100.SWISS_FRANCS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L)) - services.fillWithSomeTestCash(100.POUNDS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L), owner = AnonymousParty(freshKey)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L)) } database.transaction { val cash = vaultService.queryBy().states cash.forEach { println(it.state.data.amount) } } database.transaction { - services.fillWithSomeTestDeals(listOf("123", "456", "789"), issuerServices) + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789"), issuerServices) } database.transaction { val deals = vaultService.queryBy().states @@ -337,14 +339,14 @@ class VaultWithCashTest { val freshKey = services.keyManagementService.freshKey() val freshIdentity = AnonymousParty(freshKey) database.transaction { - services.fillWithSomeTestDeals(listOf("123", "456", "789"), issuerServices) + vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789"), issuerServices) } val deals = database.transaction { vaultService.queryBy().states } database.transaction { - services.fillWithSomeTestLinearStates(3) + vaultFiller.fillWithSomeTestLinearStates(3) } database.transaction { val linearStates = vaultService.queryBy().states diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt index 101171880c..9dde805ec6 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt @@ -18,7 +18,7 @@ import net.corda.finance.contracts.getCashBalance import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.vault.VaultSchemaV1 -import net.corda.testing.contracts.calculateRandomlySizedAmounts +import net.corda.testing.contracts.VaultFiller.Companion.calculateRandomlySizedAmounts import net.corda.traderdemo.flow.CommercialPaperIssueFlow import net.corda.traderdemo.flow.SellerFlow import java.util.* 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 5cdc3f1bfd..9f9fac4ac7 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 @@ -29,255 +29,238 @@ import java.time.Instant import java.time.Instant.now import java.util.* -@JvmOverloads -fun ServiceHub.fillWithSomeTestDeals(dealIds: List, - issuerServices: ServiceHub = this, +class VaultFiller(private val services: ServiceHub) { + companion object { + fun calculateRandomlySizedAmounts(howMuch: Amount, min: Int, max: Int, rng: Random): LongArray { + val numSlots = min + Math.floor(rng.nextDouble() * (max - min)).toInt() + val baseSize = howMuch.quantity / numSlots + check(baseSize > 0) { baseSize } + + val amounts = LongArray(numSlots) { baseSize } + var distanceFromGoal = 0L + // If we want 10 slots then max adjust is 0.1, so even if all random numbers come out to the largest downward + // adjustment possible, the last slot ends at zero. With 20 slots, max adjust is 0.05 etc. + val maxAdjust = 1.0 / numSlots + for (i in amounts.indices) { + if (i != amounts.lastIndex) { + val adjustBy = rng.nextDouble() * maxAdjust - (maxAdjust / 2) + val adjustment = (1 + adjustBy) + val adjustTo = (amounts[i] * adjustment).toLong() + amounts[i] = adjustTo + distanceFromGoal += baseSize - adjustTo + } else { + amounts[i] += distanceFromGoal + } + } + + // The desired amount may not have divided equally to start with, so adjust the first value to make up. + amounts[0] += howMuch.quantity - amounts.sum() + + return amounts + } + } + + @JvmOverloads + fun fillWithSomeTestDeals(dealIds: List, + issuerServices: ServiceHub = services, + participants: List = emptyList(), + notary: Party = DUMMY_NOTARY): Vault { + val myKey: PublicKey = services.myInfo.chooseIdentity().owningKey + val me = AnonymousParty(myKey) + + val transactions: List = dealIds.map { + // Issue a deal state + val dummyIssue = TransactionBuilder(notary = notary).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, notary.owningKey) + } + services.recordTransactions(transactions) + // Get all the StateAndRefs of all the generated transactions. + val states = transactions.flatMap { stx -> + stx.tx.outputs.indices.map { i -> stx.tx.outRef(i) } + } + + return Vault(states) + } + + @JvmOverloads + fun fillWithSomeTestLinearStates(numberToCreate: Int, + externalId: String? = null, participants: List = emptyList(), - notary: Party = DUMMY_NOTARY): Vault { - val myKey: PublicKey = myInfo.chooseIdentity().owningKey - val me = AnonymousParty(myKey) - - val transactions: List = dealIds.map { - // Issue a deal state - val dummyIssue = TransactionBuilder(notary = notary).apply { - addOutputState(DummyDealContract.State(ref = it, participants = participants.plus(me)), DUMMY_DEAL_PROGRAM_ID) - addCommand(dummyCommand()) + linearString: String = "", + linearNumber: Long = 0L, + linearBoolean: Boolean = false, + linearTimestamp: Instant = now()): Vault { + val myKey: PublicKey = services.myInfo.chooseIdentity().owningKey + val me = AnonymousParty(myKey) + val issuerKey = DUMMY_NOTARY_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 = DUMMY_NOTARY).apply { + addOutputState(DummyLinearContract.State( + linearId = UniqueIdentifier(externalId), + participants = participants.plus(me), + linearString = linearString, + linearNumber = linearNumber, + linearBoolean = linearBoolean, + linearTimestamp = linearTimestamp), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) + addCommand(dummyCommand()) + } + return@map services.signInitialTransaction(dummyIssue).withAdditionalSignature(issuerKey, signatureMetadata) } - val stx = issuerServices.signInitialTransaction(dummyIssue) - return@map addSignature(stx, notary.owningKey) - } - - recordTransactions(transactions) - - // Get all the StateAndRefs of all the generated transactions. - val states = transactions.flatMap { stx -> - stx.tx.outputs.indices.map { i -> stx.tx.outRef(i) } - } - - return Vault(states) -} - -@JvmOverloads -fun ServiceHub.fillWithSomeTestLinearStates(numberToCreate: Int, - externalId: String? = null, - participants: List = emptyList(), - linearString: String = "", - linearNumber: Long = 0L, - linearBoolean: Boolean = false, - linearTimestamp: Instant = now()): Vault { - val myKey: PublicKey = myInfo.chooseIdentity().owningKey - val me = AnonymousParty(myKey) - val issuerKey = DUMMY_NOTARY_KEY - val signatureMetadata = SignatureMetadata(myInfo.platformVersion, Crypto.findSignatureScheme(issuerKey.public).schemeNumberID) - - val transactions: List = (1..numberToCreate).map { - // Issue a Linear state - val dummyIssue = TransactionBuilder(notary = DUMMY_NOTARY).apply { - addOutputState(DummyLinearContract.State( - linearId = UniqueIdentifier(externalId), - participants = participants.plus(me), - linearString = linearString, - linearNumber = linearNumber, - linearBoolean = linearBoolean, - linearTimestamp = linearTimestamp), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) - addCommand(dummyCommand()) + services.recordTransactions(transactions) + // Get all the StateAndRefs of all the generated transactions. + val states = transactions.flatMap { stx -> + stx.tx.outputs.indices.map { i -> stx.tx.outRef(i) } } - return@map signInitialTransaction(dummyIssue).withAdditionalSignature(issuerKey, signatureMetadata) + return Vault(states) } - recordTransactions(transactions) + /** + * Creates a random set of cash states that add up to the given amount and adds them to the vault. This is intended for + * unit tests. The cash is owned by the legal identity key from the storage service. + * + * The service hub needs to provide at least a key management service and a storage service. + * + * @param issuerServices service hub of the issuer node, which will be used to sign the transaction. + * @param outputNotary the notary to use for output states. The transaction is NOT signed by this notary. + * @return a vault object that represents the generated states (it will NOT be the full vault from the service hub!). + */ + fun fillWithSomeTestCash(howMuch: Amount, + issuerServices: ServiceHub, + outputNotary: Party, + states: Int, + issuedBy: PartyAndReference): Vault + = fillWithSomeTestCash(howMuch, issuerServices, outputNotary, states, states, issuedBy = issuedBy) - // Get all the StateAndRefs of all the generated transactions. - val states = transactions.flatMap { stx -> - stx.tx.outputs.indices.map { i -> stx.tx.outRef(i) } + /** + * Creates a random set of between (by default) 3 and 10 cash states that add up to the given amount and adds them + * to the vault. This is intended for unit tests. By default the cash is issued by [DUMMY_CASH_ISSUER] and owned by the legal + * identity key from the storage service. + * + * The service hub needs to provide at least a key management service and a storage service. + * + * @param issuerServices service hub of the issuer node, which will be used to sign the transaction. + * @param outputNotary the notary to use for output states. The transaction is NOT signed by this notary. + * @return a vault object that represents the generated states (it will NOT be the full vault from the service hub!). + */ + fun fillWithSomeTestCash(howMuch: Amount, + issuerServices: ServiceHub = services, + outputNotary: Party = DUMMY_NOTARY, + atLeastThisManyStates: Int = 3, + atMostThisManyStates: Int = 10, + rng: Random = Random(), + owner: AbstractParty? = null, + issuedBy: PartyAndReference = DUMMY_CASH_ISSUER): Vault { + val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng) + + // We will allocate one state to one transaction, for simplicities sake. + val cash = Cash() + val transactions: List = amounts.map { pennies -> + val issuance = TransactionBuilder(null as Party?) + cash.generateIssue(issuance, Amount(pennies, Issued(issuedBy, howMuch.token)), owner ?: services.myInfo.singleIdentity(), outputNotary) + return@map issuerServices.signInitialTransaction(issuance, issuedBy.party.owningKey) + } + services.recordTransactions(transactions) + // Get all the StateRefs of all the generated transactions. + val states = transactions.flatMap { stx -> + stx.tx.outputs.indices.map { i -> stx.tx.outRef(i) } + } + + return Vault(states) } - return Vault(states) -} + /** + * + * @param issuerServices service hub of the issuer node, which will be used to sign the transaction. + * @param outputNotary the notary to use for output states. The transaction is NOT signed by this notary. + * @return a vault object that represents the generated states (it will NOT be the full vault from the service hub!). + */ + // TODO: need to make all FungibleAsset commands (issue, move, exit) generic + fun fillWithSomeTestCommodity(amount: Amount, + issuerServices: ServiceHub = services, + outputNotary: Party = DUMMY_NOTARY, + ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })), + ownedBy: AbstractParty? = null, + issuedBy: PartyAndReference = DUMMY_OBLIGATION_ISSUER.ref(1)): Vault { + val myKey: PublicKey = ownedBy?.owningKey ?: services.myInfo.chooseIdentity().owningKey + val me = AnonymousParty(myKey) -/** - * Creates a random set of cash states that add up to the given amount and adds them to the vault. This is intended for - * unit tests. The cash is owned by the legal identity key from the storage service. - * - * The service hub needs to provide at least a key management service and a storage service. - * - * @param issuerServices service hub of the issuer node, which will be used to sign the transaction. - * @param outputNotary the notary to use for output states. The transaction is NOT signed by this notary. - * @return a vault object that represents the generated states (it will NOT be the full vault from the service hub!). - */ -fun ServiceHub.fillWithSomeTestCash(howMuch: Amount, - issuerServices: ServiceHub, - outputNotary: Party, - states: Int, - issuedBy: PartyAndReference): Vault - = fillWithSomeTestCash(howMuch, issuerServices, outputNotary, states, states, issuedBy = issuedBy) - -/** - * Creates a random set of between (by default) 3 and 10 cash states that add up to the given amount and adds them - * to the vault. This is intended for unit tests. By default the cash is issued by [DUMMY_CASH_ISSUER] and owned by the legal - * identity key from the storage service. - * - * The service hub needs to provide at least a key management service and a storage service. - * - * @param issuerServices service hub of the issuer node, which will be used to sign the transaction. - * @param outputNotary the notary to use for output states. The transaction is NOT signed by this notary. - * @return a vault object that represents the generated states (it will NOT be the full vault from the service hub!). - */ -fun ServiceHub.fillWithSomeTestCash(howMuch: Amount, - issuerServices: ServiceHub = this, - outputNotary: Party = DUMMY_NOTARY, - atLeastThisManyStates: Int = 3, - atMostThisManyStates: Int = 10, - rng: Random = Random(), - owner: AbstractParty? = null, - issuedBy: PartyAndReference = DUMMY_CASH_ISSUER): Vault { - val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng) - - // We will allocate one state to one transaction, for simplicities sake. - val cash = Cash() - val transactions: List = amounts.map { pennies -> + val commodity = CommodityContract() val issuance = TransactionBuilder(null as Party?) - cash.generateIssue(issuance, Amount(pennies, Issued(issuedBy, howMuch.token)),owner ?: myInfo.singleIdentity(), outputNotary) - - return@map issuerServices.signInitialTransaction(issuance, issuedBy.party.owningKey) + commodity.generateIssue(issuance, Amount(amount.quantity, Issued(issuedBy.copy(reference = ref), amount.token)), me, outputNotary) + val transaction = issuerServices.signInitialTransaction(issuance, issuedBy.party.owningKey) + services.recordTransactions(transaction) + return Vault(setOf(transaction.tx.outRef(0))) } - recordTransactions(transactions) - - // Get all the StateRefs of all the generated transactions. - val states = transactions.flatMap { stx -> - stx.tx.outputs.indices.map { i -> stx.tx.outRef(i) } - } - - return Vault(states) -} - -/** - * - * @param issuerServices service hub of the issuer node, which will be used to sign the transaction. - * @param outputNotary the notary to use for output states. The transaction is NOT signed by this notary. - * @return a vault object that represents the generated states (it will NOT be the full vault from the service hub!). - */ -// TODO: need to make all FungibleAsset commands (issue, move, exit) generic -fun ServiceHub.fillWithSomeTestCommodity(amount: Amount, - issuerServices: ServiceHub = this, - outputNotary: Party = DUMMY_NOTARY, - ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })), - ownedBy: AbstractParty? = null, - issuedBy: PartyAndReference = DUMMY_OBLIGATION_ISSUER.ref(1)): Vault { - val myKey: PublicKey = ownedBy?.owningKey ?: myInfo.chooseIdentity().owningKey - val me = AnonymousParty(myKey) - - val commodity = CommodityContract() - val issuance = TransactionBuilder(null as Party?) - commodity.generateIssue(issuance, Amount(amount.quantity, Issued(issuedBy.copy(reference = ref), amount.token)), me, outputNotary) - val transaction = issuerServices.signInitialTransaction(issuance, issuedBy.party.owningKey) - - recordTransactions(transaction) - - return Vault(setOf(transaction.tx.outRef(0))) -} - -fun calculateRandomlySizedAmounts(howMuch: Amount, min: Int, max: Int, rng: Random): LongArray { - val numSlots = min + Math.floor(rng.nextDouble() * (max - min)).toInt() - val baseSize = howMuch.quantity / numSlots - check(baseSize > 0) { baseSize } - - val amounts = LongArray(numSlots) { baseSize } - var distanceFromGoal = 0L - // If we want 10 slots then max adjust is 0.1, so even if all random numbers come out to the largest downward - // adjustment possible, the last slot ends at zero. With 20 slots, max adjust is 0.05 etc. - val maxAdjust = 1.0 / numSlots - for (i in amounts.indices) { - if (i != amounts.lastIndex) { - val adjustBy = rng.nextDouble() * maxAdjust - (maxAdjust / 2) - val adjustment = (1 + adjustBy) - val adjustTo = (amounts[i] * adjustment).toLong() - amounts[i] = adjustTo - distanceFromGoal += baseSize - adjustTo - } else { - amounts[i] += distanceFromGoal + fun consume(states: List>, notary: Party) { + // Create a txn consuming different contract types + states.forEach { + val builder = TransactionBuilder(notary = notary).apply { + addInputState(it) + addCommand(dummyCommand(notary.owningKey)) + } + val consumedTx = services.signInitialTransaction(builder, notary.owningKey) + services.recordTransactions(consumedTx) } } - // The desired amount may not have divided equally to start with, so adjust the first value to make up. - amounts[0] += howMuch.quantity - amounts.sum() - - return amounts -} - -fun ServiceHub.consume(states: List>, notary: Party) { - // Create a txn consuming different contract types - states.forEach { - val builder = TransactionBuilder(notary = notary).apply { - addInputState(it) + private fun consumeAndProduce(stateAndRef: StateAndRef, notary: Party): StateAndRef { + // Create a txn consuming different contract types + var builder = TransactionBuilder(notary = notary).apply { + addInputState(stateAndRef) addCommand(dummyCommand(notary.owningKey)) } - val consumedTx = signInitialTransaction(builder, notary.owningKey) + val consumedTx = services.signInitialTransaction(builder, notary.owningKey) + services.recordTransactions(consumedTx) + // Create a txn consuming different contract types + builder = TransactionBuilder(notary = notary).apply { + addOutputState(DummyLinearContract.State(linearId = stateAndRef.state.data.linearId, + participants = stateAndRef.state.data.participants), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) + addCommand(dummyCommand(notary.owningKey)) + } + val producedTx = services.signInitialTransaction(builder, notary.owningKey) + services.recordTransactions(producedTx) + return producedTx.tx.outRef(0) + } - recordTransactions(consumedTx) + private fun consumeAndProduce(states: List>, notary: Party) { + states.forEach { + consumeAndProduce(it, notary) + } + } + + fun consumeDeals(dealStates: List>, notary: Party) = consume(dealStates, notary) + fun consumeLinearStates(linearStates: List>, notary: Party) = consume(linearStates, notary) + fun evolveLinearStates(linearStates: List>, notary: Party) = consumeAndProduce(linearStates, notary) + fun evolveLinearState(linearState: StateAndRef, notary: Party): StateAndRef = consumeAndProduce(linearState, notary) + + /** + * Consume cash, sending any change to the default identity for this node. Only suitable for use in test scenarios, + * where nodes have a default identity. + */ + @JvmOverloads + fun consumeCash(amount: Amount, to: Party = CHARLIE, notary: Party): Vault.Update { + return consumeCash(amount, services.myInfo.chooseIdentityAndCert(), to, notary) + } + + /** + * Consume cash, sending any change to the specified identity. + */ + private fun consumeCash(amount: Amount, ourIdentity: PartyAndCertificate, to: Party = CHARLIE, notary: Party): Vault.Update { + val update = services.vaultService.rawUpdates.toFuture() + // A tx that spends our money. + val builder = TransactionBuilder(notary).apply { + Cash.generateSpend(services, this, amount, ourIdentity, to) + } + val spendTx = services.signInitialTransaction(builder, notary.owningKey) + services.recordTransactions(spendTx) + return update.getOrThrow(Duration.ofSeconds(3)) } } - -fun ServiceHub.consumeAndProduce(stateAndRef: StateAndRef, notary: Party): StateAndRef { - // Create a txn consuming different contract types - var builder = TransactionBuilder(notary = notary).apply { - addInputState(stateAndRef) - addCommand(dummyCommand(notary.owningKey)) - } - val consumedTx = signInitialTransaction(builder, notary.owningKey) - - recordTransactions(consumedTx) - - // Create a txn consuming different contract types - builder = TransactionBuilder(notary = notary).apply { - addOutputState(DummyLinearContract.State(linearId = stateAndRef.state.data.linearId, - participants = stateAndRef.state.data.participants), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) - addCommand(dummyCommand(notary.owningKey)) - } - val producedTx = signInitialTransaction(builder, notary.owningKey) - - recordTransactions(producedTx) - - return producedTx.tx.outRef(0) -} - -fun ServiceHub.consumeAndProduce(states: List>, notary: Party) { - states.forEach { - consumeAndProduce(it, notary) - } -} - -fun ServiceHub.consumeDeals(dealStates: List>, notary: Party) = consume(dealStates, notary) -fun ServiceHub.consumeLinearStates(linearStates: List>, notary: Party) = consume(linearStates, notary) -fun ServiceHub.evolveLinearStates(linearStates: List>, notary: Party) = consumeAndProduce(linearStates, notary) -fun ServiceHub.evolveLinearState(linearState: StateAndRef, notary: Party): StateAndRef = consumeAndProduce(linearState, notary) - -/** - * Consume cash, sending any change to the default identity for this node. Only suitable for use in test scenarios, - * where nodes have a default identity. - */ -@JvmOverloads -fun ServiceHub.consumeCash(amount: Amount, to: Party = CHARLIE, notary: Party): Vault.Update { - return consumeCash(amount, myInfo.chooseIdentityAndCert(), to, notary) -} - -/** - * Consume cash, sending any change to the specified identity. - */ -@JvmOverloads -fun ServiceHub.consumeCash(amount: Amount, ourIdentity: PartyAndCertificate, to: Party = CHARLIE, notary: Party): Vault.Update { - val update = vaultService.rawUpdates.toFuture() - val services = this - - // A tx that spends our money. - val builder = TransactionBuilder(notary).apply { - Cash.generateSpend(services, this, amount, ourIdentity, to) - } - val spendTx = signInitialTransaction(builder, notary.owningKey) - - recordTransactions(spendTx) - - return update.getOrThrow(Duration.ofSeconds(3)) -} From b45d9e957b241c1727b54d795792f59ffdfd87f7 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Wed, 29 Nov 2017 12:51:01 +0000 Subject: [PATCH 13/27] CORDA-654 Pass key constants into VaultFiller (#2118) --- .../finance/contracts/CommercialPaperTests.kt | 4 +- .../finance/contracts/asset/CashTests.kt | 14 +- .../services/vault/VaultQueryJavaTests.java | 66 ++- .../node/messaging/TwoPartyTradeFlowTests.kt | 9 +- .../persistence/HibernateConfigurationTest.kt | 57 +-- .../services/vault/NodeVaultServiceTest.kt | 40 +- .../node/services/vault/VaultQueryTests.kt | 397 +++++++++--------- .../node/services/vault/VaultWithCashTest.kt | 14 +- .../corda/testing/contracts/VaultFiller.kt | 148 +++---- 9 files changed, 350 insertions(+), 399 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 b64b9ccc88..e4056fe36c 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt @@ -240,7 +240,7 @@ class CommercialPaperTestsGeneric { aliceVaultService = aliceServices.vaultService databaseAlice.transaction { - alicesVault = VaultFiller(aliceServices).fillWithSomeTestCash(9000.DOLLARS, issuerServices, atLeastThisManyStates = 1, atMostThisManyStates = 1, issuedBy = DUMMY_CASH_ISSUER) + alicesVault = VaultFiller(aliceServices, DUMMY_NOTARY, DUMMY_NOTARY_KEY, rngFactory = ::Random).fillWithSomeTestCash(9000.DOLLARS, issuerServices, 1, DUMMY_CASH_ISSUER) aliceVaultService = aliceServices.vaultService } @@ -250,7 +250,7 @@ class CommercialPaperTestsGeneric { bigCorpVaultService = bigCorpServices.vaultService databaseBigCorp.transaction { - bigCorpVault = VaultFiller(bigCorpServices).fillWithSomeTestCash(13000.DOLLARS, issuerServices, atLeastThisManyStates = 1, atMostThisManyStates = 1, issuedBy = DUMMY_CASH_ISSUER) + bigCorpVault = VaultFiller(bigCorpServices, DUMMY_NOTARY, DUMMY_NOTARY_KEY, 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 86f15f5bfa..c3c17c6661 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 @@ -82,15 +82,11 @@ class CashTests { // Create some cash. Any attempt to spend >$500 will require multiple issuers to be involved. database.transaction { - val vaultFiller = VaultFiller(ourServices) - vaultFiller.fillWithSomeTestCash(howMuch = 100.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1, - owner = ourIdentity, issuedBy = MEGA_CORP.ref(1), issuerServices = megaCorpServices) - vaultFiller.fillWithSomeTestCash(howMuch = 400.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1, - owner = ourIdentity, issuedBy = MEGA_CORP.ref(1), issuerServices = megaCorpServices) - vaultFiller.fillWithSomeTestCash(howMuch = 80.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1, - owner = ourIdentity, issuedBy = MINI_CORP.ref(1), issuerServices = miniCorpServices) - vaultFiller.fillWithSomeTestCash(howMuch = 80.SWISS_FRANCS, atLeastThisManyStates = 1, atMostThisManyStates = 1, - owner = ourIdentity, issuedBy = MINI_CORP.ref(1), issuerServices = miniCorpServices) + val vaultFiller = VaultFiller(ourServices, DUMMY_NOTARY, DUMMY_NOTARY_KEY, 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) + vaultFiller.fillWithSomeTestCash(80.SWISS_FRANCS, miniCorpServices, 1, MINI_CORP.ref(1), ourIdentity) } database.transaction { vaultStatesUnconsumed = ourServices.vaultService.queryBy().states 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 4ade7ae074..be124c6bd0 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 @@ -18,13 +18,11 @@ import net.corda.core.utilities.EncodingUtils; import net.corda.core.utilities.OpaqueBytes; import net.corda.finance.contracts.DealState; import net.corda.finance.contracts.asset.Cash; -import net.corda.finance.contracts.asset.CashUtilities; import net.corda.finance.schemas.CashSchemaV1; import net.corda.node.services.identity.InMemoryIdentityService; import net.corda.node.utilities.CordaPersistence; import net.corda.node.utilities.DatabaseTransaction; import net.corda.testing.SerializationEnvironmentRule; -import net.corda.testing.TestConstants; import net.corda.testing.contracts.DummyLinearContract; import net.corda.testing.contracts.VaultFiller; import net.corda.testing.node.MockServices; @@ -74,7 +72,7 @@ public class VaultQueryJavaTests { issuerServices = new MockServices(cordappPackages, getDUMMY_CASH_ISSUER_NAME(), getDUMMY_CASH_ISSUER_KEY(), getBOC_KEY()); database = databaseAndServices.getFirst(); MockServices services = databaseAndServices.getSecond(); - vaultFiller = new VaultFiller(services); + vaultFiller = new VaultFiller(services, getDUMMY_NOTARY(), getDUMMY_NOTARY_KEY()); vaultService = services.getVaultService(); } @@ -139,18 +137,16 @@ public class VaultQueryJavaTests { Amount amount = new Amount<>(100, Currency.getInstance("USD")); database.transaction(tx -> { vaultFiller.fillWithSomeTestCash( - new Amount(100, Currency.getInstance("USD")), + new Amount<>(100, Currency.getInstance("USD")), issuerServices, - TestConstants.getDUMMY_NOTARY(), 3, - 3, - new Random(), + getDUMMY_CASH_ISSUER(), null, - CashUtilities.getDUMMY_CASH_ISSUER()); + new Random()); return tx; }); database.transaction(tx -> { - vaultFiller.consumeCash(amount, getDUMMY_NOTARY()); + vaultFiller.consumeCash(amount, getCHARLIE()); return tx; }); database.transaction(tx -> { @@ -179,8 +175,8 @@ public class VaultQueryJavaTests { }); database.transaction(tx -> { // consume states - vaultFiller.consumeDeals((List>) ids.getThird().getStates(), getDUMMY_NOTARY()); - vaultFiller.consumeLinearStates(Collections.singletonList(ids.getFirst()), getDUMMY_NOTARY()); + vaultFiller.consumeDeals((List>) ids.getThird().getStates()); + vaultFiller.consumeLinearStates(Collections.singletonList(ids.getFirst())); return tx; }); database.transaction(tx -> { @@ -219,10 +215,10 @@ public class VaultQueryJavaTests { Amount dollars100 = new Amount<>(100, Currency.getInstance("USD")); Amount dollars10 = new Amount<>(10, Currency.getInstance("USD")); Amount dollars1 = new Amount<>(1, Currency.getInstance("USD")); - vaultFiller.fillWithSomeTestCash(pounds, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); - vaultFiller.fillWithSomeTestCash(dollars100, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); - vaultFiller.fillWithSomeTestCash(dollars10, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); - vaultFiller.fillWithSomeTestCash(dollars1, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(pounds, issuerServices, 1, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars100, issuerServices, 1, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars10, issuerServices, 1, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars1, issuerServices, 1, getDUMMY_CASH_ISSUER()); return tx; }); database.transaction(tx -> { @@ -262,12 +258,10 @@ public class VaultQueryJavaTests { vaultFiller.fillWithSomeTestCash( new Amount<>(100, Currency.getInstance("USD")), issuerServices, - TestConstants.getDUMMY_NOTARY(), 3, - 3, - new Random(), + getDUMMY_CASH_ISSUER(), null, - getDUMMY_CASH_ISSUER()); + new Random()); return tx; }); database.transaction(tx -> { @@ -339,11 +333,11 @@ public class VaultQueryJavaTests { Amount dollars300 = new Amount<>(300, Currency.getInstance("USD")); Amount pounds = new Amount<>(400, Currency.getInstance("GBP")); Amount swissfrancs = new Amount<>(500, Currency.getInstance("CHF")); - vaultFiller.fillWithSomeTestCash(dollars100, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); - vaultFiller.fillWithSomeTestCash(dollars200, issuerServices, TestConstants.getDUMMY_NOTARY(), 2, 2, new Random(0L), null, getDUMMY_CASH_ISSUER()); - vaultFiller.fillWithSomeTestCash(dollars300, issuerServices, TestConstants.getDUMMY_NOTARY(), 3, 3, new Random(0L), null, getDUMMY_CASH_ISSUER()); - vaultFiller.fillWithSomeTestCash(pounds, issuerServices, TestConstants.getDUMMY_NOTARY(), 4, 4, new Random(0L), null, getDUMMY_CASH_ISSUER()); - vaultFiller.fillWithSomeTestCash(swissfrancs, issuerServices, TestConstants.getDUMMY_NOTARY(), 5, 5, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars100, issuerServices, 1, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars200, issuerServices, 2, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars300, issuerServices, 3, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(pounds, issuerServices, 4, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(swissfrancs, issuerServices, 5, getDUMMY_CASH_ISSUER()); return tx; }); database.transaction(tx -> { @@ -385,11 +379,11 @@ public class VaultQueryJavaTests { Amount dollars300 = new Amount<>(300, Currency.getInstance("USD")); Amount pounds = new Amount<>(400, Currency.getInstance("GBP")); Amount swissfrancs = new Amount<>(500, Currency.getInstance("CHF")); - vaultFiller.fillWithSomeTestCash(dollars100, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); - vaultFiller.fillWithSomeTestCash(dollars200, issuerServices, TestConstants.getDUMMY_NOTARY(), 2, 2, new Random(0L), null, getDUMMY_CASH_ISSUER()); - vaultFiller.fillWithSomeTestCash(dollars300, issuerServices, TestConstants.getDUMMY_NOTARY(), 3, 3, new Random(0L), null, getDUMMY_CASH_ISSUER()); - vaultFiller.fillWithSomeTestCash(pounds, issuerServices, TestConstants.getDUMMY_NOTARY(), 4, 4, new Random(0L), null, getDUMMY_CASH_ISSUER()); - vaultFiller.fillWithSomeTestCash(swissfrancs, issuerServices, TestConstants.getDUMMY_NOTARY(), 5, 5, new Random(0L), null, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars100, issuerServices, 1, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars200, issuerServices, 2, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars300, issuerServices, 3, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(pounds, issuerServices, 4, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(swissfrancs, issuerServices, 5, getDUMMY_CASH_ISSUER()); return tx; }); database.transaction(tx -> { @@ -409,21 +403,21 @@ public class VaultQueryJavaTests { // DOCEND VaultJavaQueryExample22 assertThat(results.getOtherResults()).hasSize(18); - /** CHF */ + /* CHF */ assertThat(results.getOtherResults().get(0)).isEqualTo(500L); assertThat(results.getOtherResults().get(1)).isEqualTo(5L); assertThat(results.getOtherResults().get(2)).isEqualTo(102L); assertThat(results.getOtherResults().get(3)).isEqualTo(94L); assertThat(results.getOtherResults().get(4)).isEqualTo(100.00); assertThat(results.getOtherResults().get(5)).isEqualTo("CHF"); - /** GBP */ + /* GBP */ assertThat(results.getOtherResults().get(6)).isEqualTo(400L); assertThat(results.getOtherResults().get(7)).isEqualTo(4L); assertThat(results.getOtherResults().get(8)).isEqualTo(103L); assertThat(results.getOtherResults().get(9)).isEqualTo(93L); assertThat(results.getOtherResults().get(10)).isEqualTo(100.0); assertThat(results.getOtherResults().get(11)).isEqualTo("GBP"); - /** USD */ + /* USD */ assertThat(results.getOtherResults().get(12)).isEqualTo(600L); assertThat(results.getOtherResults().get(13)).isEqualTo(6L); assertThat(results.getOtherResults().get(14)).isEqualTo(113L); @@ -446,10 +440,10 @@ public class VaultQueryJavaTests { Amount dollars200 = new Amount<>(200, Currency.getInstance("USD")); Amount pounds300 = new Amount<>(300, Currency.getInstance("GBP")); Amount pounds400 = new Amount<>(400, Currency.getInstance("GBP")); - vaultFiller.fillWithSomeTestCash(dollars100, issuerServices, TestConstants.getDUMMY_NOTARY(), 1, 1, new Random(0L), null, getDUMMY_CASH_ISSUER()); - vaultFiller.fillWithSomeTestCash(dollars200, issuerServices, TestConstants.getDUMMY_NOTARY(), 2, 2, new Random(0L), null, getBOC().ref(new OpaqueBytes("1".getBytes()))); - vaultFiller.fillWithSomeTestCash(pounds300, issuerServices, TestConstants.getDUMMY_NOTARY(), 3, 3, new Random(0L), null, getDUMMY_CASH_ISSUER()); - vaultFiller.fillWithSomeTestCash(pounds400, issuerServices, TestConstants.getDUMMY_NOTARY(), 4, 4, new Random(0L), null, getBOC().ref(new OpaqueBytes("1".getBytes()))); + vaultFiller.fillWithSomeTestCash(dollars100, issuerServices, 1, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(dollars200, issuerServices, 2, getBOC().ref(new OpaqueBytes("1".getBytes()))); + vaultFiller.fillWithSomeTestCash(pounds300, issuerServices, 3, getDUMMY_CASH_ISSUER()); + vaultFiller.fillWithSomeTestCash(pounds400, issuerServices, 4, getBOC().ref(new OpaqueBytes("1".getBytes()))); return tx; }); database.transaction(tx -> { 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 16b2947647..7696190b3c 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -106,8 +106,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { bobNode.internals.disableDBCloseOnStop() bobNode.database.transaction { - VaultFiller(bobNode.services).fillWithSomeTestCash(2000.DOLLARS, bankNode.services, outputNotary = notary, - issuedBy = cashIssuer) + VaultFiller(bobNode.services, DUMMY_NOTARY, DUMMY_NOTARY_KEY, notary, ::Random).fillWithSomeTestCash(2000.DOLLARS, bankNode.services, 3, 10, cashIssuer) } val alicesFakePaper = aliceNode.database.transaction { @@ -156,8 +155,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { bobNode.internals.disableDBCloseOnStop() val cashStates = bobNode.database.transaction { - VaultFiller(bobNode.services).fillWithSomeTestCash(2000.DOLLARS, bankNode.services, notary, 3, 3, - issuedBy = issuer) + VaultFiller(bobNode.services, DUMMY_NOTARY, DUMMY_NOTARY_KEY, notary, ::Random).fillWithSomeTestCash(2000.DOLLARS, bankNode.services, 3, issuer) } val alicesFakePaper = aliceNode.database.transaction { @@ -216,8 +214,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { val issuer = bank.ref(1, 2, 3) bobNode.database.transaction { - VaultFiller(bobNode.services).fillWithSomeTestCash(2000.DOLLARS, bankNode.services, outputNotary = notary, - issuedBy = issuer) + VaultFiller(bobNode.services, DUMMY_NOTARY, DUMMY_NOTARY_KEY, 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/persistence/HibernateConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt index 8a399b394b..3709de0e4b 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 @@ -3,6 +3,7 @@ package net.corda.node.services.persistence import com.nhaarman.mockito_kotlin.any import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever +import net.corda.core.contracts.Amount import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateRef import net.corda.core.contracts.TransactionState @@ -82,6 +83,7 @@ class HibernateConfigurationTest { bankServices = MockServices(cordappPackages, BOC.name, BOC_KEY) issuerServices = MockServices(cordappPackages, DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY) notaryServices = MockServices(cordappPackages, DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) + notary = notaryServices.myInfo.singleIdentity() val dataSourceProps = makeTestDataSourceProperties() val identityService = rigorousMock().also { mock -> doReturn(null).whenever(mock).wellKnownPartyFromAnonymous(any()) @@ -106,18 +108,15 @@ class HibernateConfigurationTest { override fun jdbcSession() = database.createSession() } - vaultFiller = VaultFiller(services) + vaultFiller = VaultFiller(services, DUMMY_NOTARY, DUMMY_NOTARY_KEY, notary, ::Random) hibernatePersister = services.hibernatePersister } identity = services.myInfo.singleIdentity() issuer = issuerServices.myInfo.singleIdentity() - notary = notaryServices.myInfo.singleIdentity() - database.transaction { val numStates = 10 - cashStates = vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, numStates, numStates, Random(0L), issuedBy = issuer.ref(1)) - .states.toList() + cashStates = vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, numStates, issuer.ref(1), rng = Random(0L)).states.toList() } sessionFactory = sessionFactoryForSchemas(VaultSchemaV1, CashSchemaV1, SampleCashSchemaV2, SampleCashSchemaV3) @@ -126,7 +125,7 @@ class HibernateConfigurationTest { } private fun sessionFactoryForSchemas(vararg schemas: MappedSchema) = hibernateConfig.sessionFactoryForSchemas(schemas.toSet()) - + private fun consumeCash(amount: Amount) = vaultFiller.consumeCash(amount, CHARLIE) @After fun cleanUp() { database.close() @@ -147,7 +146,7 @@ class HibernateConfigurationTest { @Test fun `consumed states`() { database.transaction { - vaultFiller.consumeCash(50.DOLLARS, notary = notary) + consumeCash(50.DOLLARS) } // structure query @@ -228,11 +227,7 @@ class HibernateConfigurationTest { fun `with sorting by state ref desc and asc`() { // generate additional state ref indexes database.transaction { - vaultFiller.consumeCash(1.DOLLARS, notary = notary) - vaultFiller.consumeCash(2.DOLLARS, notary = notary) - vaultFiller.consumeCash(3.DOLLARS, notary = notary) - vaultFiller.consumeCash(4.DOLLARS, notary = notary) - vaultFiller.consumeCash(5.DOLLARS, notary = notary) + (1..5).forEach { consumeCash(it.DOLLARS) } } // structure query @@ -258,11 +253,7 @@ class HibernateConfigurationTest { fun `with sorting by state ref index and txId desc and asc`() { // generate additional state ref indexes database.transaction { - vaultFiller.consumeCash(1.DOLLARS, notary = notary) - vaultFiller.consumeCash(2.DOLLARS, notary = notary) - vaultFiller.consumeCash(3.DOLLARS, notary = notary) - vaultFiller.consumeCash(4.DOLLARS, notary = notary) - vaultFiller.consumeCash(5.DOLLARS, notary = notary) + (1..5).forEach { consumeCash(it.DOLLARS) } } // structure query @@ -289,7 +280,7 @@ class HibernateConfigurationTest { fun `with pagination`() { // add 100 additional cash entries database.transaction { - vaultFiller.fillWithSomeTestCash(1000.POUNDS, issuerServices, notary, 100, 100, Random(0L), issuedBy = issuer.ref(1)) + vaultFiller.fillWithSomeTestCash(1000.POUNDS, issuerServices, 100, issuer.ref(1), rng = Random(0L)) } // structure query @@ -389,11 +380,11 @@ class HibernateConfigurationTest { @Test fun `calculate cash balances`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, 10, issuer.ref(1)) // +$100 = $200 - vaultFiller.fillWithSomeTestCash(50.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // £50 = £50 - vaultFiller.fillWithSomeTestCash(25.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // +£25 = £175 - vaultFiller.fillWithSomeTestCash(500.SWISS_FRANCS, issuerServices, notary, 10, issuer.ref(1)) // CHF500 = CHF500 - vaultFiller.fillWithSomeTestCash(250.SWISS_FRANCS, issuerServices, notary, 5, issuer.ref(1)) // +CHF250 = CHF750 + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 10, issuer.ref(1)) // +$100 = $200 + vaultFiller.fillWithSomeTestCash(50.POUNDS, issuerServices, 5, issuer.ref(1)) // £50 = £50 + vaultFiller.fillWithSomeTestCash(25.POUNDS, issuerServices, 5, issuer.ref(1)) // +£25 = £175 + vaultFiller.fillWithSomeTestCash(500.SWISS_FRANCS, issuerServices, 10, issuer.ref(1)) // CHF500 = CHF500 + vaultFiller.fillWithSomeTestCash(250.SWISS_FRANCS, issuerServices, 5, issuer.ref(1)) // +CHF250 = CHF750 } // structure query @@ -422,8 +413,8 @@ class HibernateConfigurationTest { @Test fun `calculate cash balance for single currency`() { database.transaction { - vaultFiller.fillWithSomeTestCash(50.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // £50 = £50 - vaultFiller.fillWithSomeTestCash(25.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // +£25 = £175 + vaultFiller.fillWithSomeTestCash(50.POUNDS, issuerServices, 5, issuer.ref(1)) // £50 = £50 + vaultFiller.fillWithSomeTestCash(25.POUNDS, issuerServices, 5, issuer.ref(1)) // +£25 = £175 } // structure query @@ -453,9 +444,9 @@ class HibernateConfigurationTest { fun `calculate and order by cash balance for owner and currency`() { database.transaction { val bank = bankServices.myInfo.legalIdentities.single() - vaultFiller.fillWithSomeTestCash(200.DOLLARS, bankServices, notary, 2, bank.ref(1)) - vaultFiller.fillWithSomeTestCash(300.POUNDS, issuerServices, notary, 3, issuer.ref(1)) - vaultFiller.fillWithSomeTestCash(400.POUNDS, bankServices, notary, 4, bank.ref(2)) + vaultFiller.fillWithSomeTestCash(200.DOLLARS, bankServices, 2, bank.ref(1)) + vaultFiller.fillWithSomeTestCash(300.POUNDS, issuerServices, 3, issuer.ref(1)) + vaultFiller.fillWithSomeTestCash(400.POUNDS, bankServices, 4, bank.ref(2)) } // structure query @@ -640,9 +631,8 @@ class HibernateConfigurationTest { val dummyFungibleState = DummyFungibleContract.State(cashState.amount, cashState.owner) hibernatePersister.persistStateWithSchema(dummyFungibleState, it.ref, SampleCashSchemaV3) } - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, 2, 2, Random(0L), - issuedBy = issuer.ref(1), owner = ALICE) - val cashStates = vaultFiller.fillWithSomeTestCash(100.DOLLARS, services, notary, 2, identity.ref(0)).states + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 2, issuer.ref(1), ALICE, Random(0L)) + val cashStates = vaultFiller.fillWithSomeTestCash(100.DOLLARS, services, 2, identity.ref(0)).states // persist additional cash states explicitly with V3 schema cashStates.forEach { val cashState = it.state.data @@ -718,15 +708,14 @@ class HibernateConfigurationTest { val dummyFungibleState = DummyFungibleContract.State(cashState.amount, cashState.owner) hibernatePersister.persistStateWithSchema(dummyFungibleState, it.ref, SampleCashSchemaV3) } - val moreCash = vaultFiller.fillWithSomeTestCash(100.DOLLARS, services, notary, 2, 2, Random(0L), - issuedBy = identity.ref(0), owner = identity).states + val moreCash = vaultFiller.fillWithSomeTestCash(100.DOLLARS, services, 2, identity.ref(0), identity, Random(0L)).states // persist additional cash states explicitly with V3 schema moreCash.forEach { val cashState = it.state.data val dummyFungibleState = DummyFungibleContract.State(cashState.amount, cashState.owner) hibernatePersister.persistStateWithSchema(dummyFungibleState, it.ref, SampleCashSchemaV3) } - val cashStates = vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, 2, 2, Random(0L), owner = ALICE, issuedBy = issuer.ref(1)).states + val cashStates = vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 2, issuer.ref(1), ALICE, Random(0L)).states // persist additional cash states explicitly with V3 schema cashStates.forEach { val cashState = it.state.data 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 c24f563b85..4ffdfd05d1 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 @@ -75,7 +75,7 @@ class NodeVaultServiceTest { val databaseAndServices = MockServices.makeTestDatabaseAndMockServices(cordappPackages = cordappPackages) database = databaseAndServices.first services = databaseAndServices.second - vaultFiller = VaultFiller(services) + 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, DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY) @@ -113,7 +113,7 @@ class NodeVaultServiceTest { @Test fun `states not local to instance`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 3, DUMMY_CASH_ISSUER) } database.transaction { val w1 = vaultService.queryBy().states @@ -138,7 +138,7 @@ class NodeVaultServiceTest { @Test fun `states for refs`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 3, DUMMY_CASH_ISSUER) } database.transaction { val w1 = vaultService.queryBy().states @@ -152,7 +152,7 @@ class NodeVaultServiceTest { @Test fun `states soft locking reserve and release`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 3, DUMMY_CASH_ISSUER) } database.transaction { @@ -205,7 +205,7 @@ class NodeVaultServiceTest { val vaultStates = database.transaction { assertEquals(0.DOLLARS, services.getCashBalance(USD)) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 3, DUMMY_CASH_ISSUER) } val stateRefsToSoftLock = (vaultStates.states.map { it.ref }).toNonEmptySet() println("State Refs:: $stateRefsToSoftLock") @@ -260,7 +260,7 @@ class NodeVaultServiceTest { val vaultStates = database.transaction { assertEquals(0.DOLLARS, services.getCashBalance(USD)) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 3, DUMMY_CASH_ISSUER) } val stateRefsToSoftLock = vaultStates.states.map { it.ref } println("State Refs:: $stateRefsToSoftLock") @@ -288,7 +288,7 @@ class NodeVaultServiceTest { val vaultStates = database.transaction { assertEquals(0.DOLLARS, services.getCashBalance(USD)) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 3, DUMMY_CASH_ISSUER) } val stateRefsToSoftLock = (vaultStates.states.map { it.ref }).toNonEmptySet() println("State Refs:: $stateRefsToSoftLock") @@ -315,7 +315,7 @@ class NodeVaultServiceTest { val vaultStates = database.transaction { assertEquals(0.DOLLARS, services.getCashBalance(USD)) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 3, DUMMY_CASH_ISSUER) } val stateRefsToSoftLock = vaultStates.states.map { it.ref } println("State Refs:: $stateRefsToSoftLock") @@ -336,7 +336,7 @@ class NodeVaultServiceTest { @Test fun `unconsumedStatesForSpending exact amount`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 1, DUMMY_CASH_ISSUER) } database.transaction { @@ -355,8 +355,8 @@ class NodeVaultServiceTest { @Test fun `unconsumedStatesForSpending from two issuer parties`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = DUMMY_CASH_ISSUER) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, bocServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(1)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 1, DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, bocServices, 1, BOC.ref(1)) } database.transaction { val spendableStatesUSD = vaultService.unconsumedCashStatesForSpending(200.DOLLARS, @@ -372,10 +372,10 @@ class NodeVaultServiceTest { @Test fun `unconsumedStatesForSpending from specific issuer party and refs`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = DUMMY_CASH_ISSUER) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, bocServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(1)) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, bocServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(2)) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, bocServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(3)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 1, DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, bocServices, 1, BOC.ref(1)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, bocServices, 1, BOC.ref(2)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, bocServices, 1, BOC.ref(3)) } database.transaction { val unconsumedStates = vaultService.queryBy().states @@ -394,7 +394,7 @@ class NodeVaultServiceTest { @Test fun `unconsumedStatesForSpending insufficient amount`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 1, DUMMY_CASH_ISSUER) } database.transaction { val unconsumedStates = vaultService.queryBy().states @@ -411,7 +411,7 @@ class NodeVaultServiceTest { @Test fun `unconsumedStatesForSpending small amount`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 2, DUMMY_CASH_ISSUER) } database.transaction { val unconsumedStates = vaultService.queryBy().states @@ -429,9 +429,9 @@ class NodeVaultServiceTest { @Test fun `states soft locking query granularity`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.POUNDS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L)) + listOf(USD, GBP, CHF).forEach { + vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), issuerServices, 10, DUMMY_CASH_ISSUER) + } } database.transaction { var unlockedStates = 30 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 20ea02690f..6586686921 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 @@ -23,6 +23,7 @@ import net.corda.finance.contracts.DealState import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY +import net.corda.finance.contracts.asset.DUMMY_OBLIGATION_ISSUER import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.schemas.CashSchemaV1.PersistentCashState import net.corda.finance.schemas.CommercialPaperSchemaV1 @@ -64,6 +65,7 @@ class VaultQueryTests { DummyLinearStateSchemaV1::class.packageName) private lateinit var services: MockServices private lateinit var vaultFiller: VaultFiller + private lateinit var vaultFillerCashNotary: VaultFiller private lateinit var notaryServices: MockServices private val vaultService: VaultService get() = services.vaultService private lateinit var identitySvc: IdentityService @@ -81,7 +83,8 @@ class VaultQueryTests { cordappPackages = cordappPackages) database = databaseAndServices.first services = databaseAndServices.second - vaultFiller = VaultFiller(services) + vaultFiller = VaultFiller(services, DUMMY_NOTARY, DUMMY_NOTARY_KEY) + vaultFillerCashNotary = VaultFiller(services, DUMMY_NOTARY, DUMMY_NOTARY_KEY, CASH_NOTARY) notaryServices = MockServices(cordappPackages, DUMMY_NOTARY.name, DUMMY_NOTARY_KEY, DUMMY_CASH_ISSUER_KEY, BOC_KEY, MEGA_CORP_KEY) identitySvc = services.identityService // Register all of the identities we're going to use @@ -107,10 +110,11 @@ class VaultQueryTests { database.close() } + private fun consumeCash(amount: Amount) = vaultFiller.consumeCash(amount, CHARLIE) private fun setUpDb(_database: CordaPersistence, delay: Long = 0) { _database.transaction { // create new states - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 10, 10, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 10, DUMMY_CASH_ISSUER) val linearStatesXYZ = vaultFiller.fillWithSomeTestLinearStates(1, "XYZ") val linearStatesJKL = vaultFiller.fillWithSomeTestLinearStates(2, "JKL") vaultFiller.fillWithSomeTestLinearStates(3, "ABC") @@ -119,10 +123,10 @@ class VaultQueryTests { sleep(delay) // consume some states - vaultFiller.consumeLinearStates(linearStatesXYZ.states.toList(), DUMMY_NOTARY) - vaultFiller.consumeLinearStates(linearStatesJKL.states.toList(), DUMMY_NOTARY) - vaultFiller.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }, DUMMY_NOTARY) - vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) + vaultFiller.consumeLinearStates(linearStatesXYZ.states.toList()) + vaultFiller.consumeLinearStates(linearStatesJKL.states.toList()) + vaultFiller.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }) + consumeCash(50.DOLLARS) // Total unconsumed states = 4 + 3 + 2 + 1 (new cash change) = 10 // Total consumed states = 6 + 1 + 2 + 1 = 10 } @@ -147,7 +151,7 @@ class VaultQueryTests { @Test fun `unconsumed states simple`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) vaultFiller.fillWithSomeTestLinearStates(10) vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) // DOCSTART VaultQueryExample1 @@ -172,7 +176,7 @@ class VaultQueryTests { @Test fun `unconsumed states verbose`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) vaultFiller.fillWithSomeTestLinearStates(10) vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) val criteria = VaultQueryCriteria() // default is UNCONSUMED @@ -186,16 +190,15 @@ class VaultQueryTests { @Test fun `unconsumed states with count`() { database.transaction { - vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + repeat(4) { + vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, 1, DUMMY_CASH_ISSUER) + } val paging = PageSpecification(DEFAULT_PAGE_NUM, 10) val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) val resultsBeforeConsume = vaultService.queryBy(criteria, paging) assertThat(resultsBeforeConsume.states).hasSize(4) assertThat(resultsBeforeConsume.totalStatesAvailable).isEqualTo(4) - vaultFiller.consumeCash(75.DOLLARS, notary = DUMMY_NOTARY) + consumeCash(75.DOLLARS) val consumedCriteria = VaultQueryCriteria(status = Vault.StateStatus.UNCONSUMED) val resultsAfterConsume = vaultService.queryBy(consumedCriteria, paging) assertThat(resultsAfterConsume.states).hasSize(1) @@ -206,7 +209,7 @@ class VaultQueryTests { @Test fun `unconsumed cash states simple`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) vaultFiller.fillWithSomeTestLinearStates(10) vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) val result = vaultService.queryBy() @@ -219,7 +222,7 @@ class VaultQueryTests { @Test fun `unconsumed cash states verbose`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) vaultFiller.fillWithSomeTestLinearStates(10) vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) val criteria = VaultQueryCriteria() // default is UNCONSUMED @@ -234,12 +237,12 @@ class VaultQueryTests { fun `unconsumed cash states sorted by state ref`() { val stateRefs: MutableList = mutableListOf() database.transaction { - val issuedStates = vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 10, 10, Random(0L)) + val issuedStates = vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 10, DUMMY_CASH_ISSUER) val issuedStateRefs = issuedStates.states.map { it.ref }.toList() stateRefs.addAll(issuedStateRefs) } database.transaction { - val spentStates = vaultFiller.consumeCash(25.DOLLARS, notary = DUMMY_NOTARY) + val spentStates = consumeCash(25.DOLLARS) val consumedStateRefs = spentStates.consumed.map { it.ref }.toList() val producedStateRefs = spentStates.produced.map { it.ref }.toList() stateRefs.addAll(consumedStateRefs.plus(producedStateRefs)) @@ -265,11 +268,11 @@ class VaultQueryTests { fun `unconsumed cash states sorted by state ref txnId and index`() { val consumed = mutableSetOf() database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 10, 10, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 10, DUMMY_CASH_ISSUER) } database.transaction { - vaultFiller.consumeCash(10.DOLLARS, notary = DUMMY_NOTARY).consumed.forEach { consumed += it.ref.txhash } - vaultFiller.consumeCash(10.DOLLARS, notary = DUMMY_NOTARY).consumed.forEach { consumed += it.ref.txhash } + consumeCash(10.DOLLARS).consumed.forEach { consumed += it.ref.txhash } + consumeCash(10.DOLLARS).consumed.forEach { consumed += it.ref.txhash } val sortAttributeTxnId = SortAttribute.Standard(Sort.CommonStateAttribute.STATE_REF_TXN_ID) val sortAttributeIndex = SortAttribute.Standard(Sort.CommonStateAttribute.STATE_REF_INDEX) val sortBy = Sort(setOf(Sort.SortColumn(sortAttributeTxnId, Sort.Direction.ASC), @@ -311,7 +314,7 @@ class VaultQueryTests { @Test fun `unconsumed states for contract state types`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) vaultFiller.fillWithSomeTestLinearStates(10) vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) // default State.Status is UNCONSUMED @@ -326,13 +329,13 @@ class VaultQueryTests { @Test fun `consumed states`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) val linearStates = vaultFiller.fillWithSomeTestLinearStates(2, "TEST") // create 2 states with same externalId vaultFiller.fillWithSomeTestLinearStates(8) val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) - vaultFiller.consumeLinearStates(linearStates.states.toList(), DUMMY_NOTARY) - vaultFiller.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }, DUMMY_NOTARY) - vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) + vaultFiller.consumeLinearStates(linearStates.states.toList()) + vaultFiller.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }) + consumeCash(50.DOLLARS) val criteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED) val results = vaultService.queryBy(criteria) assertThat(results.states).hasSize(5) @@ -342,16 +345,15 @@ class VaultQueryTests { @Test fun `consumed states with count`() { database.transaction { - vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + repeat(4) { + vaultFiller.fillWithSomeTestCash(25.DOLLARS, notaryServices, 1, DUMMY_CASH_ISSUER) + } val paging = PageSpecification(DEFAULT_PAGE_NUM, 10) val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) val resultsBeforeConsume = vaultService.queryBy(criteria, paging) assertThat(resultsBeforeConsume.states).hasSize(4) assertThat(resultsBeforeConsume.totalStatesAvailable).isEqualTo(4) - vaultFiller.consumeCash(75.DOLLARS, notary = DUMMY_NOTARY) + consumeCash(75.DOLLARS) val consumedCriteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED) val resultsAfterConsume = vaultService.queryBy(consumedCriteria, paging) assertThat(resultsAfterConsume.states).hasSize(3) @@ -362,13 +364,13 @@ class VaultQueryTests { @Test fun `all states`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) val linearStates = vaultFiller.fillWithSomeTestLinearStates(2, "TEST") // create 2 results with same UID vaultFiller.fillWithSomeTestLinearStates(8) val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) - vaultFiller.consumeLinearStates(linearStates.states.toList(), DUMMY_NOTARY) - vaultFiller.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }, DUMMY_NOTARY) - vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) // generates a new change state! + vaultFiller.consumeLinearStates(linearStates.states.toList()) + vaultFiller.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }) + consumeCash(50.DOLLARS) // generates a new change state! val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) val results = vaultService.queryBy(criteria) assertThat(results.states).hasSize(17) @@ -378,14 +380,14 @@ class VaultQueryTests { @Test fun `all states with count`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 1, DUMMY_CASH_ISSUER) val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) val paging = PageSpecification(DEFAULT_PAGE_NUM, 10) val resultsBeforeConsume = vaultService.queryBy(criteria, paging) assertThat(resultsBeforeConsume.states).hasSize(1) assertThat(resultsBeforeConsume.totalStatesAvailable).isEqualTo(1) - vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) // consumed 100 (spent), produced 50 (change) + consumeCash(50.DOLLARS) // consumed 100 (spent), produced 50 (change) val resultsAfterConsume = vaultService.queryBy(criteria, paging) assertThat(resultsAfterConsume.states).hasSize(2) assertThat(resultsAfterConsume.totalStatesAvailable).isEqualTo(2) @@ -395,7 +397,7 @@ class VaultQueryTests { @Test fun `unconsumed states by notary`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 3, 3, Random(0L)) + vaultFillerCashNotary.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) vaultFiller.fillWithSomeTestLinearStates(10) vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) // DOCSTART VaultQueryExample4 @@ -438,7 +440,7 @@ class VaultQueryTests { @Test fun `unconsumed states with soft locking`() { database.transaction { - val issuedStates = vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 10, 10, Random(0L)).states.toList() + val issuedStates = vaultFillerCashNotary.fillWithSomeTestCash(100.DOLLARS, notaryServices, 10, DUMMY_CASH_ISSUER).states.toList() vaultService.softLockReserve(UUID.randomUUID(), NonEmptySet.of(issuedStates[1].ref, issuedStates[2].ref, issuedStates[3].ref)) val lockId1 = UUID.randomUUID() vaultService.softLockReserve(lockId1, NonEmptySet.of(issuedStates[4].ref, issuedStates[5].ref)) @@ -481,9 +483,9 @@ class VaultQueryTests { @Test fun `logical operator EQUAL`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + listOf(USD, GBP, CHF).forEach { + vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER) + } val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.equal(GBP.currencyCode) } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -494,9 +496,9 @@ class VaultQueryTests { @Test fun `logical operator NOT EQUAL`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + listOf(USD, GBP, CHF).forEach { + vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER) + } val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.notEqual(GBP.currencyCode) } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -507,9 +509,9 @@ class VaultQueryTests { @Test fun `logical operator GREATER_THAN`() { database.transaction { - vaultFiller.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(10.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + listOf(1.DOLLARS, 10.POUNDS, 100.SWISS_FRANCS).forEach { + vaultFiller.fillWithSomeTestCash(it, notaryServices, 1, DUMMY_CASH_ISSUER) + } val logicalExpression = builder { CashSchemaV1.PersistentCashState::pennies.greaterThan(1000L) } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -520,9 +522,9 @@ class VaultQueryTests { @Test fun `logical operator GREATER_THAN_OR_EQUAL`() { database.transaction { - vaultFiller.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(10.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + listOf(1.DOLLARS, 10.POUNDS, 100.SWISS_FRANCS).forEach { + vaultFiller.fillWithSomeTestCash(it, notaryServices, 1, DUMMY_CASH_ISSUER) + } val logicalExpression = builder { CashSchemaV1.PersistentCashState::pennies.greaterThanOrEqual(1000L) } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -533,9 +535,9 @@ class VaultQueryTests { @Test fun `logical operator LESS_THAN`() { database.transaction { - vaultFiller.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(10.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + listOf(1.DOLLARS, 10.POUNDS, 100.SWISS_FRANCS).forEach { + vaultFiller.fillWithSomeTestCash(it, notaryServices, 1, DUMMY_CASH_ISSUER) + } val logicalExpression = builder { CashSchemaV1.PersistentCashState::pennies.lessThan(1000L) } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -546,9 +548,9 @@ class VaultQueryTests { @Test fun `logical operator LESS_THAN_OR_EQUAL`() { database.transaction { - vaultFiller.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(10.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + listOf(1.DOLLARS, 10.POUNDS, 100.SWISS_FRANCS).forEach { + vaultFiller.fillWithSomeTestCash(it, notaryServices, 1, DUMMY_CASH_ISSUER) + } val logicalExpression = builder { CashSchemaV1.PersistentCashState::pennies.lessThanOrEqual(1000L) } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -559,9 +561,9 @@ class VaultQueryTests { @Test fun `logical operator BETWEEN`() { database.transaction { - vaultFiller.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(10.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + listOf(1.DOLLARS, 10.POUNDS, 100.SWISS_FRANCS).forEach { + vaultFiller.fillWithSomeTestCash(it, notaryServices, 1, DUMMY_CASH_ISSUER) + } val logicalExpression = builder { CashSchemaV1.PersistentCashState::pennies.between(500L, 1500L) } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -572,9 +574,9 @@ class VaultQueryTests { @Test fun `logical operator IN`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + listOf(USD, GBP, CHF).forEach { + vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER) + } val currencies = listOf(CHF.currencyCode, GBP.currencyCode) val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.`in`(currencies) } val criteria = VaultCustomQueryCriteria(logicalExpression) @@ -586,9 +588,9 @@ class VaultQueryTests { @Test fun `logical operator NOT IN`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + listOf(USD, GBP, CHF).forEach { + vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER) + } val currencies = listOf(CHF.currencyCode, GBP.currencyCode) val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.notIn(currencies) } val criteria = VaultCustomQueryCriteria(logicalExpression) @@ -600,9 +602,9 @@ class VaultQueryTests { @Test fun `logical operator LIKE`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + listOf(USD, GBP, CHF).forEach { + vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER) + } val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.like("%BP") } // GPB val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -613,9 +615,9 @@ class VaultQueryTests { @Test fun `logical operator NOT LIKE`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + listOf(USD, GBP, CHF).forEach { + vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER) + } val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.notLike("%BP") } // GPB val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -626,9 +628,9 @@ class VaultQueryTests { @Test fun `logical operator IS_NULL`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + listOf(USD, GBP, CHF).forEach { + vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER) + } val logicalExpression = builder { CashSchemaV1.PersistentCashState::issuerPartyHash.isNull() } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -639,9 +641,9 @@ class VaultQueryTests { @Test fun `logical operator NOT_NULL`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + listOf(USD, GBP, CHF).forEach { + vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 1, DUMMY_CASH_ISSUER) + } val logicalExpression = builder { CashSchemaV1.PersistentCashState::issuerPartyHash.notNull() } val criteria = VaultCustomQueryCriteria(logicalExpression) val results = vaultService.queryBy(criteria) @@ -652,11 +654,9 @@ class VaultQueryTests { @Test fun `aggregate functions without group clause`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(200.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L)) - vaultFiller.fillWithSomeTestCash(300.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - vaultFiller.fillWithSomeTestCash(400.POUNDS, notaryServices, DUMMY_NOTARY, 4, 4, Random(0L)) - vaultFiller.fillWithSomeTestCash(500.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) + listOf(100.DOLLARS, 200.DOLLARS, 300.DOLLARS, 400.POUNDS, 500.SWISS_FRANCS).zip(1..5).forEach { (howMuch, states) -> + vaultFiller.fillWithSomeTestCash(howMuch, notaryServices, states, DUMMY_CASH_ISSUER) + } // DOCSTART VaultQueryExample21 val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum() } val sumCriteria = VaultCustomQueryCriteria(sum) @@ -692,11 +692,9 @@ class VaultQueryTests { @Test fun `aggregate functions with single group clause`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(200.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L)) - vaultFiller.fillWithSomeTestCash(300.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - vaultFiller.fillWithSomeTestCash(400.POUNDS, notaryServices, DUMMY_NOTARY, 4, 4, Random(0L)) - vaultFiller.fillWithSomeTestCash(500.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) + listOf(100.DOLLARS, 200.DOLLARS, 300.DOLLARS, 400.POUNDS, 500.SWISS_FRANCS).zip(1..5).forEach { (howMuch, states) -> + vaultFiller.fillWithSomeTestCash(howMuch, notaryServices, states, DUMMY_CASH_ISSUER) + } // DOCSTART VaultQueryExample22 val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) } val sumCriteria = VaultCustomQueryCriteria(sum) @@ -742,10 +740,10 @@ class VaultQueryTests { fun `aggregate functions sum by issuer and currency and sort by aggregate sum`() { database.transaction { identitySvc.verifyAndRegisterIdentity(BOC_IDENTITY) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = DUMMY_CASH_ISSUER) - vaultFiller.fillWithSomeTestCash(200.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L), issuedBy = BOC.ref(1)) - vaultFiller.fillWithSomeTestCash(300.POUNDS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L), issuedBy = DUMMY_CASH_ISSUER) - vaultFiller.fillWithSomeTestCash(400.POUNDS, notaryServices, DUMMY_NOTARY, 4, 4, Random(0L), issuedBy = BOC.ref(2)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 1, DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(200.DOLLARS, notaryServices, 2, BOC.ref(1)) + vaultFiller.fillWithSomeTestCash(300.POUNDS, notaryServices, 3, DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(400.POUNDS, notaryServices, 4, BOC.ref(2)) // DOCSTART VaultQueryExample23 val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum(groupByColumns = listOf(CashSchemaV1.PersistentCashState::issuerPartyHash, @@ -777,7 +775,7 @@ class VaultQueryTests { fun `aggregate functions count by contract type`() { database.transaction { // create new states - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 10, 10, Random(0L)) + vaultFillerCashNotary.fillWithSomeTestCash(100.DOLLARS, notaryServices, 10, DUMMY_CASH_ISSUER) vaultFiller.fillWithSomeTestLinearStates(1, "XYZ") vaultFiller.fillWithSomeTestLinearStates(2, "JKL") vaultFiller.fillWithSomeTestLinearStates(3, "ABC") @@ -802,7 +800,7 @@ class VaultQueryTests { fun `aggregate functions count by contract type and state status`() { database.transaction { // create new states - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 10, 10, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 10, DUMMY_CASH_ISSUER) val linearStatesXYZ = vaultFiller.fillWithSomeTestLinearStates(1, "XYZ") val linearStatesJKL = vaultFiller.fillWithSomeTestLinearStates(2, "JKL") vaultFiller.fillWithSomeTestLinearStates(3, "ABC") @@ -823,10 +821,10 @@ class VaultQueryTests { assertThat(dealStateCount).isEqualTo(3L) // consume some states - vaultFiller.consumeLinearStates(linearStatesXYZ.states.toList(), DUMMY_NOTARY) - vaultFiller.consumeLinearStates(linearStatesJKL.states.toList(), DUMMY_NOTARY) - vaultFiller.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }, DUMMY_NOTARY) - val cashUpdates = vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) + vaultFiller.consumeLinearStates(linearStatesXYZ.states.toList()) + vaultFiller.consumeLinearStates(linearStatesJKL.states.toList()) + vaultFiller.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }) + val cashUpdates = consumeCash(50.DOLLARS) // UNCONSUMED states (default) // count fungible assets @@ -864,7 +862,7 @@ class VaultQueryTests { @Test fun `unconsumed states recorded between two time intervals`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 3, 3, Random(0L)) + vaultFillerCashNotary.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) // DOCSTART VaultQueryExample6 val start = TODAY val end = TODAY.plus(30, ChronoUnit.DAYS) @@ -888,12 +886,12 @@ class VaultQueryTests { @Test fun `states consumed after time`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) vaultFiller.fillWithSomeTestLinearStates(10) vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) } database.transaction { - vaultFiller.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) + consumeCash(100.DOLLARS) val asOfDateTime = TODAY val consumedAfterExpression = TimeCondition( QueryCriteria.TimeInstantType.CONSUMED, ColumnPredicate.BinaryComparison(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL, asOfDateTime)) @@ -909,7 +907,7 @@ class VaultQueryTests { @Test fun `all states with paging specification - first page`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 100, 100, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 100, DUMMY_CASH_ISSUER) // DOCSTART VaultQueryExample7 val pagingSpec = PageSpecification(DEFAULT_PAGE_NUM, 10) val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) @@ -924,7 +922,7 @@ class VaultQueryTests { @Test fun `all states with paging specification - last`() { database.transaction { - vaultFiller.fillWithSomeTestCash(95.DOLLARS, notaryServices, DUMMY_NOTARY, 95, 95, Random(0L)) + vaultFiller.fillWithSomeTestCash(95.DOLLARS, notaryServices, 95, DUMMY_CASH_ISSUER) // Last page implies we need to perform a row count for the Query first, // and then re-query for a given offset defined by (count - pageSize) val pagingSpec = PageSpecification(10, 10) @@ -943,7 +941,7 @@ class VaultQueryTests { expectedEx.expectMessage("Page specification: invalid page number") database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 100, 100, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 100, DUMMY_CASH_ISSUER) val pagingSpec = PageSpecification(0, 10) val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) @@ -958,7 +956,7 @@ class VaultQueryTests { expectedEx.expectMessage("Page specification: invalid page size") database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 100, 100, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 100, DUMMY_CASH_ISSUER) @Suppress("EXPECTED_CONDITION") val pagingSpec = PageSpecification(DEFAULT_PAGE_NUM, @Suppress("INTEGER_OVERFLOW") MAX_PAGE_SIZE + 1) // overflow = -2147483648 val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) @@ -973,7 +971,7 @@ class VaultQueryTests { expectedEx.expectMessage("Please specify a `PageSpecification`") database.transaction { - vaultFiller.fillWithSomeTestCash(201.DOLLARS, notaryServices, DUMMY_NOTARY, 201, 201, Random(0L)) + vaultFiller.fillWithSomeTestCash(201.DOLLARS, notaryServices, 201, DUMMY_CASH_ISSUER) val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) vaultService.queryBy(criteria) } @@ -1011,8 +1009,8 @@ class VaultQueryTests { @Test fun `unconsumed fungible assets`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - vaultFiller.fillWithSomeTestCommodity(Amount(100, Commodity.getInstance("FCOJ")!!), notaryServices) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCommodity(Amount(100, Commodity.getInstance("FCOJ")!!), notaryServices, DUMMY_OBLIGATION_ISSUER.ref(1)) vaultFiller.fillWithSomeTestLinearStates(10) val results = vaultService.queryBy>() assertThat(results.states).hasSize(4) @@ -1022,11 +1020,11 @@ class VaultQueryTests { @Test fun `consumed fungible assets`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) } database.transaction { - vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) - vaultFiller.fillWithSomeTestCommodity(Amount(100, Commodity.getInstance("FCOJ")!!), notaryServices) + consumeCash(50.DOLLARS) + vaultFiller.fillWithSomeTestCommodity(Amount(100, Commodity.getInstance("FCOJ")!!), notaryServices, DUMMY_OBLIGATION_ISSUER.ref(1)) vaultFiller.fillWithSomeTestLinearStates(10) val criteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED) val results = vaultService.queryBy>(criteria) @@ -1037,7 +1035,7 @@ class VaultQueryTests { @Test fun `unconsumed cash fungible assets`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) vaultFiller.fillWithSomeTestLinearStates(10) val results = vaultService.queryBy() assertThat(results.states).hasSize(3) @@ -1047,10 +1045,10 @@ class VaultQueryTests { @Test fun `unconsumed cash fungible assets after spending`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) } database.transaction { - vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) + consumeCash(50.DOLLARS) // should now have x2 CONSUMED + x2 UNCONSUMED (one spent + one change) val results = vaultService.queryBy(FungibleAssetQueryCriteria()) assertThat(results.statesMetadata).hasSize(2) @@ -1061,12 +1059,12 @@ class VaultQueryTests { @Test fun `consumed cash fungible assets`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) } database.transaction { - vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) + consumeCash(50.DOLLARS) val linearStates = vaultFiller.fillWithSomeTestLinearStates(10) - vaultFiller.consumeLinearStates(linearStates.states.toList(), DUMMY_NOTARY) + vaultFiller.consumeLinearStates(linearStates.states.toList()) val criteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED) val results = vaultService.queryBy(criteria) assertThat(results.states).hasSize(2) @@ -1076,7 +1074,7 @@ class VaultQueryTests { @Test fun `unconsumed linear heads`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) vaultFiller.fillWithSomeTestLinearStates(10) vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) val results = vaultService.queryBy() @@ -1087,13 +1085,13 @@ class VaultQueryTests { @Test fun `consumed linear heads`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) val linearStates = vaultFiller.fillWithSomeTestLinearStates(2, "TEST") // create 2 states with same externalId vaultFiller.fillWithSomeTestLinearStates(8) val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")) - vaultFiller.consumeLinearStates(linearStates.states.toList(), DUMMY_NOTARY) - vaultFiller.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }, DUMMY_NOTARY) - vaultFiller.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY) + vaultFiller.consumeLinearStates(linearStates.states.toList()) + vaultFiller.consumeDeals(dealStates.states.filter { it.state.data.linearId.externalId == "456" }) + consumeCash(50.DOLLARS) val criteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED) val results = vaultService.queryBy(criteria) assertThat(results.states).hasSize(3) @@ -1146,9 +1144,9 @@ class VaultQueryTests { database.transaction { val txns = vaultFiller.fillWithSomeTestLinearStates(1, "TEST") val linearState = txns.states.first() - vaultFiller.evolveLinearState(linearState, DUMMY_NOTARY) // consume current and produce new state reference - vaultFiller.evolveLinearState(linearState, DUMMY_NOTARY) // consume current and produce new state reference - vaultFiller.evolveLinearState(linearState, DUMMY_NOTARY) // consume current and produce new state reference + repeat(3) { + vaultFiller.evolveLinearState(linearState) // consume current and produce new state reference + } val linearId = linearState.state.data.linearId // should now have 1 UNCONSUMED & 3 CONSUMED state refs for Linear State with "TEST" @@ -1166,9 +1164,9 @@ class VaultQueryTests { database.transaction { val txns = vaultFiller.fillWithSomeTestLinearStates(2, "TEST") val linearStates = txns.states.toList() - vaultFiller.evolveLinearStates(linearStates, DUMMY_NOTARY) // consume current and produce new state reference - vaultFiller.evolveLinearStates(linearStates, DUMMY_NOTARY) // consume current and produce new state reference - vaultFiller.evolveLinearStates(linearStates, DUMMY_NOTARY) // consume current and produce new state reference + repeat(3) { + vaultFiller.evolveLinearStates(linearStates) // consume current and produce new state reference + } // should now have 1 UNCONSUMED & 3 CONSUMED state refs for Linear State with "TEST" val linearStateCriteria = LinearStateQueryCriteria(uuid = linearStates.map { it.state.data.linearId.id }, status = Vault.StateStatus.ALL) val vaultCriteria = VaultQueryCriteria(status = Vault.StateStatus.ALL) @@ -1234,9 +1232,9 @@ class VaultQueryTests { database.transaction { val txns = vaultFiller.fillWithSomeTestLinearStates(1, "TEST") val linearState = txns.states.first() - val linearState2 = vaultFiller.evolveLinearState(linearState, DUMMY_NOTARY) // consume current and produce new state reference - val linearState3 = vaultFiller.evolveLinearState(linearState2, DUMMY_NOTARY) // consume current and produce new state reference - vaultFiller.evolveLinearState(linearState3, DUMMY_NOTARY) // consume current and produce new state reference + val linearState2 = vaultFiller.evolveLinearState(linearState) // consume current and produce new state reference + val linearState3 = vaultFiller.evolveLinearState(linearState2) // consume current and produce new state reference + vaultFiller.evolveLinearState(linearState3) // consume current and produce new state reference // should now have 1 UNCONSUMED & 3 CONSUMED state refs for Linear State with "TEST" val linearStateCriteria = LinearStateQueryCriteria(linearId = txns.states.map { it.state.data.linearId }, status = Vault.StateStatus.CONSUMED) val vaultCriteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED) @@ -1308,10 +1306,9 @@ class VaultQueryTests { fun `unconsumed fungible assets for specific issuer party and refs`() { database.transaction { identitySvc.verifyAndRegisterIdentity(BOC_IDENTITY) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = DUMMY_CASH_ISSUER) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(1)) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(2)) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(3)) + listOf(DUMMY_CASH_ISSUER, BOC.ref(1), BOC.ref(2), BOC.ref(3)).forEach { + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 1, it) + } val criteria = FungibleAssetQueryCriteria(issuer = listOf(BOC), issuerRef = listOf(BOC.ref(1).reference, BOC.ref(2).reference)) val results = vaultService.queryBy>(criteria) @@ -1337,9 +1334,9 @@ class VaultQueryTests { services.identityService.verifyAndRegisterIdentity(identity) } database.transaction { - vaultFiller.fillWithSomeTestCash(100.POUNDS, gbpCashIssuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = gbpCashIssuer.party.ref(1)) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, usdCashIssuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = usdCashIssuer.party.ref(1)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, chfCashIssuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = chfCashIssuer.party.ref(1)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, gbpCashIssuerServices, 1, gbpCashIssuer.party.ref(1)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, usdCashIssuerServices, 1, usdCashIssuer.party.ref(1)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, chfCashIssuerServices, 1, chfCashIssuer.party.ref(1)) } database.transaction { val criteria = FungibleAssetQueryCriteria(issuer = listOf(gbpCashIssuer.party, usdCashIssuer.party)) @@ -1351,9 +1348,8 @@ class VaultQueryTests { @Test fun `unconsumed fungible assets by owner`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(1)) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), - issuedBy = MEGA_CORP.ref(0), owner = (MINI_CORP)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 1, BOC.ref(1)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 1, MEGA_CORP.ref(0), MINI_CORP) } database.transaction { val criteria = FungibleAssetQueryCriteria(owner = listOf(MEGA_CORP)) @@ -1365,11 +1361,9 @@ class VaultQueryTests { @Test fun `unconsumed fungible states for owners`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), - issuedBy = MEGA_CORP.ref(0), owner = (MEGA_CORP)) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), - issuedBy = BOC.ref(0), owner = MINI_CORP) // irrelevant to this vault + vaultFillerCashNotary.fillWithSomeTestCash(100.DOLLARS, notaryServices, 1, DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 1, MEGA_CORP.ref(0), MEGA_CORP) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 1, BOC.ref(0), MINI_CORP) // irrelevant to this vault } database.transaction { // DOCSTART VaultQueryExample5.2 @@ -1386,9 +1380,9 @@ class VaultQueryTests { fun `unconsumed fungible assets for single currency`() { database.transaction { vaultFiller.fillWithSomeTestLinearStates(10) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + listOf(USD, GBP, CHF).forEach { + vaultFiller.fillWithSomeTestCash(AMOUNT(100, it), notaryServices, 3, DUMMY_CASH_ISSUER) + } // DOCSTART VaultQueryExample12 val ccyIndex = builder { CashSchemaV1.PersistentCashState::currency.equal(USD.currencyCode) } val criteria = VaultCustomQueryCriteria(ccyIndex) @@ -1402,8 +1396,9 @@ class VaultQueryTests { @Test fun `unconsumed cash balance for single currency`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(200.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L)) + listOf(100, 200).zip(1..2).forEach { (howMuch, states) -> + vaultFiller.fillWithSomeTestCash(howMuch.DOLLARS, notaryServices, states, DUMMY_CASH_ISSUER) + } val sum = builder { CashSchemaV1.PersistentCashState::pennies.sum(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) } val sumCriteria = VaultCustomQueryCriteria(sum) @@ -1421,12 +1416,9 @@ class VaultQueryTests { @Test fun `unconsumed cash balances for all currencies`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(200.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L)) - vaultFiller.fillWithSomeTestCash(300.POUNDS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - vaultFiller.fillWithSomeTestCash(400.POUNDS, notaryServices, DUMMY_NOTARY, 4, 4, Random(0L)) - vaultFiller.fillWithSomeTestCash(500.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) - vaultFiller.fillWithSomeTestCash(600.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 6, 6, Random(0L)) + listOf(100.DOLLARS, 200.DOLLARS, 300.POUNDS, 400.POUNDS, 500.SWISS_FRANCS, 600.SWISS_FRANCS).zip(1..6).forEach { (howMuch, states) -> + vaultFiller.fillWithSomeTestCash(howMuch, notaryServices, states, DUMMY_CASH_ISSUER) + } val ccyIndex = builder { CashSchemaV1.PersistentCashState::pennies.sum(groupByColumns = listOf(CashSchemaV1.PersistentCashState::currency)) } val criteria = VaultCustomQueryCriteria(ccyIndex) val results = vaultService.queryBy>(criteria) @@ -1444,10 +1436,9 @@ class VaultQueryTests { @Test fun `unconsumed fungible assets for quantity greater than`() { database.transaction { - vaultFiller.fillWithSomeTestCash(10.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) - vaultFiller.fillWithSomeTestCash(25.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(50.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + listOf(10.DOLLARS, 25.POUNDS, 50.POUNDS, 100.SWISS_FRANCS).zip(listOf(3, 1, 1, 3)).forEach { (howMuch, states) -> + vaultFiller.fillWithSomeTestCash(howMuch, notaryServices, states, DUMMY_CASH_ISSUER) + } // DOCSTART VaultQueryExample13 val fungibleAssetCriteria = FungibleAssetQueryCriteria(quantity = builder { greaterThan(2500L) }) val results = vaultService.queryBy(fungibleAssetCriteria) @@ -1461,8 +1452,9 @@ class VaultQueryTests { fun `unconsumed fungible assets for issuer party`() { database.transaction { identitySvc.verifyAndRegisterIdentity(BOC_IDENTITY) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (DUMMY_CASH_ISSUER)) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (BOC.ref(1))) + listOf(DUMMY_CASH_ISSUER, BOC.ref(1)).forEach { + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 1, it) + } // DOCSTART VaultQueryExample14 val criteria = FungibleAssetQueryCriteria(issuer = listOf(BOC)) val results = vaultService.queryBy>(criteria) @@ -1475,10 +1467,9 @@ class VaultQueryTests { @Test fun `unconsumed fungible assets for single currency and quantity greater than`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(50.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + listOf(100.DOLLARS, 100.POUNDS, 50.POUNDS, 100.SWISS_FRANCS).forEach { + vaultFiller.fillWithSomeTestCash(it, notaryServices, 1, DUMMY_CASH_ISSUER) + } val ccyIndex = builder { CashSchemaV1.PersistentCashState::currency.equal(GBP.currencyCode) } val customCriteria = VaultCustomQueryCriteria(ccyIndex) val fungibleAssetCriteria = FungibleAssetQueryCriteria(quantity = builder { greaterThan(5000L) }) @@ -1578,9 +1569,9 @@ class VaultQueryTests { cordappPackages -= SampleCashSchemaV3::class.packageName setUp() database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 1, DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, 1, DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, notaryServices, 1, DUMMY_CASH_ISSUER) // CashSchemaV3 NOT registered with NodeSchemaService val logicalExpression = builder { SampleCashSchemaV3.PersistentCashState::currency.equal(GBP.currencyCode) } val criteria = VaultCustomQueryCriteria(logicalExpression) @@ -1597,10 +1588,10 @@ class VaultQueryTests { @Test fun `custom - all cash states with amount of currency greater or equal than`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(10.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) - vaultFiller.fillWithSomeTestCash(1.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, 1, DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 1, DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(10.DOLLARS, notaryServices, 1, DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(1.DOLLARS, notaryServices, 1, DUMMY_CASH_ISSUER) // DOCSTART VaultQueryExample20 val generalCriteria = VaultQueryCriteria(Vault.StateStatus.ALL) @@ -1690,7 +1681,7 @@ class VaultQueryTests { database.transaction { vaultFiller.fillWithSomeTestLinearStates(1, "TEST1") val aState = vaultFiller.fillWithSomeTestLinearStates(1, "TEST2").states - vaultFiller.consumeLinearStates(aState.toList(), DUMMY_NOTARY) + vaultFiller.consumeLinearStates(aState.toList()) val uuid = vaultFiller.fillWithSomeTestLinearStates(1, "TEST1").states.first().state.data.linearId.id // 2 unconsumed states with same external ID, 1 consumed with different external ID val results = builder { @@ -1777,8 +1768,8 @@ class VaultQueryTests { @Test fun `enriched and overridden composite query handles defaults correctly`() { database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 2, 2, Random(0L)) - vaultFiller.fillWithSomeTestCommodity(Amount(100, Commodity.getInstance("FCOJ")!!), notaryServices) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 2, DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCommodity(Amount(100, Commodity.getInstance("FCOJ")!!), notaryServices, DUMMY_OBLIGATION_ISSUER.ref(1)) vaultFiller.fillWithSomeTestLinearStates(1, "ABC") vaultFiller.fillWithSomeTestDeals(listOf("123")) // Base criteria @@ -1813,20 +1804,20 @@ class VaultQueryTests { } val (linearStates, dealStates) = database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 5, DUMMY_CASH_ISSUER) val linearStates = vaultFiller.fillWithSomeTestLinearStates(10).states val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")).states // add more cash - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, 1, DUMMY_CASH_ISSUER) // add another deal vaultFiller.fillWithSomeTestDeals(listOf("SAMPLE DEAL")) Pair(linearStates, dealStates) } database.transaction { // consume stuff - vaultFiller.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) - vaultFiller.consumeDeals(dealStates.toList(), DUMMY_NOTARY) - vaultFiller.consumeLinearStates(linearStates.toList(), DUMMY_NOTARY) + consumeCash(100.DOLLARS) + vaultFiller.consumeDeals(dealStates.toList()) + vaultFiller.consumeLinearStates(linearStates.toList()) } updates.expectEvents { @@ -1854,24 +1845,24 @@ class VaultQueryTests { } val (linearStates, dealStates) = database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 5, DUMMY_CASH_ISSUER) val linearStates = vaultFiller.fillWithSomeTestLinearStates(10).states val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")).states // add more cash - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, 1, DUMMY_CASH_ISSUER) // add another deal vaultFiller.fillWithSomeTestDeals(listOf("SAMPLE DEAL")) Pair(linearStates, dealStates) } database.transaction { // consume stuff - vaultFiller.consumeCash(100.POUNDS, notary = DUMMY_NOTARY) + consumeCash(100.POUNDS) } database.transaction { // consume more stuff - vaultFiller.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) - vaultFiller.consumeDeals(dealStates.toList(), DUMMY_NOTARY) - vaultFiller.consumeLinearStates(linearStates.toList(), DUMMY_NOTARY) + consumeCash(100.DOLLARS) + vaultFiller.consumeDeals(dealStates.toList()) + vaultFiller.consumeLinearStates(linearStates.toList()) } updates.expectEvents { @@ -1899,24 +1890,24 @@ class VaultQueryTests { } val (linearStates, dealStates) = database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 5, 5, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 5, DUMMY_CASH_ISSUER) val linearStates = vaultFiller.fillWithSomeTestLinearStates(10).states val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")).states // add more cash - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, 1, DUMMY_CASH_ISSUER) // add another deal vaultFiller.fillWithSomeTestDeals(listOf("SAMPLE DEAL")) Pair(linearStates, dealStates) } database.transaction { // consume stuff - vaultFiller.consumeCash(99.POUNDS, notary = DUMMY_NOTARY) + consumeCash(99.POUNDS) } database.transaction { // consume more stuff - vaultFiller.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) - vaultFiller.consumeDeals(dealStates.toList(), DUMMY_NOTARY) - vaultFiller.consumeLinearStates(linearStates.toList(), DUMMY_NOTARY) + consumeCash(100.DOLLARS) + vaultFiller.consumeDeals(dealStates.toList()) + vaultFiller.consumeLinearStates(linearStates.toList()) } updates.expectEvents { @@ -1957,20 +1948,20 @@ class VaultQueryTests { } val (linearStates, dealStates) = database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) val linearStates = vaultFiller.fillWithSomeTestLinearStates(10).states val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")).states // add more cash - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, 1, DUMMY_CASH_ISSUER) // add another deal vaultFiller.fillWithSomeTestDeals(listOf("SAMPLE DEAL")) Pair(linearStates, dealStates) } database.transaction { // consume stuff - vaultFiller.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) - vaultFiller.consumeDeals(dealStates.toList(), DUMMY_NOTARY) - vaultFiller.consumeLinearStates(linearStates.toList(), DUMMY_NOTARY) + consumeCash(100.DOLLARS) + vaultFiller.consumeDeals(dealStates.toList()) + vaultFiller.consumeLinearStates(linearStates.toList()) } updates.expectEvents { @@ -2006,20 +1997,20 @@ class VaultQueryTests { } val (linearStates, dealStates) = database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 3, 3, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 3, DUMMY_CASH_ISSUER) val linearStates = vaultFiller.fillWithSomeTestLinearStates(10).states val dealStates = vaultFiller.fillWithSomeTestDeals(listOf("123", "456", "789")).states // add more cash - vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.POUNDS, notaryServices, 1, DUMMY_CASH_ISSUER) // add another deal vaultFiller.fillWithSomeTestDeals(listOf("SAMPLE DEAL")) Pair(linearStates, dealStates) } database.transaction { // consume stuff - vaultFiller.consumeCash(100.DOLLARS, notary = DUMMY_NOTARY) - vaultFiller.consumeDeals(dealStates.toList(), DUMMY_NOTARY) - vaultFiller.consumeLinearStates(linearStates.toList(), DUMMY_NOTARY) + consumeCash(100.DOLLARS) + vaultFiller.consumeDeals(dealStates.toList()) + vaultFiller.consumeLinearStates(linearStates.toList()) } updates.expectEvents { 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 1146b79b96..361519e41d 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 @@ -35,7 +35,6 @@ import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test -import java.util.* import java.util.concurrent.Executors import kotlin.test.assertEquals import kotlin.test.fail @@ -64,7 +63,7 @@ class VaultWithCashTest { val databaseAndServices = makeTestDatabaseAndMockServices(cordappPackages = cordappPackages, keys = listOf(generateKeyPair(), DUMMY_NOTARY_KEY)) database = databaseAndServices.first services = databaseAndServices.second - vaultFiller = VaultFiller(services) + vaultFiller = VaultFiller(services, DUMMY_NOTARY, DUMMY_NOTARY_KEY) issuerServices = MockServices(cordappPackages, DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY, MEGA_CORP_KEY) notaryServices = MockServices(cordappPackages, DUMMY_NOTARY.name, DUMMY_NOTARY_KEY) notary = notaryServices.myInfo.legalIdentitiesAndCerts.single().party @@ -80,7 +79,7 @@ class VaultWithCashTest { fun splits() { database.transaction { // Fix the PRNG so that we get the same splits every time. - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L), issuedBy = DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 3, DUMMY_CASH_ISSUER) } database.transaction { val w = vaultService.queryBy().states @@ -150,8 +149,7 @@ class VaultWithCashTest { database.transaction { // A tx that sends us money. - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L), owner = AnonymousParty(freshKey), - issuedBy = MEGA_CORP.ref(1)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 10, MEGA_CORP.ref(1), AnonymousParty(freshKey)) println("Cash balance: ${services.getCashBalance(USD)}") } database.transaction { @@ -300,9 +298,9 @@ class VaultWithCashTest { val freshKey = services.keyManagementService.freshKey() database.transaction { - vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L), owner = AnonymousParty(freshKey)) - vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L)) - vaultFiller.fillWithSomeTestCash(100.POUNDS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L)) + vaultFiller.fillWithSomeTestCash(100.DOLLARS, issuerServices, 3, DUMMY_CASH_ISSUER, AnonymousParty(freshKey)) + vaultFiller.fillWithSomeTestCash(100.SWISS_FRANCS, issuerServices, 2, DUMMY_CASH_ISSUER) + vaultFiller.fillWithSomeTestCash(100.POUNDS, issuerServices, 1, DUMMY_CASH_ISSUER) } database.transaction { val cash = vaultService.queryBy().states 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 9f9fac4ac7..b16ca113fb 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 @@ -8,28 +8,39 @@ import net.corda.core.crypto.SignatureMetadata 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.node.ServiceHub import net.corda.core.node.services.Vault import net.corda.core.toFuture import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder -import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow 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.finance.contracts.asset.DUMMY_CASH_ISSUER -import net.corda.finance.contracts.asset.DUMMY_OBLIGATION_ISSUER -import net.corda.testing.* +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 java.security.PublicKey import java.time.Duration import java.time.Instant import java.time.Instant.now import java.util.* -class VaultFiller(private val services: ServiceHub) { +/** + * The service hub should provide at least a key management service and a storage service. + * @param defaultNotary used in [fillWithSomeTestDeals] and [fillWithSomeTestLinearStates]. + * @param altNotary used in [fillWithSomeTestCash], [fillWithSomeTestCommodity] and consume/evolve methods. If not specified, same as [defaultNotary]. + * @param rngFactory used by [fillWithSomeTestCash] if no custom [Random] provided. + */ +class VaultFiller @JvmOverloads constructor( + private val services: ServiceHub, + private val defaultNotary: Party, + private val defaultNotaryKeyPair: KeyPair, + private val altNotary: Party = defaultNotary, + private val rngFactory: () -> Random = { Random(0L) }) { companion object { fun calculateRandomlySizedAmounts(howMuch: Amount, min: Int, max: Int, rng: Random): LongArray { val numSlots = min + Math.floor(rng.nextDouble() * (max - min)).toInt() @@ -60,22 +71,25 @@ class VaultFiller(private val services: ServiceHub) { } } + init { + require(defaultNotary.owningKey == defaultNotaryKeyPair.public) { "Default notary public keys must match." } + } + @JvmOverloads fun fillWithSomeTestDeals(dealIds: List, issuerServices: ServiceHub = services, - participants: List = emptyList(), - notary: Party = DUMMY_NOTARY): Vault { + participants: List = emptyList()): Vault { val myKey: PublicKey = services.myInfo.chooseIdentity().owningKey val me = AnonymousParty(myKey) val transactions: List = dealIds.map { // Issue a deal state - val dummyIssue = TransactionBuilder(notary = notary).apply { + val dummyIssue = TransactionBuilder(notary = defaultNotary).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, notary.owningKey) + return@map services.addSignature(stx, defaultNotaryKeyPair.public) } services.recordTransactions(transactions) // Get all the StateAndRefs of all the generated transactions. @@ -96,11 +110,11 @@ class VaultFiller(private val services: ServiceHub) { linearTimestamp: Instant = now()): Vault { val myKey: PublicKey = services.myInfo.chooseIdentity().owningKey val me = AnonymousParty(myKey) - val issuerKey = DUMMY_NOTARY_KEY + val issuerKey = defaultNotaryKeyPair 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 = DUMMY_NOTARY).apply { + val dummyIssue = TransactionBuilder(notary = defaultNotary).apply { addOutputState(DummyLinearContract.State( linearId = UniqueIdentifier(externalId), participants = participants.plus(me), @@ -121,49 +135,35 @@ class VaultFiller(private val services: ServiceHub) { return Vault(states) } + @JvmOverloads + fun fillWithSomeTestCash(howMuch: Amount, + issuerServices: ServiceHub, + thisManyStates: Int, + issuedBy: PartyAndReference, + owner: AbstractParty? = null, + rng: Random? = null) = fillWithSomeTestCash(howMuch, issuerServices, thisManyStates, thisManyStates, issuedBy, owner, rng) + /** - * Creates a random set of cash states that add up to the given amount and adds them to the vault. This is intended for - * unit tests. The cash is owned by the legal identity key from the storage service. - * - * The service hub needs to provide at least a key management service and a storage service. + * Creates a random set of between (by default) 3 and 10 cash states that add up to the given amount and adds them + * to the vault. This is intended for unit tests. By default the cash is owned by the legal + * identity key from the storage service. * * @param issuerServices service hub of the issuer node, which will be used to sign the transaction. - * @param outputNotary the notary to use for output states. The transaction is NOT signed by this notary. * @return a vault object that represents the generated states (it will NOT be the full vault from the service hub!). */ fun fillWithSomeTestCash(howMuch: Amount, issuerServices: ServiceHub, - outputNotary: Party, - states: Int, - issuedBy: PartyAndReference): Vault - = fillWithSomeTestCash(howMuch, issuerServices, outputNotary, states, states, issuedBy = issuedBy) - - /** - * Creates a random set of between (by default) 3 and 10 cash states that add up to the given amount and adds them - * to the vault. This is intended for unit tests. By default the cash is issued by [DUMMY_CASH_ISSUER] and owned by the legal - * identity key from the storage service. - * - * The service hub needs to provide at least a key management service and a storage service. - * - * @param issuerServices service hub of the issuer node, which will be used to sign the transaction. - * @param outputNotary the notary to use for output states. The transaction is NOT signed by this notary. - * @return a vault object that represents the generated states (it will NOT be the full vault from the service hub!). - */ - fun fillWithSomeTestCash(howMuch: Amount, - issuerServices: ServiceHub = services, - outputNotary: Party = DUMMY_NOTARY, - atLeastThisManyStates: Int = 3, - atMostThisManyStates: Int = 10, - rng: Random = Random(), + atLeastThisManyStates: Int, + atMostThisManyStates: Int, + issuedBy: PartyAndReference, owner: AbstractParty? = null, - issuedBy: PartyAndReference = DUMMY_CASH_ISSUER): Vault { - val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng) - + rng: Random? = null): Vault { + val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng ?: rngFactory()) // We will allocate one state to one transaction, for simplicities sake. val cash = Cash() val transactions: List = amounts.map { pennies -> val issuance = TransactionBuilder(null as Party?) - cash.generateIssue(issuance, Amount(pennies, Issued(issuedBy, howMuch.token)), owner ?: services.myInfo.singleIdentity(), outputNotary) + cash.generateIssue(issuance, Amount(pennies, Issued(issuedBy, howMuch.token)), owner ?: services.myInfo.singleIdentity(), altNotary) return@map issuerServices.signInitialTransaction(issuance, issuedBy.party.owningKey) } services.recordTransactions(transactions) @@ -178,88 +178,74 @@ class VaultFiller(private val services: ServiceHub) { /** * * @param issuerServices service hub of the issuer node, which will be used to sign the transaction. - * @param outputNotary the notary to use for output states. The transaction is NOT signed by this notary. * @return a vault object that represents the generated states (it will NOT be the full vault from the service hub!). */ // TODO: need to make all FungibleAsset commands (issue, move, exit) generic - fun fillWithSomeTestCommodity(amount: Amount, - issuerServices: ServiceHub = services, - outputNotary: Party = DUMMY_NOTARY, - ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })), - ownedBy: AbstractParty? = null, - issuedBy: PartyAndReference = DUMMY_OBLIGATION_ISSUER.ref(1)): Vault { - val myKey: PublicKey = ownedBy?.owningKey ?: services.myInfo.chooseIdentity().owningKey + fun fillWithSomeTestCommodity(amount: Amount, issuerServices: ServiceHub, issuedBy: PartyAndReference): Vault { + val myKey: PublicKey = services.myInfo.chooseIdentity().owningKey val me = AnonymousParty(myKey) val commodity = CommodityContract() val issuance = TransactionBuilder(null as Party?) - commodity.generateIssue(issuance, Amount(amount.quantity, Issued(issuedBy.copy(reference = ref), amount.token)), me, outputNotary) + commodity.generateIssue(issuance, Amount(amount.quantity, Issued(issuedBy, amount.token)), me, altNotary) val transaction = issuerServices.signInitialTransaction(issuance, issuedBy.party.owningKey) services.recordTransactions(transaction) return Vault(setOf(transaction.tx.outRef(0))) } - fun consume(states: List>, notary: Party) { + private fun consume(states: List>) { // Create a txn consuming different contract types states.forEach { - val builder = TransactionBuilder(notary = notary).apply { + val builder = TransactionBuilder(notary = altNotary).apply { addInputState(it) - addCommand(dummyCommand(notary.owningKey)) + addCommand(dummyCommand(altNotary.owningKey)) } - val consumedTx = services.signInitialTransaction(builder, notary.owningKey) + val consumedTx = services.signInitialTransaction(builder, altNotary.owningKey) services.recordTransactions(consumedTx) } } - private fun consumeAndProduce(stateAndRef: StateAndRef, notary: Party): StateAndRef { + private fun consumeAndProduce(stateAndRef: StateAndRef): StateAndRef { // Create a txn consuming different contract types - var builder = TransactionBuilder(notary = notary).apply { + var builder = TransactionBuilder(notary = altNotary).apply { addInputState(stateAndRef) - addCommand(dummyCommand(notary.owningKey)) + addCommand(dummyCommand(altNotary.owningKey)) } - val consumedTx = services.signInitialTransaction(builder, notary.owningKey) + val consumedTx = services.signInitialTransaction(builder, altNotary.owningKey) services.recordTransactions(consumedTx) // Create a txn consuming different contract types - builder = TransactionBuilder(notary = notary).apply { + builder = TransactionBuilder(notary = altNotary).apply { addOutputState(DummyLinearContract.State(linearId = stateAndRef.state.data.linearId, participants = stateAndRef.state.data.participants), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) - addCommand(dummyCommand(notary.owningKey)) + addCommand(dummyCommand(altNotary.owningKey)) } - val producedTx = services.signInitialTransaction(builder, notary.owningKey) + val producedTx = services.signInitialTransaction(builder, altNotary.owningKey) services.recordTransactions(producedTx) return producedTx.tx.outRef(0) } - private fun consumeAndProduce(states: List>, notary: Party) { + private fun consumeAndProduce(states: List>) { states.forEach { - consumeAndProduce(it, notary) + consumeAndProduce(it) } } - fun consumeDeals(dealStates: List>, notary: Party) = consume(dealStates, notary) - fun consumeLinearStates(linearStates: List>, notary: Party) = consume(linearStates, notary) - fun evolveLinearStates(linearStates: List>, notary: Party) = consumeAndProduce(linearStates, notary) - fun evolveLinearState(linearState: StateAndRef, notary: Party): StateAndRef = consumeAndProduce(linearState, notary) - + fun consumeDeals(dealStates: List>) = consume(dealStates) + fun consumeLinearStates(linearStates: List>) = consume(linearStates) + fun evolveLinearStates(linearStates: List>) = consumeAndProduce(linearStates) + fun evolveLinearState(linearState: StateAndRef): StateAndRef = consumeAndProduce(linearState) /** * Consume cash, sending any change to the default identity for this node. Only suitable for use in test scenarios, * where nodes have a default identity. */ - @JvmOverloads - fun consumeCash(amount: Amount, to: Party = CHARLIE, notary: Party): Vault.Update { - return consumeCash(amount, services.myInfo.chooseIdentityAndCert(), to, notary) - } - - /** - * Consume cash, sending any change to the specified identity. - */ - private fun consumeCash(amount: Amount, ourIdentity: PartyAndCertificate, to: Party = CHARLIE, notary: Party): Vault.Update { + fun consumeCash(amount: Amount, to: AbstractParty): Vault.Update { + val ourIdentity = services.myInfo.chooseIdentityAndCert() val update = services.vaultService.rawUpdates.toFuture() // A tx that spends our money. - val builder = TransactionBuilder(notary).apply { + val builder = TransactionBuilder(altNotary).apply { Cash.generateSpend(services, this, amount, ourIdentity, to) } - val spendTx = services.signInitialTransaction(builder, notary.owningKey) + val spendTx = services.signInitialTransaction(builder, altNotary.owningKey) services.recordTransactions(spendTx) return update.getOrThrow(Duration.ofSeconds(3)) } From 5c53a9178528faf6da71d96c9b4fb56b6bcb08b1 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Thu, 23 Nov 2017 22:27:24 +0000 Subject: [PATCH 14/27] Overhaul of the Bank of Corda demo to fix two problems it had: 1. The runRPCCashIssue and runWebCashIssue gradle tasks didn't work because they were using the wrong ports 2. Notary lookup was failing because the lookup name didn't include the correct CN for the notary name (this slipped through when reverting the network parameters) The ports change occurred in #1922 which was attempting the fix the runIssuer gradle task. This is actually a misleading and redundant task as all it does is start up the nodes, which is what the documented deployNodes already does. The ports runIssuer allocated to the nodes were different to the ones specified in deployNodes. To make sure we have integration tests which closely match deployNodes, the BoC demo has been updated to make use of CordformDefinition. This keeps the node definitions in one place, removing the need to have disparate files in sync. runIssuer has been removed. --- .../BankOfCordaDriverKt___Issue_Web.xml | 15 --- .../BankOfCordaDriverKt___Run_Stack.xml | 15 --- build.gradle | 1 + constants.properties | 5 +- core/build.gradle | 2 +- gradle-plugins/build.gradle | 1 + gradle-plugins/cordform-common/build.gradle | 3 + .../corda/cordform/CordformDefinition.java | 28 +++- .../java/net/corda/cordform/CordformNode.java | 60 +++++++-- .../main/kotlin/net/corda/plugins/Cordform.kt | 47 ++++++- .../src/main/kotlin/net/corda/plugins/Node.kt | 38 +----- gradle/wrapper/gradle-wrapper.properties | 3 +- node/build.gradle | 2 - .../node/services/config/NodeConfiguration.kt | 1 + samples/bank-of-corda-demo/build.gradle | 58 +-------- .../net/corda/bank/BankOfCordaCordformTest.kt | 16 +++ .../net/corda/bank/BankOfCordaHttpAPITest.kt | 24 ---- .../corda/bank/BankOfCordaRPCClientTest.kt | 4 +- .../net/corda/bank/BankOfCordaCordform.kt | 123 ++++++++++++++++++ .../net/corda/bank/BankOfCordaDriver.kt | 122 ----------------- .../corda/bank/api/BankOfCordaClientApi.kt | 16 +-- .../net/corda/bank/api/BankOfCordaWebApi.kt | 16 ++- .../kotlin/net/corda/irs/IRSDemoTest.kt | 2 +- .../corda/irs/web/demo/IrsDemoClientApi.kt | 4 +- samples/notary-demo/build.gradle | 4 - .../net/corda/notarydemo/BFTNotaryCordform.kt | 7 +- .../corda/notarydemo/CustomNotaryCordform.kt | 7 +- .../corda/notarydemo/RaftNotaryCordform.kt | 6 +- .../corda/notarydemo/SingleNotaryCordform.kt | 15 +-- .../net/corda/vega/SimmValuationTest.kt | 6 +- .../kotlin/net/corda/testing/driver/Driver.kt | 37 +++--- .../testing/internal/demorun/DemoRunner.kt | 55 +++++++- .../net/corda/testing/http/HttpUtils.kt | 15 ++- 33 files changed, 395 insertions(+), 363 deletions(-) delete mode 100644 .idea/runConfigurations/BankOfCordaDriverKt___Issue_Web.xml delete mode 100644 .idea/runConfigurations/BankOfCordaDriverKt___Run_Stack.xml create mode 100644 samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaCordformTest.kt delete mode 100644 samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt create mode 100644 samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt delete mode 100644 samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt diff --git a/.idea/runConfigurations/BankOfCordaDriverKt___Issue_Web.xml b/.idea/runConfigurations/BankOfCordaDriverKt___Issue_Web.xml deleted file mode 100644 index 321d3d2d06..0000000000 --- a/.idea/runConfigurations/BankOfCordaDriverKt___Issue_Web.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations/BankOfCordaDriverKt___Run_Stack.xml b/.idea/runConfigurations/BankOfCordaDriverKt___Run_Stack.xml deleted file mode 100644 index ea61b6ef8d..0000000000 --- a/.idea/runConfigurations/BankOfCordaDriverKt___Run_Stack.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - \ No newline at end of file diff --git a/build.gradle b/build.gradle index d71ffe58cc..e72f20a51a 100644 --- a/build.gradle +++ b/build.gradle @@ -48,6 +48,7 @@ buildscript { ext.commons_collections_version = '4.1' ext.beanutils_version = '1.9.3' ext.crash_version = 'faba68332800f21278c5b600bf14ad55cef5989e' + ext.jsr305_version = constants.getProperty("jsr305Version") // Update 121 is required for ObjectInputFilter and at time of writing 131 was latest: ext.java8_minUpdateVersion = '131' diff --git a/constants.properties b/constants.properties index 211f29e563..7ee707fd23 100644 --- a/constants.properties +++ b/constants.properties @@ -1,6 +1,7 @@ -gradlePluginsVersion=2.0.9 +gradlePluginsVersion=3.0.0 kotlinVersion=1.1.60 platformVersion=2 guavaVersion=21.0 bouncycastleVersion=1.57 -typesafeConfigVersion=1.3.1 \ No newline at end of file +typesafeConfigVersion=1.3.1 +jsr305Version=3.0.2 diff --git a/core/build.gradle b/core/build.gradle index ab67d23248..bd443598d7 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -78,7 +78,7 @@ dependencies { compileOnly "co.paralleluniverse:quasar-core:$quasar_version:jdk8" // Thread safety annotations - compile "com.google.code.findbugs:jsr305:3.0.1" + compile "com.google.code.findbugs:jsr305:$jsr305_version" // Log4J: logging framework (ONLY explicitly referenced by net.corda.core.utilities.Logging.kt) compile "org.apache.logging.log4j:log4j-core:${log4j_version}" diff --git a/gradle-plugins/build.gradle b/gradle-plugins/build.gradle index 88169c215c..bfab124a56 100644 --- a/gradle-plugins/build.gradle +++ b/gradle-plugins/build.gradle @@ -10,6 +10,7 @@ buildscript { ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion") ext.bouncycastle_version = constants.getProperty("bouncycastleVersion") ext.typesafe_config_version = constants.getProperty("typesafeConfigVersion") + ext.jsr305_version = constants.getProperty("jsr305Version") ext.kotlin_version = constants.getProperty("kotlinVersion") repositories { diff --git a/gradle-plugins/cordform-common/build.gradle b/gradle-plugins/cordform-common/build.gradle index 82274e0d09..2423bcd773 100644 --- a/gradle-plugins/cordform-common/build.gradle +++ b/gradle-plugins/cordform-common/build.gradle @@ -11,6 +11,9 @@ version gradle_plugins_version group 'net.corda.plugins' dependencies { + // JSR 305: Nullability annotations + compile "com.google.code.findbugs:jsr305:$jsr305_version" + // TypeSafe Config: for simple and human friendly config files. compile "com.typesafe:config:$typesafe_config_version" diff --git a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformDefinition.java b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformDefinition.java index 047d7e6289..fc62b1bbee 100644 --- a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformDefinition.java +++ b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformDefinition.java @@ -1,24 +1,40 @@ package net.corda.cordform; +import javax.annotation.Nonnull; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; +import java.util.List; import java.util.function.Consumer; public abstract class CordformDefinition { - public final Path driverDirectory; - public final ArrayList> nodeConfigurers = new ArrayList<>(); + private Path nodesDirectory = Paths.get("build", "nodes"); + private final List> nodeConfigurers = new ArrayList<>(); + private final List cordappPackages = new ArrayList<>(); - public CordformDefinition(Path driverDirectory) { - this.driverDirectory = driverDirectory; + public Path getNodesDirectory() { + return nodesDirectory; } - public void addNode(Consumer configurer) { + public void setNodesDirectory(Path nodesDirectory) { + this.nodesDirectory = nodesDirectory; + } + + public List> getNodeConfigurers() { + return nodeConfigurers; + } + + public void addNode(Consumer configurer) { nodeConfigurers.add(configurer); } + public List getCordappPackages() { + return cordappPackages; + } + /** * Make arbitrary changes to the node directories before they are started. * @param context Lookup of node directory by node name. */ - public abstract void setup(CordformContext context); + public abstract void setup(@Nonnull CordformContext context); } diff --git a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java index f8c4248f99..33b433a506 100644 --- a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java +++ b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java @@ -2,6 +2,9 @@ package net.corda.cordform; import static java.util.Collections.emptyList; import com.typesafe.config.*; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.Collections; import java.util.List; import java.util.Map; @@ -54,7 +57,7 @@ public class CordformNode implements NodeDefinition { */ public void name(String name) { this.name = name; - config = config.withValue("myLegalName", ConfigValueFactory.fromAnyRef(name)); + setValue("myLegalName", name); } /** @@ -62,6 +65,7 @@ public class CordformNode implements NodeDefinition { * * @return This node's P2P address. */ + @Nonnull public String getP2pAddress() { return config.getString("p2pAddress"); } @@ -71,8 +75,8 @@ public class CordformNode implements NodeDefinition { * * @param p2pPort The Artemis messaging queue port. */ - public void p2pPort(Integer p2pPort) { - config = config.withValue("p2pAddress", ConfigValueFactory.fromAnyRef(DEFAULT_HOST + ':' + p2pPort)); + public void p2pPort(int p2pPort) { + p2pAddress(DEFAULT_HOST + ':' + p2pPort); } /** @@ -81,7 +85,15 @@ public class CordformNode implements NodeDefinition { * @param p2pAddress The Artemis messaging queue host and port. */ public void p2pAddress(String p2pAddress) { - config = config.withValue("p2pAddress", ConfigValueFactory.fromAnyRef(p2pAddress)); + setValue("p2pAddress", p2pAddress); + } + + /** + * Returns the RPC address for this node, or null if one hasn't been specified. + */ + @Nullable + public String getRpcAddress() { + return getOptionalString("rpcAddress"); } /** @@ -89,8 +101,8 @@ public class CordformNode implements NodeDefinition { * * @param rpcPort The Artemis RPC queue port. */ - public void rpcPort(Integer rpcPort) { - config = config.withValue("rpcAddress", ConfigValueFactory.fromAnyRef(DEFAULT_HOST + ':' + rpcPort)); + public void rpcPort(int rpcPort) { + rpcAddress(DEFAULT_HOST + ':' + rpcPort); } /** @@ -99,7 +111,31 @@ public class CordformNode implements NodeDefinition { * @param rpcAddress The Artemis RPC queue host and port. */ public void rpcAddress(String rpcAddress) { - config = config.withValue("rpcAddress", ConfigValueFactory.fromAnyRef(rpcAddress)); + setValue("rpcAddress", rpcAddress); + } + + /** + * Returns the address of the web server that will connect to the node, or null if one hasn't been specified. + */ + @Nullable + public String getWebAddress() { + return getOptionalString("webAddress"); + } + + /** + * Configure a webserver to connect to the node via RPC. This port will specify the port it will listen on. The node + * must have an RPC address configured. + */ + public void webPort(int webPort) { + webAddress(DEFAULT_HOST + ':' + webPort); + } + + /** + * Configure a webserver to connect to the node via RPC. This address will specify the port it will listen on. The node + * must have an RPC address configured. + */ + public void webAddress(String webAddress) { + setValue("webAddress", webAddress); } /** @@ -108,6 +144,14 @@ public class CordformNode implements NodeDefinition { * @param configFile The file path. */ public void configFile(String configFile) { - config = config.withValue("configFile", ConfigValueFactory.fromAnyRef(configFile)); + setValue("configFile", configFile); + } + + private String getOptionalString(String path) { + return config.hasPath(path) ? config.getString(path) : null; + } + + private void setValue(String path, Object value) { + config = config.withValue(path, ConfigValueFactory.fromAnyRef(value)); } } diff --git a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt index 057b1861b5..f43d39f6f4 100644 --- a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt +++ b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt @@ -11,10 +11,10 @@ import org.gradle.api.tasks.SourceSet.MAIN_SOURCE_SET_NAME import org.gradle.api.tasks.TaskAction import java.io.File import java.net.URLClassLoader -import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths import java.util.concurrent.TimeUnit +import java.util.jar.JarInputStream /** * Creates nodes based on the configuration of this task in the gradle configuration DSL. @@ -23,12 +23,16 @@ import java.util.concurrent.TimeUnit */ @Suppress("unused") open class Cordform : DefaultTask() { + private companion object { + private val defaultDirectory: Path = Paths.get("build", "nodes") + } + /** * Optionally the name of a CordformDefinition subclass to which all configuration will be delegated. */ @Suppress("MemberVisibilityCanPrivate") var definitionClass: String? = null - private var directory = Paths.get("build", "nodes") + private var directory = defaultDirectory private val nodes = mutableListOf() /** @@ -116,7 +120,6 @@ open class Cordform : DefaultTask() { /** * This task action will create and install the nodes based on the node configurations added. */ - @Suppress("unused") @TaskAction fun build() { project.logger.info("Running Cordform task") @@ -129,10 +132,18 @@ open class Cordform : DefaultTask() { private fun initializeConfiguration() { if (definitionClass != null) { val cd = loadCordformDefinition() + // If the user has specified their own directory (even if it's the same default path) then let them know + // it's not used and should just rely on the one in CordformDefinition + require(directory === defaultDirectory) { + "'directory' cannot be used when 'definitionClass' is specified. Use CordformDefinition.nodesDirectory instead." + } + directory = cd.nodesDirectory + val cordapps = cd.getMatchingCordapps() cd.nodeConfigurers.forEach { val node = node { } it.accept(node) node.rootDir(directory) + node.installCordapps(cordapps) } cd.setup { nodeName -> project.projectDir.toPath().resolve(getNodeByName(nodeName)!!.nodeDir.toPath()) } } else { @@ -142,6 +153,30 @@ open class Cordform : DefaultTask() { } } + private fun CordformDefinition.getMatchingCordapps(): List { + val cordappJars = project.configuration("cordapp").files + return cordappPackages.map { `package` -> + val cordappsWithPackage = cordappJars.filter { it.containsPackage(`package`) } + when (cordappsWithPackage.size) { + 0 -> throw IllegalArgumentException("There are no cordapp dependencies containing the package $`package`") + 1 -> cordappsWithPackage[0] + else -> throw IllegalArgumentException("More than one cordapp dependency contains the package $`package`: $cordappsWithPackage") + } + } + } + + private fun File.containsPackage(`package`: String): Boolean { + JarInputStream(inputStream()).use { + while (true) { + val name = it.nextJarEntry?.name ?: break + if (name.endsWith(".class") && name.replace('/', '.').startsWith(`package`)) { + return true + } + } + return false + } + } + private fun generateAndInstallNodeInfos() { generateNodeInfos() installNodeInfos() @@ -149,7 +184,7 @@ open class Cordform : DefaultTask() { private fun generateNodeInfos() { project.logger.info("Generating node infos") - var nodeProcesses = buildNodeProcesses() + val nodeProcesses = buildNodeProcesses() try { validateNodeProcessess(nodeProcesses) } finally { @@ -177,7 +212,7 @@ open class Cordform : DefaultTask() { private fun buildNodeProcess(node: Node): Pair { node.makeLogDirectory() - var process = ProcessBuilder(generateNodeInfoCommand()) + val process = ProcessBuilder(generateNodeInfoCommand()) .directory(node.fullPath().toFile()) .redirectErrorStream(true) // InheritIO causes hangs on windows due the gradle buffer also not being flushed. @@ -224,6 +259,8 @@ open class Cordform : DefaultTask() { } } } + private fun Node.logFile(): Path = this.logDirectory().resolve("generate-info.log") + private fun ProcessBuilder.addEnvironment(key: String, value: String) = this.apply { environment().put(key, value) } } diff --git a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt index 9358a293b5..90b1364126 100644 --- a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt +++ b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt @@ -3,7 +3,6 @@ package net.corda.plugins import com.typesafe.config.* import net.corda.cordform.CordformNode import org.bouncycastle.asn1.x500.X500Name -import org.bouncycastle.asn1.x500.RDN import org.bouncycastle.asn1.x500.style.BCStyle import org.gradle.api.Project import java.io.File @@ -39,6 +38,7 @@ class Node(private val project: Project) : CordformNode() { private val releaseVersion = project.rootProject.ext("corda_release_version") internal lateinit var nodeDir: File + private set /** * Sets whether this node will use HTTPS communication. @@ -60,26 +60,6 @@ class Node(private val project: Project) : CordformNode() { config = config.withValue("useTestClock", ConfigValueFactory.fromAnyRef(useTestClock)) } - /** - * Set the HTTP web server port for this node. Will use localhost as the address. - * - * @param webPort The web port number for this node. - */ - fun webPort(webPort: Int) { - config = config.withValue("webAddress", - ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$webPort")) - } - - /** - * Set the HTTP web server address and port for this node. - * - * @param webAddress The web address for this node. - */ - fun webAddress(webAddress: String) { - config = config.withValue("webAddress", - ConfigValueFactory.fromAnyRef(webAddress)) - } - /** * Set the network map address for this node. * @@ -104,7 +84,6 @@ class Node(private val project: Project) : CordformNode() { config = config.withValue("sshd.port", ConfigValueFactory.fromAnyRef(sshdPort)) } - internal fun build() { configureProperties() installCordaJar() @@ -118,19 +97,15 @@ class Node(private val project: Project) : CordformNode() { } internal fun rootDir(rootDir: Path) { - if(name == null) { + if (name == null) { project.logger.error("Node has a null name - cannot create node") throw IllegalStateException("Node has a null name - cannot create node") } val dirName = try { val o = X500Name(name).getRDNs(BCStyle.O) - if (o.size > 0) { - o.first().first.value.toString() - } else { - name - } - } catch(_ : IllegalArgumentException) { + if (o.isNotEmpty()) o.first().first.value.toString() else name + } catch (_ : IllegalArgumentException) { // Can't parse as an X500 name, use the full string name } @@ -192,9 +167,8 @@ class Node(private val project: Project) : CordformNode() { /** * Installs other cordapps to this node's cordapps directory. */ - private fun installCordapps() { + internal fun installCordapps(cordapps: Collection = getCordappList()) { val cordappsDir = File(nodeDir, "cordapps") - val cordapps = getCordappList() project.copy { it.apply { from(cordapps) @@ -280,7 +254,7 @@ class Node(private val project: Project) : CordformNode() { throw RuntimeException("No Corda Webserver JAR found. Have you deployed the Corda project to Maven? Looked for \"corda-webserver-$releaseVersion.jar\"") } else { val jar = maybeJar.singleFile - assert(jar.isFile) + require(jar.isFile) return jar } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 92165eede8..10cb7bb37b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Sat Nov 25 22:21:50 GMT 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip diff --git a/node/build.gradle b/node/build.gradle index 7e9adec300..07a4248964 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -54,8 +54,6 @@ dependencies { compile project(':client:rpc') compile "net.corda.plugins:cordform-common:$gradle_plugins_version" - compile "com.google.code.findbugs:jsr305:3.0.1" - // Log4J: logging framework (with SLF4J bindings) compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}" compile "org.apache.logging.log4j:log4j-web:${log4j_version}" diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index e3a915f6b1..86124da7fc 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -29,6 +29,7 @@ interface NodeConfiguration : NodeSSLConfiguration { val notary: NotaryConfig? val activeMQServer: ActiveMqServerConfiguration val additionalNodeInfoPollingFrequencyMsec: Long + // TODO Remove as this is only used by the driver val useHTTPS: Boolean val p2pAddress: NetworkHostAndPort val rpcAddress: NetworkHostAndPort? diff --git a/samples/bank-of-corda-demo/build.gradle b/samples/bank-of-corda-demo/build.gradle index 4d291f727a..b496155694 100644 --- a/samples/bank-of-corda-demo/build.gradle +++ b/samples/bank-of-corda-demo/build.gradle @@ -48,51 +48,8 @@ dependencies { } task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { - directory "./build/nodes" - // This name "Notary" is hard-coded into BankOfCordaClientApi so if you change it here, change it there too. - node { - name "O=Notary Service,L=Zurich,C=CH" - notary = [validating : true] - p2pPort 10002 - rpcPort 10003 - cordapps = ["$project.group:finance:$corda_release_version"] - } - node { - name "O=BankOfCorda,L=London,C=GB" - extraConfig = [issuableCurrencies : ["USD"]] - p2pPort 10005 - rpcPort 10006 - webPort 10007 - cordapps = ["$project.group:finance:$corda_release_version"] - rpcUsers = [ - ['username' : "bankUser", - 'password' : "test", - 'permissions': ["StartFlow.net.corda.finance.flows.CashPaymentFlow", - "StartFlow.net.corda.finance.flows.CashConfigDataFlow", - "StartFlow.net.corda.finance.flows.CashExitFlow", - "StartFlow.net.corda.finance.flows.CashIssueAndPaymentFlow", - "StartFlow.net.corda.finance.flows.CashConfigDataFlow", - "InvokeRpc.waitUntilNetworkReady", - "InvokeRpc.wellKnownPartyFromX500Name", - "InvokeRpc.notaryIdentities"]] - ] - } - node { - name "O=BigCorporation,L=New York,C=US" - p2pPort 10008 - rpcPort 10009 - webPort 10010 - cordapps = ["$project.group:finance:$corda_release_version"] - rpcUsers = [ - ['username' : "bigCorpUser", - 'password' : "test", - 'permissions': ["StartFlow.net.corda.finance.flows.CashPaymentFlow", - "StartFlow.net.corda.finance.flows.CashConfigDataFlow", - "InvokeRpc.waitUntilNetworkReady", - "InvokeRpc.wellKnownPartyFromX500Name", - "InvokeRpc.notaryIdentities"]] - ] - } + // CordformationDefinition is an experimental feature + definitionClass = 'net.corda.bank.BankOfCordaCordform' } task integrationTest(type: Test, dependsOn: []) { @@ -119,16 +76,9 @@ publishing { } } -task runIssuer(type: JavaExec) { - classpath = sourceSets.main.runtimeClasspath - main = 'net.corda.bank.BankOfCordaDriverKt' - args '--role' - args 'ISSUER' -} - task runRPCCashIssue(type: JavaExec) { classpath = sourceSets.main.runtimeClasspath - main = 'net.corda.bank.BankOfCordaDriverKt' + main = 'net.corda.bank.IssueCash' args '--role' args 'ISSUE_CASH_RPC' args '--quantity' @@ -139,7 +89,7 @@ task runRPCCashIssue(type: JavaExec) { task runWebCashIssue(type: JavaExec) { classpath = sourceSets.main.runtimeClasspath - main = 'net.corda.bank.BankOfCordaDriverKt' + main = 'net.corda.bank.IssueCash' args '--role' args 'ISSUE_CASH_WEB' args '--quantity' 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 new file mode 100644 index 0000000000..6890793aba --- /dev/null +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaCordformTest.kt @@ -0,0 +1,16 @@ +package net.corda.bank + +import net.corda.finance.DOLLARS +import net.corda.finance.POUNDS +import net.corda.testing.internal.demorun.deployNodesThen +import org.junit.Test + +class BankOfCordaCordformTest { + @Test + fun `run demo`() { + BankOfCordaCordform().deployNodesThen { + IssueCash.requestWebIssue(30000.POUNDS) + IssueCash.requestRpcIssue(20000.DOLLARS) + } + } +} diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt deleted file mode 100644 index 0f37be7cbc..0000000000 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package net.corda.bank - -import net.corda.bank.api.BankOfCordaClientApi -import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams -import net.corda.core.utilities.getOrThrow -import net.corda.testing.BOC -import net.corda.testing.driver.driver -import org.junit.Test -import kotlin.test.assertTrue - -class BankOfCordaHttpAPITest { - @Test - fun `issuer flow via Http`() { - driver(extraCordappPackagesToScan = listOf("net.corda.finance"), isDebug = true) { - val (bocNode) = listOf( - startNode(providedName = BOC.name), - startNode(providedName = BIGCORP_LEGAL_NAME) - ).map { it.getOrThrow() } - val bocApiAddress = startWebserver(bocNode).getOrThrow().listenAddress - val issueRequestParams = IssueRequestParams(1000, "USD", BIGCORP_LEGAL_NAME, "1", BOC.name, defaultNotaryIdentity.name) - assertTrue(BankOfCordaClientApi(bocApiAddress).requestWebIssue(issueRequestParams)) - } - } -} 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 4b2e6c2580..229d74ed4b 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 @@ -29,7 +29,7 @@ class BankOfCordaRPCClientTest { val bigCorpCFO = User("bigCorpCFO", "password2", permissions = emptySet() + commonPermissions) val (nodeBankOfCorda, nodeBigCorporation) = listOf( startNode(providedName = BOC.name, rpcUsers = listOf(bocManager)), - startNode(providedName = BIGCORP_LEGAL_NAME, rpcUsers = listOf(bigCorpCFO)) + startNode(providedName = BIGCORP_NAME, rpcUsers = listOf(bigCorpCFO)) ).map { it.getOrThrow() } // Bank of Corda RPC Client @@ -47,7 +47,7 @@ class BankOfCordaRPCClientTest { // Register for Big Corporation Vault updates val vaultUpdatesBigCorp = bigCorpProxy.vaultTrackByCriteria(Cash.State::class.java, criteria).updates - val bigCorporation = bigCorpProxy.wellKnownPartyFromX500Name(BIGCORP_LEGAL_NAME)!! + val bigCorporation = bigCorpProxy.wellKnownPartyFromX500Name(BIGCORP_NAME)!! // Kick-off actual Issuer Flow val anonymous = true 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 new file mode 100644 index 0000000000..24dda81869 --- /dev/null +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt @@ -0,0 +1,123 @@ +package net.corda.bank + +import joptsimple.OptionParser +import net.corda.bank.api.BankOfCordaClientApi +import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams +import net.corda.cordform.CordformContext +import net.corda.cordform.CordformDefinition +import net.corda.core.contracts.Amount +import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.VisibleForTesting +import net.corda.core.transactions.SignedTransaction +import net.corda.core.utilities.NetworkHostAndPort +import net.corda.node.services.Permissions.Companion.all +import net.corda.node.services.config.NotaryConfig +import net.corda.node.services.transactions.ValidatingNotaryService +import net.corda.nodeapi.User +import net.corda.testing.BOC +import net.corda.testing.internal.demorun.* +import java.util.* +import kotlin.system.exitProcess + +val BIGCORP_NAME = CordaX500Name(organisation = "BigCorporation", locality = "New York", country = "US") +private val NOTARY_NAME = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH") +private val BOC_RPC_PORT = 10006 +private val BOC_WEB_PORT = 10007 + +class BankOfCordaCordform : CordformDefinition() { + init { + cordappPackages += "net.corda.finance" + node { + name(NOTARY_NAME) + notary(NotaryConfig(validating = true)) + p2pPort(10002) + rpcPort(10003) + } + node { + name(CordaX500Name(organisation = "BankOfCorda", locality = "London", country = "GB")) + extraConfig = mapOf("issuableCurrencies" to listOf("USD")) + p2pPort(10005) + rpcPort(BOC_RPC_PORT) + webPort(BOC_WEB_PORT) + rpcUsers(User("bankUser", "test", setOf(all()))) + } + node { + name(BIGCORP_NAME) + p2pPort(10008) + rpcPort(10009) + webPort(10010) + rpcUsers(User("bigCorpUser", "test", setOf(all()))) + } + } + + override fun setup(context: CordformContext) = Unit +} + +object DeployNodes { + @JvmStatic + fun main(args: Array) { + BankOfCordaCordform().deployNodes() + } +} + +object IssueCash { + @JvmStatic + fun main(args: Array) { + val parser = OptionParser() + val roleArg = parser.accepts("role").withRequiredArg().ofType(Role::class.java).describedAs("[ISSUER|ISSUE_CASH_RPC|ISSUE_CASH_WEB]") + val quantity = parser.accepts("quantity").withOptionalArg().ofType(Long::class.java) + val currency = parser.accepts("currency").withOptionalArg().ofType(String::class.java).describedAs("[GBP|USD|CHF|EUR]") + val options = try { + parser.parse(*args) + } catch (e: Exception) { + println(e.message) + printHelp(parser) + exitProcess(1) + } + + val role = options.valueOf(roleArg)!! + val amount = Amount(options.valueOf(quantity), Currency.getInstance(options.valueOf(currency))) + when (role) { + Role.ISSUE_CASH_RPC -> { + println("Requesting Cash via RPC ...") + val result = requestRpcIssue(amount) + println("Success!! Your transaction receipt is ${result.tx.id}") + } + Role.ISSUE_CASH_WEB -> { + println("Requesting Cash via Web ...") + requestWebIssue(amount) + println("Successfully processed Cash Issue request") + } + } + } + + @VisibleForTesting + fun requestRpcIssue(amount: Amount): SignedTransaction { + return BankOfCordaClientApi.requestRPCIssue(NetworkHostAndPort("localhost", BOC_RPC_PORT), createParams(amount, NOTARY_NAME)) + } + + @VisibleForTesting + fun requestWebIssue(amount: Amount) { + BankOfCordaClientApi.requestWebIssue(NetworkHostAndPort("localhost", BOC_WEB_PORT), createParams(amount, NOTARY_NAME)) + } + + private fun createParams(amount: Amount, notaryName: CordaX500Name): IssueRequestParams { + return IssueRequestParams(amount, BIGCORP_NAME, "1", BOC.name, notaryName.copy(commonName = ValidatingNotaryService.id)) + } + + private fun printHelp(parser: OptionParser) { + println(""" + Usage: bank-of-corda --role ISSUER + bank-of-corda --role (ISSUE_CASH_RPC|ISSUE_CASH_WEB) --quantity --currency + + Please refer to the documentation in docs/build/index.html for more info. + + """.trimIndent()) + parser.printHelpOn(System.out) + } + + enum class Role { + ISSUE_CASH_RPC, + ISSUE_CASH_WEB, + } +} diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt deleted file mode 100644 index 00ada75a26..0000000000 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt +++ /dev/null @@ -1,122 +0,0 @@ -package net.corda.bank - -import joptsimple.OptionParser -import net.corda.bank.api.BankOfCordaClientApi -import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams -import net.corda.core.identity.CordaX500Name -import net.corda.core.utilities.NetworkHostAndPort -import net.corda.finance.flows.CashConfigDataFlow -import net.corda.finance.flows.CashExitFlow -import net.corda.finance.flows.CashIssueAndPaymentFlow -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.User -import net.corda.testing.BOC -import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.driver.driver -import kotlin.system.exitProcess - -/** - * This entry point allows for command line running of the Bank of Corda functions on nodes started by BankOfCordaDriver.kt. - */ -fun main(args: Array) { - BankOfCordaDriver().main(args) -} - -const val BANK_USERNAME = "bankUser" -const val BIGCORP_USERNAME = "bigCorpUser" - -val BIGCORP_LEGAL_NAME = CordaX500Name(organisation = "BigCorporation", locality = "New York", country = "US") - -private class BankOfCordaDriver { - enum class Role { - ISSUE_CASH_RPC, - ISSUE_CASH_WEB, - ISSUER - } - - fun main(args: Array) { - val parser = OptionParser() - val roleArg = parser.accepts("role").withRequiredArg().ofType(Role::class.java).describedAs("[ISSUER|ISSUE_CASH_RPC|ISSUE_CASH_WEB]") - val quantity = parser.accepts("quantity").withOptionalArg().ofType(Long::class.java) - val currency = parser.accepts("currency").withOptionalArg().ofType(String::class.java).describedAs("[GBP|USD|CHF|EUR]") - val options = try { - parser.parse(*args) - } catch (e: Exception) { - println(e.message) - printHelp(parser) - exitProcess(1) - } - - // What happens next depends on the role. - // The ISSUER will launch a Bank of Corda node - // The ISSUE_CASH will request some Cash from the ISSUER on behalf of Big Corporation node - val role = options.valueOf(roleArg)!! - - try { - when (role) { - Role.ISSUER -> { - driver(isDebug = true, extraCordappPackagesToScan = listOf("net.corda.finance.contracts.asset"), waitForAllNodesToFinish = true) { - val bankUser = User( - BANK_USERNAME, - "test", - permissions = setOf( - startFlow(), - startFlow(), - startFlow(), - startFlow(), - startFlow(), - all() - )) - val bankOfCorda = startNode( - providedName = BOC.name, - rpcUsers = listOf(bankUser)) - val bigCorpUser = User(BIGCORP_USERNAME, "test", - permissions = setOf( - startFlow(), - startFlow(), - all())) - startNode(providedName = BIGCORP_LEGAL_NAME, rpcUsers = listOf(bigCorpUser)) - startWebserver(bankOfCorda.get()) - } - } - else -> { - val requestParams = IssueRequestParams(options.valueOf(quantity), options.valueOf(currency), BIGCORP_LEGAL_NAME, - "1", BOC.name, DUMMY_NOTARY.name) - when(role) { - Role.ISSUE_CASH_RPC -> { - println("Requesting Cash via RPC ...") - val result = BankOfCordaClientApi(NetworkHostAndPort("localhost", 10004)).requestRPCIssue(requestParams) - println("Success!! You transaction receipt is ${result.tx.id}") - } - Role.ISSUE_CASH_WEB -> { - println("Requesting Cash via Web ...") - val result = BankOfCordaClientApi(NetworkHostAndPort("localhost", 10005)).requestWebIssue(requestParams) - if (result) - println("Successfully processed Cash Issue request") - } - else -> { - throw IllegalArgumentException("Unrecognized role: " + role) - } - } - } - } - } catch (e: Exception) { - println("Exception occurred: $e \n ${e.printStackTrace()}") - exitProcess(1) - } - } - - fun printHelp(parser: OptionParser) { - println(""" - Usage: bank-of-corda --role ISSUER - bank-of-corda --role (ISSUE_CASH_RPC|ISSUE_CASH_WEB) --quantity --currency - - Please refer to the documentation in docs/build/index.html for more info. - - """.trimIndent()) - parser.printHelpOn(System.out) - } -} - diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt index a50e99b727..ac0a136f7c 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt @@ -15,15 +15,14 @@ import java.util.* /** * Interface for communicating with Bank of Corda node */ -class BankOfCordaClientApi(val hostAndPort: NetworkHostAndPort) { - private val apiRoot = "api/bank" +object BankOfCordaClientApi { /** * HTTP API */ // TODO: security controls required - fun requestWebIssue(params: IssueRequestParams): Boolean { - val api = HttpApi.fromHostAndPort(hostAndPort, apiRoot) - return api.postJson("issue-asset-request", params) + fun requestWebIssue(webAddress: NetworkHostAndPort, params: IssueRequestParams) { + val api = HttpApi.fromHostAndPort(webAddress, "api/bank") + api.postJson("issue-asset-request", params) } /** @@ -31,8 +30,8 @@ class BankOfCordaClientApi(val hostAndPort: NetworkHostAndPort) { * * @return a pair of the issuing and payment transactions. */ - fun requestRPCIssue(params: IssueRequestParams): SignedTransaction { - val client = CordaRPCClient(hostAndPort) + fun requestRPCIssue(rpcAddress: NetworkHostAndPort, params: IssueRequestParams): SignedTransaction { + val client = CordaRPCClient(rpcAddress) // TODO: privileged security controls required client.start("bankUser", "test").use { connection -> val rpc = connection.proxy @@ -44,11 +43,10 @@ class BankOfCordaClientApi(val hostAndPort: NetworkHostAndPort) { val notaryLegalIdentity = rpc.notaryIdentities().firstOrNull { it.name == params.notaryName } ?: throw IllegalStateException("Couldn't locate notary ${params.notaryName} in NetworkMapCache") - val amount = Amount(params.amount, Currency.getInstance(params.currency)) val anonymous = true val issuerBankPartyRef = OpaqueBytes.of(params.issuerBankPartyRef.toByte()) - return rpc.startFlow(::CashIssueAndPaymentFlow, amount, issuerBankPartyRef, issueToParty, anonymous, notaryLegalIdentity) + return rpc.startFlow(::CashIssueAndPaymentFlow, params.amount, issuerBankPartyRef, issueToParty, anonymous, notaryLegalIdentity) .returnValue.getOrThrow().stx } } diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt index 30614d0cdf..25daa53476 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt @@ -16,11 +16,14 @@ import javax.ws.rs.core.Response // API is accessible from /api/bank. All paths specified below are relative to it. @Path("bank") -class BankOfCordaWebApi(val rpc: CordaRPCOps) { - data class IssueRequestParams(val amount: Long, val currency: String, - val issueToPartyName: CordaX500Name, val issuerBankPartyRef: String, - val issuerBankName: CordaX500Name, - val notaryName: CordaX500Name) +class BankOfCordaWebApi(private val rpc: CordaRPCOps) { + data class IssueRequestParams( + val amount: Amount, + val issueToPartyName: CordaX500Name, + val issuerBankPartyRef: String, + val issuerBankName: CordaX500Name, + val notaryName: CordaX500Name + ) private companion object { private val logger = contextLogger() @@ -47,14 +50,13 @@ class BankOfCordaWebApi(val rpc: CordaRPCOps) { val notaryParty = rpc.notaryIdentities().firstOrNull { it.name == params.notaryName } ?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate notary ${params.notaryName} in network map").build() - val amount = Amount(params.amount, Currency.getInstance(params.currency)) val anonymous = true val issuerBankPartyRef = OpaqueBytes.of(params.issuerBankPartyRef.toByte()) // invoke client side of Issuer Flow: IssuanceRequester // The line below blocks and waits for the future to resolve. return try { - rpc.startFlow(::CashIssueAndPaymentFlow, amount, issuerBankPartyRef, issueToParty, anonymous, notaryParty).returnValue.getOrThrow() + rpc.startFlow(::CashIssueAndPaymentFlow, params.amount, issuerBankPartyRef, issueToParty, anonymous, notaryParty).returnValue.getOrThrow() logger.info("Issue and payment request completed successfully: $params") Response.status(Response.Status.CREATED).build() } catch (e: Exception) { 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 d924e41287..f47c16a623 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 @@ -117,7 +117,7 @@ class IRSDemoTest { log.info("Running trade against ${nodeApi.root}") val fileContents = loadResourceFile("net/corda/irs/web/simulation/example-irs-trade.json") val tradeFile = fileContents.replace("tradeXXX", "trade1").replace("oracleXXX", oracle.name.toString()) - assertThat(nodeApi.postJson("deals", tradeFile)).isTrue() + nodeApi.postJson("deals", tradeFile) } private fun runUploadRates(nodeApi: HttpApi) { diff --git a/samples/irs-demo/web/src/test/kotlin/net/corda/irs/web/demo/IrsDemoClientApi.kt b/samples/irs-demo/web/src/test/kotlin/net/corda/irs/web/demo/IrsDemoClientApi.kt index 5c434cdb19..82e81de047 100644 --- a/samples/irs-demo/web/src/test/kotlin/net/corda/irs/web/demo/IrsDemoClientApi.kt +++ b/samples/irs-demo/web/src/test/kotlin/net/corda/irs/web/demo/IrsDemoClientApi.kt @@ -11,10 +11,10 @@ import org.apache.commons.io.IOUtils class IRSDemoClientApi(private val hostAndPort: NetworkHostAndPort) { private val api = HttpApi.fromHostAndPort(hostAndPort, apiRoot) - fun runTrade(tradeId: String, oracleName: CordaX500Name): Boolean { + fun runTrade(tradeId: String, oracleName: CordaX500Name) { val fileContents = IOUtils.toString(javaClass.classLoader.getResourceAsStream("net/corda/irs/simulation/example-irs-trade.json"), Charsets.UTF_8.name()) val tradeFile = fileContents.replace("tradeXXX", tradeId).replace("oracleXXX", oracleName.toString()) - return api.postJson("deals", tradeFile) + api.postJson("deals", tradeFile) } fun runDateChange(newDate: String): Boolean { diff --git a/samples/notary-demo/build.gradle b/samples/notary-demo/build.gradle index 4d3f97f065..84ba21d268 100644 --- a/samples/notary-demo/build.gradle +++ b/samples/notary-demo/build.gradle @@ -49,22 +49,18 @@ publishing { task deployNodes(dependsOn: ['deployNodesSingle', 'deployNodesRaft', 'deployNodesBFT', 'deployNodesCustom']) task deployNodesSingle(type: Cordform, dependsOn: 'jar') { - directory "./build/nodes/nodesSingle" definitionClass = 'net.corda.notarydemo.SingleNotaryCordform' } task deployNodesCustom(type: Cordform, dependsOn: 'jar') { - directory "./build/nodes/nodesCustom" definitionClass = 'net.corda.notarydemo.CustomNotaryCordform' } task deployNodesRaft(type: Cordform, dependsOn: 'jar') { - directory "./build/nodes/nodesRaft" definitionClass = 'net.corda.notarydemo.RaftNotaryCordform' } task deployNodesBFT(type: Cordform, dependsOn: 'jar') { - directory "./build/nodes/nodesBFT" definitionClass = 'net.corda.notarydemo.BFTNotaryCordform' } 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 e0919a43ab..99e158b723 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 @@ -4,7 +4,6 @@ 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.utilities.NetworkHostAndPort import net.corda.node.services.config.BFTSMaRtConfiguration import net.corda.node.services.config.NotaryConfig @@ -14,18 +13,20 @@ import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.internal.demorun.* +import java.nio.file.Paths -fun main(args: Array) = BFTNotaryCordform().runNodes() +fun main(args: Array) = BFTNotaryCordform().deployNodes() private val clusterSize = 4 // Minimum size that tolerates a faulty replica. private val notaryNames = createNotaryNames(clusterSize) // This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO // NOT use this as a design to copy. -class BFTNotaryCordform : CordformDefinition("build" / "notary-demo-nodes") { +class BFTNotaryCordform : CordformDefinition() { private val clusterName = CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH") init { + nodesDirectory = Paths.get("build", "nodes", "nodesBFT") node { name(ALICE.name) p2pPort(10002) 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 3cd43d7a0e..8117ef9a60 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 @@ -2,17 +2,18 @@ package net.corda.notarydemo import net.corda.cordform.CordformContext import net.corda.cordform.CordformDefinition -import net.corda.core.internal.div 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.internal.demorun.* +import java.nio.file.Paths -fun main(args: Array) = CustomNotaryCordform().runNodes() +fun main(args: Array) = CustomNotaryCordform().deployNodes() -class CustomNotaryCordform : CordformDefinition("build" / "notary-demo-nodes") { +class CustomNotaryCordform : CordformDefinition() { init { + nodesDirectory = Paths.get("build", "nodes", "nodesCustom") node { name(ALICE.name) p2pPort(10002) 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 ff128c6edf..e6c6f54595 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 @@ -13,8 +13,9 @@ import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.internal.demorun.* +import java.nio.file.Paths -fun main(args: Array) = RaftNotaryCordform().runNodes() +fun main(args: Array) = RaftNotaryCordform().deployNodes() internal fun createNotaryNames(clusterSize: Int) = (0 until clusterSize).map { CordaX500Name("Notary Service $it", "Zurich", "CH") } @@ -22,10 +23,11 @@ private val notaryNames = createNotaryNames(3) // This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO // NOT use this as a design to copy. -class RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes") { +class RaftNotaryCordform : CordformDefinition() { private val clusterName = CordaX500Name(RaftValidatingNotaryService.id, "Raft", "Zurich", "CH") init { + nodesDirectory = Paths.get("build", "nodes", "nodesRaft") node { name(ALICE.name) p2pPort(10002) 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 97168834c0..23381b72d1 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 @@ -2,29 +2,24 @@ package net.corda.notarydemo import net.corda.cordform.CordformContext import net.corda.cordform.CordformDefinition -import net.corda.core.internal.div import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.config.NotaryConfig import net.corda.nodeapi.User -import net.corda.notarydemo.flows.DummyIssueAndMove -import net.corda.notarydemo.flows.RPCStartableNotaryFlowClient import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.internal.demorun.name -import net.corda.testing.internal.demorun.node -import net.corda.testing.internal.demorun.notary -import net.corda.testing.internal.demorun.rpcUsers -import net.corda.testing.internal.demorun.runNodes +import net.corda.testing.internal.demorun.* +import java.nio.file.Paths -fun main(args: Array) = SingleNotaryCordform().runNodes() +fun main(args: Array) = SingleNotaryCordform().deployNodes() val notaryDemoUser = User("demou", "demop", setOf(all())) // This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO // NOT use this as a design to copy. -class SingleNotaryCordform : CordformDefinition("build" / "notary-demo-nodes") { +class SingleNotaryCordform : CordformDefinition() { init { + nodesDirectory = Paths.get("build", "nodes", "nodesSingle") node { name(ALICE.name) p2pPort(10002) 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 d09d8f5a8c..2b74779a1c 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 @@ -41,7 +41,7 @@ class SimmValuationTest { assertThat(createTradeBetween(nodeAApi, nodeBParty, testTradeId)).isTrue() assertTradeExists(nodeBApi, nodeAParty, testTradeId) assertTradeExists(nodeAApi, nodeBParty, testTradeId) - assertThat(runValuationsBetween(nodeAApi, nodeBParty)).isTrue() + runValuationsBetween(nodeAApi, nodeBParty) assertValuationExists(nodeBApi, nodeAParty) assertValuationExists(nodeAApi, nodeBParty) } @@ -66,8 +66,8 @@ class SimmValuationTest { assertThat(trades).filteredOn { it.id == tradeId }.isNotEmpty() } - private fun runValuationsBetween(partyApi: HttpApi, counterparty: PortfolioApi.ApiParty): Boolean { - return partyApi.postJson("${counterparty.id}/portfolio/valuations/calculate", PortfolioApi.ValuationCreationParams(valuationDate)) + private fun runValuationsBetween(partyApi: HttpApi, counterparty: PortfolioApi.ApiParty) { + partyApi.postJson("${counterparty.id}/portfolio/valuations/calculate", PortfolioApi.ValuationCreationParams(valuationDate)) } private fun assertValuationExists(partyApi: HttpApi, counterparty: PortfolioApi.ApiParty) { 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 d062cc9163..eab1ebd159 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 @@ -161,12 +161,6 @@ interface DriverDSLExposedInterface : CordformContext { */ fun startNode(parameters: NodeParameters): CordaFuture = startNode(defaultParameters = parameters) - fun startNodes( - nodes: List, - startInSameProcess: Boolean? = null, - maximumHeapSize: String = "200m" - ): List> - /** Call [startWebserver] with a default maximumHeapSize. */ fun startWebserver(handle: NodeHandle): CordaFuture = startWebserver(handle, "200m") @@ -672,22 +666,21 @@ class DriverDSL( return startNodeInternal(config, webAddress, startInSameProcess, maximumHeapSize) } - override fun startNodes(nodes: List, startInSameProcess: Boolean?, maximumHeapSize: String): List> { - return nodes.map { node -> - portAllocation.nextHostAndPort() // rpcAddress - val webAddress = portAllocation.nextHostAndPort() - val name = CordaX500Name.parse(node.name) - val rpcUsers = node.rpcUsers - val notary = if (node.notary != null) mapOf("notary" to node.notary) else emptyMap() - val config = ConfigHelper.loadConfig( - baseDirectory = baseDirectory(name), - allowMissingConfig = true, - configOverrides = node.config + notary + mapOf( - "rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers - ) - ) - startNodeInternal(config, webAddress, startInSameProcess, maximumHeapSize) - } + internal fun startCordformNode(cordform: CordformNode): CordaFuture { + val name = CordaX500Name.parse(cordform.name) + // TODO We shouldn't have to allocate an RPC or web address if they're not specified. We're having to do this because of startNodeInternal + val rpcAddress = if (cordform.rpcAddress == null) mapOf("rpcAddress" to portAllocation.nextHostAndPort().toString()) else emptyMap() + val webAddress = cordform.webAddress?.let { NetworkHostAndPort.parse(it) } ?: portAllocation.nextHostAndPort() + val notary = if (cordform.notary != null) mapOf("notary" to cordform.notary) else emptyMap() + val rpcUsers = cordform.rpcUsers + val config = ConfigHelper.loadConfig( + baseDirectory = baseDirectory(name), + allowMissingConfig = true, + configOverrides = cordform.config + rpcAddress + notary + mapOf( + "rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers + ) + ) + return startNodeInternal(config, webAddress, null, "200m") } private fun queryWebserver(handle: NodeHandle, process: Process): WebserverHandle { 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/internal/demorun/DemoRunner.kt index 188b6a30de..5155a31850 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/internal/demorun/DemoRunner.kt @@ -4,20 +4,63 @@ package net.corda.testing.internal.demorun import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode +import net.corda.core.internal.concurrent.flatMap +import net.corda.core.internal.concurrent.transpose +import net.corda.core.utilities.NetworkHostAndPort +import net.corda.core.utilities.getOrThrow +import net.corda.testing.driver.DriverDSL import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver fun CordformDefinition.clean() { - System.err.println("Deleting: $driverDirectory") - driverDirectory.toFile().deleteRecursively() + System.err.println("Deleting: $nodesDirectory") + nodesDirectory.toFile().deleteRecursively() } /** - * Creates and starts all nodes required for the demo. + * Deploy the nodes specified in the given [CordformDefinition]. This will block until all the nodes and webservers + * have terminated. */ -fun CordformDefinition.runNodes() { - driver(isDebug = true, driverDirectory = driverDirectory, portAllocation = PortAllocation.Incremental(10001), waitForAllNodesToFinish = true) { +fun CordformDefinition.deployNodes() { + runNodes(waitForAllNodesToFinish = true) { } +} + +/** + * Deploy the nodes specified in the given [CordformDefinition] and then execute the given [block] once all the nodes + * and webservers are up. After execution all these processes will be terminated. + */ +fun CordformDefinition.deployNodesThen(block: () -> Unit) { + runNodes(waitForAllNodesToFinish = false, block = block) +} + +private fun CordformDefinition.runNodes(waitForAllNodesToFinish: Boolean, block: () -> Unit) { + val nodes = nodeConfigurers.map { configurer -> CordformNode().also { configurer.accept(it) } } + val maxPort = nodes + .flatMap { listOf(it.p2pAddress, it.rpcAddress, it.webAddress) } + .mapNotNull { address -> address?.let { NetworkHostAndPort.parse(it).port } } + .max()!! + driver( + isDebug = true, + driverDirectory = nodesDirectory, + extraCordappPackagesToScan = cordappPackages, + // Notaries are manually specified in Cordform so we don't want the driver automatically starting any + notarySpecs = emptyList(), + // Start from after the largest port used to prevent port clash + portAllocation = PortAllocation.Incremental(maxPort + 1), + waitForAllNodesToFinish = waitForAllNodesToFinish + ) { setup(this) - startNodes(nodeConfigurers.map { configurer -> CordformNode().also { configurer.accept(it) } }) + this as DriverDSL // startCordformNode is an internal API + nodes.map { + val startedNode = startCordformNode(it) + if (it.webAddress != null) { + // Start a webserver if an address for it was specified + startedNode.flatMap { startWebserver(it) } + } else { + startedNode + } + }.transpose().getOrThrow() // Only proceed once everything is up and running + println("All nodes and webservers are ready...") + block() } } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt index 327f85193b..cf1af9b9ab 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt @@ -6,6 +6,7 @@ import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody import org.slf4j.LoggerFactory +import java.io.IOException import java.net.URL import java.util.concurrent.TimeUnit @@ -14,11 +15,13 @@ import java.util.concurrent.TimeUnit */ object HttpUtils { private val logger = LoggerFactory.getLogger(javaClass) + private val client by lazy { OkHttpClient.Builder() .connectTimeout(5, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS).build() } + val defaultMapper: ObjectMapper by lazy { net.corda.client.jackson.JacksonSupport.createNonRpcMapper() } @@ -28,9 +31,9 @@ object HttpUtils { return makeRequest(Request.Builder().url(url).header("Content-Type", "application/json").put(body).build()) } - fun postJson(url: URL, data: String): Boolean { + fun postJson(url: URL, data: String) { val body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), data) - return makeRequest(Request.Builder().url(url).header("Content-Type", "application/json").post(body).build()) + makeExceptionalRequest(Request.Builder().url(url).header("Content-Type", "application/json").post(body).build()) } fun postPlain(url: URL, data: String): Boolean { @@ -44,6 +47,14 @@ object HttpUtils { return mapper.readValue(parameterisedUrl, T::class.java) } + // TODO Move everything to use this instead of makeRequest + private fun makeExceptionalRequest(request: Request) { + val response = client.newCall(request).execute() + if (!response.isSuccessful) { + throw IOException("${request.method()} to ${request.url()} returned a ${response.code()}: ${response.body().string()}") + } + } + private fun makeRequest(request: Request): Boolean { val response = client.newCall(request).execute() From 2525fb52be739aec7f833532807e176c02819c21 Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Wed, 29 Nov 2017 17:07:13 +0000 Subject: [PATCH 15/27] New docs on deploying a node. Makes it clear existing docs were only for local or dev deployments. --- docs/source/corda-configuration-file.rst | 38 +-- docs/source/corda-nodes-index.rst | 3 +- docs/source/cordapp-build-systems.rst | 8 +- docs/source/deploying-a-node.rst | 321 +++++++++++++++-------- docs/source/generating-a-node.rst | 136 ++++++++++ docs/source/running-a-node.rst | 48 +++- docs/source/shell.rst | 2 +- docs/source/troubleshooting.rst | 3 +- docs/source/tutorial-cordapp.rst | 2 +- 9 files changed, 413 insertions(+), 148 deletions(-) create mode 100644 docs/source/generating-a-node.rst diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index 42af820033..346d92ff90 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -102,24 +102,26 @@ path to the node's base directory. :notary: Optional configuration object which if present configures the node to run as a notary. If part of a Raft or BFT SMaRt cluster then specify ``raft`` or ``bftSMaRt`` respectively as described below. If a single node notary then omit both. - :validating: Boolean to determine whether the notary is a validating or non-validating one. + :validating: Boolean to determine whether the notary is a validating or non-validating one. - :raft: If part of a distributed Raft cluster specify this config object, with the following settings: + :raft: If part of a distributed Raft cluster specify this config object, with the following settings: - :nodeAddress: The host and port to which to bind the embedded Raft server. Note that the Raft cluster uses a - separate transport layer for communication that does not integrate with ArtemisMQ messaging services. + :nodeAddress: The host and port to which to bind the embedded Raft server. Note that the Raft cluster uses a + separate transport layer for communication that does not integrate with ArtemisMQ messaging services. - :clusterAddresses: List of Raft cluster member addresses used to join the cluster. At least one of the specified - members must be active and be able to communicate with the cluster leader for joining. If empty, a new - cluster will be bootstrapped. + :clusterAddresses: Must list the addresses of all the members in the cluster. At least one of the members must + be active and be able to communicate with the cluster leader for the node to join the cluster. If empty, a + new cluster will be bootstrapped. - :bftSMaRt: If part of a distributed BFT-SMaRt cluster specify this config object, with the following settings: + :bftSMaRt: If part of a distributed BFT-SMaRt cluster specify this config object, with the following settings: - :replicaId: The zero-based index of the current replica. All replicas must specify a unique replica id. + :replicaId: The zero-based index of the current replica. All replicas must specify a unique replica id. - :clusterAddresses: List of all BFT-SMaRt cluster member addresses. + :clusterAddresses: Must list the addresses of all the members in the cluster. At least one of the members must + be active and be able to communicate with the cluster leader for the node to join the cluster. If empty, a + new cluster will be bootstrapped. - :custom: If `true`, will load and install a notary service from a CorDapp. See :doc:`tutorial-custom-notary`. + :custom: If `true`, will load and install a notary service from a CorDapp. See :doc:`tutorial-custom-notary`. Only one of ``raft``, ``bftSMaRt`` or ``custom`` configuration values may be specified. @@ -130,12 +132,12 @@ path to the node's base directory. :rpcUsers: A list of users who are authorised to access the RPC system. Each user in the list is a config object with the following fields: - :username: Username consisting only of word characters (a-z, A-Z, 0-9 and _) - :password: The password - :permissions: A list of permission strings which RPC methods can use to control access - - If this field is absent or an empty list then RPC is effectively locked down. Alternatively, if it contains the string - ``ALL`` then the user is permitted to use *any* RPC method. This value is intended for administrator users and for developers. + :username: Username consisting only of word characters (a-z, A-Z, 0-9 and _) + :password: The password + :permissions: A list of permissions for starting flows via RPC. To give the user the permission to start the flow + ``foo.bar.FlowClass``, add the string ``StartFlow.foo.bar.FlowClass`` to the list. If the list + contains the string ``ALL``, the user can start any flow via RPC. This value is intended for administrator + users and for development. :devMode: This flag sets the node to run in development mode. On startup, if the keystore ``/certificates/sslkeystore.jks`` does not exist, a developer keystore will be used if ``devMode`` is true. The node will exit if ``devMode`` is false @@ -164,4 +166,4 @@ path to the node's base directory. :sshd: If provided, node will start internal SSH server which will provide a management shell. It uses the same credentials and permissions as RPC subsystem. It has one required parameter. - :port: - the port to start SSH server on + :port: The port to start SSH server on diff --git a/docs/source/corda-nodes-index.rst b/docs/source/corda-nodes-index.rst index 16a95a19a1..c1dfa0b508 100644 --- a/docs/source/corda-nodes-index.rst +++ b/docs/source/corda-nodes-index.rst @@ -4,8 +4,9 @@ Corda nodes .. toctree:: :maxdepth: 1 - deploying-a-node + generating-a-node running-a-node + deploying-a-node corda-configuration-file clientrpc shell diff --git a/docs/source/cordapp-build-systems.rst b/docs/source/cordapp-build-systems.rst index a203f75160..bfbe08662c 100644 --- a/docs/source/cordapp-build-systems.rst +++ b/docs/source/cordapp-build-systems.rst @@ -118,9 +118,9 @@ Creating the CorDapp JAR The gradle ``jar`` task included in the CorDapp template build file will automatically build your CorDapp JAR correctly as long as your dependencies are set correctly. -Note that the hash of the resulting CorDapp JAR is not deterministic, as it depends on variables such as the timestamp -at creation. Nodes running the same CorDapp must therefore ensure they are using the exact same CorDapp jar, and not -different versions of the JAR created from identical sources. +.. warning:: The hash of the generated CorDapp JAR is not deterministic, as it depends on variables such as the + timestamp at creation. Nodes running the same CorDapp must therefore ensure they are using the exact same CorDapp + jar, and not different versions of the JAR created from identical sources. The filename of the JAR must include a unique identifier to deduplicate it from other releases of the same CorDapp. This is typically done by appending the version string to the CorDapp's name. This unique identifier should not change @@ -131,7 +131,7 @@ Installing the CorDapp jar -------------------------- .. note:: Before installing a CorDapp, you must create one or more nodes to install it on. For instructions, please see - :doc:`deploying-a-node`. + :doc:`generating-a-node`. At runtime, nodes will load any CorDapps present in their ``cordapps`` folder. Therefore in order to install a CorDapp on a node, the CorDapp JAR must be added to the ``/cordapps/`` folder, where ``node_dir`` is the folder in which diff --git a/docs/source/deploying-a-node.rst b/docs/source/deploying-a-node.rst index 6bda43ca86..2b091fddf8 100644 --- a/docs/source/deploying-a-node.rst +++ b/docs/source/deploying-a-node.rst @@ -1,141 +1,242 @@ Deploying a node ================ -Node structure --------------- -Each Corda node has the following structure: +.. contents:: -.. sourcecode:: none +.. note:: These instructions are intended for people who want to deploy a Corda node to a server, + whether they have developed and tested a CorDapp following the instructions in :doc:`generating-a-node` + or are deploying a third-party CorDapp. - . - ├── certificates // The node's doorman certificates - ├── corda-webserver.jar // The built-in node webserver - ├── corda.jar // The core Corda libraries - ├── logs // The node logs - ├── node.conf // The node's configuration files - ├── persistence.mv.db // The node's database - └── cordapps // The CorDapps jars installed on the node +Linux (systemd): Installing and running Corda as a systemd service +------------------------------------------------------------------ +We recommend creating systemd services to run a node and the optional webserver. This provides logging and service +handling, and ensures the Corda service is run at boot. -The node is configured by editing its ``node.conf`` file. You install CorDapps on the node by dropping the CorDapp JARs -into the ``cordapps`` folder. +**Prerequisites**: -Node naming ------------ -A node's name must be a valid X500 name that obeys the following additional constraints: + * Oracle Java 8. The supported versions are listed in :doc:`getting-set-up` -* The fields of the name have the following maximum character lengths: +1. Add a system user which will be used to run Corda: - * Common name: 64 - * Organisation: 128 - * Organisation unit: 64 - * Locality: 64 - * State: 64 + ``sudo adduser --system --no-create-home --group corda`` -* The country code is a valid ISO 3166-1 two letter code in upper-case +2. Create a directory called ``/opt/corda`` and change its ownership to the user you want to use to run Corda: -* The organisation, locality and country attributes are present + ``mkdir /opt/corda; chown corda:corda /opt/corda`` -* The organisation field of the name obeys the following constraints: +3. Download the `Corda jar `_ + (under ``/VERSION_NUMBER/corda-VERSION_NUMBER.jar``) and place it in ``/opt/corda`` - * Has at least two letters - * No leading or trailing whitespace - * No double-spacing - * Upper-case first letter - * Does not contain the words "node" or "server" - * Does not include the characters ',' or '=' or '$' or '"' or '\'' or '\\' - * Is in NFKC normalization form - * Only the latin, common and inherited unicode scripts are supported +3. Create a directory called ``plugins`` in ``/opt/corda`` and save your CorDapp jar file to it. Alternatively, download one of + our `sample CorDapps `_ to the ``plugins`` directory -The deployNodes task --------------------- -The CorDapp template defines a ``deployNodes`` task that allows you to automatically generate and configure a set of -nodes: +4. Save the below as ``/opt/corda/node.conf``. See :doc:`corda-configuration-file` for a description of these options -.. sourcecode:: groovy + .. code-block:: json - task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { - directory "./build/nodes" - networkMap "O=Controller,L=London,C=GB" - node { - name "O=Controller,L=London,C=GB" - // The notary will offer a validating notary service. - notary = [validating : true] - p2pPort 10002 - rpcPort 10003 - // No webport property, so no webserver will be created. - h2Port 10004 - sshdPort 22 - // Includes the corda-finance CorDapp on our node. - cordapps = ["net.corda:corda-finance:$corda_release_version"] + basedir : "/opt/corda" + p2pAddress : "example.com:10002" + rpcAddress : "example.com:10003" + webAddress : "0.0.0.0:10004" + h2port : 11000 + emailAddress : "you@example.com" + myLegalName : "O=Bank of Breakfast Tea, L=London, C=GB" + keyStorePassword : "cordacadevpass" + trustStorePassword : "trustpass" + useHTTPS : false + devMode : false + networkMapService { + address="networkmap.foo.bar.com:10002" + legalName="O=FooBar NetworkMap, L=Dublin, C=IE" + } + rpcUsers=[ + { + user=corda + password=portal_password + permissions=[ + ALL + ] + } + ] + +5. Make the following changes to ``/opt/corda/node.conf``: + + * Change the ``p2pAddress`` and ``rpcAddress`` values to start with your server's hostname or external IP address. + This is the address other nodes or RPC interfaces will use to communicate with your node + * Change the ports if necessary, for example if you are running multiple nodes on one server (see below) + * Enter an email address which will be used as an administrative contact during the registration process. This is + only visible to the permissioning service + * Enter your node's desired legal name. This will be used during the issuance of your certificate and should rarely + change as it should represent the legal identity of your node + + * Organization (``O=``) should be a unique and meaningful identifier (e.g. Bank of Breakfast Tea) + * Location (``L=``) is your nearest city + * Country (``C=``) is the `ISO 3166-1 alpha-2 code `_ + * Change the RPC username and password + +6. Create a ``corda.service`` file based on the example below and save it in the ``/etc/systemd/system/`` directory + + .. code-block:: shell + + [Unit] + Description=Corda Node - Bank of Breakfast Tea + Requires=network.target + + [Service] + Type=simple + User=corda + WorkingDirectory=/opt/corda + ExecStart=/usr/bin/java -Xmx2048m -jar /opt/corda/corda.jar + Restart=on-failure + + [Install] + WantedBy=multi-user.target + +7. Make the following changes to ``corda.service``: + + * Make sure the service description is informative - particularly if you plan to run multiple nodes. + * Change the username to the user account you want to use to run Corda. **We recommend that this is not root** + * Set the maximum amount of memory available to the Corda process by changing the ``-Xmx2048m`` parameter + * Make sure the ``corda.service`` file is owned by root with the correct permissions: + * ``sudo chown root:root /etc/systemd/system/corda.service`` + * ``sudo chmod 644 /etc/systemd/system/corda.service`` + +.. note:: The Corda webserver provides a simple interface for interacting with your installed CorDapps in a browser. + Running the webserver is optional. + +8. Create a ``corda-webserver.service`` file based on the example below and save it in the ``/etc/systemd/system/`` + directory. + + .. code-block:: shell + + [Unit] + Description=Webserver for Corda Node - Bank of Breakfast Tea + Requires=network.target + + [Service] + Type=simple + User=username + WorkingDirectory=/opt/corda + ExecStart=/usr/bin/java -jar /opt/corda/corda-webserver.jar + Restart=on-failure + + [Install] + WantedBy=multi-user.target + +9. Provision the required certificates to your node. Contact the network permissioning service or see + :doc:`permissioning` + +10. You can now start a node and its webserver by running the following ``systemctl`` commands: + + * ``sudo systemctl daemon-reload`` + * ``sudo systemctl corda start`` + * ``sudo systemctl corda-webserver start`` + +You can run multiple nodes by creating multiple directories and Corda services, modifying the ``node.conf`` and +``service`` files so they are unique. + +Windows: Installing and running Corda as a Windows service +---------------------------------------------------------- +We recommend running Corda as a Windows service. This provides service handling, ensures the Corda service is run +at boot, and means the Corda service stays running with no users connected to the server. + +**Prerequisites**: + + * Oracle Java 8. The supported versions are listed in :doc:`getting-set-up` + +1. Create a Corda directory and download the Corda jar. Replace ``VERSION_NUMBER`` with the desired version. Here's an + example using PowerShell: + + .. code-block:: PowerShell + + mkdir C:\Corda + wget http://jcenter.bintray.com/net/corda/corda/VERSION_NUMBER/corda-VERSION_NUMBER.jar -OutFile C:\Corda\corda.jar + +2. Create a directory called ``plugins`` in ``/opt/corda`` and save your CorDapp jar file to it. Alternatively, + download one of our `sample CorDapps `_ to the ``plugins`` directory + +3. Save the below as ``C:\Corda\node.conf``. See :doc:`corda-configuration-file` for a description of these options + + .. code-block:: json + + basedir : "C:\\Corda" + p2pAddress : "example.com:10002" + rpcAddress : "example.com:10003" + webAddress : "0.0.0.0:10004" + h2port : 11000 + emailAddress: "you@example.com" + myLegalName : "O=Bank of Breakfast Tea, L=London, C=GB" + keyStorePassword : "cordacadevpass" + trustStorePassword : "trustpass" + extraAdvertisedServiceIds: [ "" ] + useHTTPS : false + devMode : false + networkMapService { + address="networkmap.foo.bar.com:10002" + legalName="O=FooBar NetworkMap, L=Dublin, C=IE" } - node { - name "O=PartyA,L=London,C=GB" - advertisedServices = [] - p2pPort 10005 - rpcPort 10006 - webPort 10007 - h2Port 10008 - sshdPort 22 - cordapps = ["net.corda:corda-finance:$corda_release_version"] - // Grants user1 all RPC permissions. - rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]] - } - node { - name "O=PartyB,L=New York,C=US" - advertisedServices = [] - p2pPort 10009 - rpcPort 10010 - webPort 10011 - h2Port 10012 - sshdPort 22 - cordapps = ["net.corda:corda-finance:$corda_release_version"] - // Grants user1 the ability to start the MyFlow flow. - rpcUsers = [[ user: "user1", "password": "test", "permissions": ["StartFlow.net.corda.flows.MyFlow"]]] - } - } + rpcUsers=[ + { + user=corda + password=portal_password + permissions=[ + ALL + ] + } + ] -Running this task will create three nodes in the ``build/nodes`` folder: +4. Make the following changes to ``C:\Corda\node.conf``: -* A ``Controller`` node that: + * Change the ``p2pAddress`` and ``rpcAddress`` values to start with your server's hostname or external IP address. + This is the address other nodes or RPC interfaces will use to communicate with your node + * Change the ports if necessary, for example if you are running multiple nodes on one server (see below) + * Enter an email address which will be used as an administrative contact during the registration process. This is + only visible to the permissioning service + * Enter your node's desired legal name. This will be used during the issuance of your certificate and should rarely + change as it should represent the legal identity of your node - * Serves as the network map - * Offers a validating notary service - * Will not have a webserver (since ``webPort`` is not defined) - * Is running the ``corda-finance`` CorDapp + * Organization (``O=``) should be a unique and meaningful identifier (e.g. Bank of Breakfast Tea) + * Location (``L=``) is your nearest city + * Country (``C=``) is the `ISO 3166-1 alpha-2 code `_ + * Change the RPC username and password -* ``PartyA`` and ``PartyB`` nodes that: +5. Copy the required Java keystores to the node. See :doc:`permissioning` - * Are pointing at the ``Controller`` as the network map service - * Are not offering any services - * Will have a webserver (since ``webPort`` is defined) - * Are running the ``corda-finance`` CorDapp - * Have an RPC user, ``user1``, that can be used to log into the node via RPC +6. Download the `NSSM service manager `_ -Additionally, all three nodes will include any CorDapps defined in the project's source folders, even though these -CorDapps are not listed in each node's ``cordapps`` entry. This means that running the ``deployNodes`` task from the -template CorDapp, for example, would automatically build and add the template CorDapp to each node. +7. Unzip ``nssm-2.24\win64\nssm.exe`` to ``C:\Corda`` -You can extend ``deployNodes`` to generate additional nodes. The only requirement is that you must specify -a single node to run the network map service, by putting their name in the ``networkMap`` field. +8. Save the following as ``C:\Corda\nssm.bat``: -.. warning:: When adding nodes, make sure that there are no port clashes! + .. code-block:: batch -Running deployNodes -------------------- -To create the nodes defined in our ``deployNodes`` task, we'd run the following command in a terminal window from the -root of the project: + nssm install cordanode1 C:\ProgramData\Oracle\Java\javapath\java.exe + nssm set cordanode1 AppDirectory C:\Corda + nssm set cordanode1 AppParameters "-jar corda.jar -Xmx2048m --config-file=C:\corda\node.conf" + nssm set cordanode1 AppStdout C:\Corda\service.log + nssm set cordanode1 AppStderr C:\Corda\service.log + nssm set cordanode1 Description Corda Node - Bank of Breakfast Tea + sc start cordanode1 -* Unix/Mac OSX: ``./gradlew deployNodes`` -* Windows: ``gradlew.bat deployNodes`` +9. Modify the batch file: -This will create the nodes in the ``build/nodes`` folder. + * If you are installing multiple nodes, use a different service name (``cordanode1``) for each node + * Set the amount of Java heap memory available to this node by modifying the -Xmx argument + * Set an informative description -.. note:: During the build process each node generates a NodeInfo file which is written in its own root directory, - the plug-in proceeds and copies each node NodeInfo to every other node ``additional-node-infos`` directory. - The NodeInfo file contains a node hostname and port, legal name and security certificate. +10. Run the batch file by clicking on it or from a command prompt -There will be a node folder generated for each node you defined, plus a ``runnodes`` shell script (or batch file on -Windows) to run all the nodes at once. If you make any changes to your ``deployNodes`` task, you will need to re-run -the task to see the changes take effect. +11. Run ``services.msc`` and verify that a service called ``cordanode1`` is present and running -You can now run the nodes by following the instructions in :doc:`Running a node `. \ No newline at end of file +12. Run ``netstat -ano`` and check for the ports you configured in ``node.conf`` + +13. You may need to open the ports on the Windows firewall + +Testing your installation +------------------------- +You can verify Corda is running by connecting to your RPC port from another host, e.g.: + + ``telnet your-hostname.example.com 10002`` + +If you receive the message "Escape character is ^]", Corda is running and accessible. Press Ctrl-] and Ctrl-D to exit +telnet. \ No newline at end of file diff --git a/docs/source/generating-a-node.rst b/docs/source/generating-a-node.rst new file mode 100644 index 0000000000..afbde7127c --- /dev/null +++ b/docs/source/generating-a-node.rst @@ -0,0 +1,136 @@ +Creating nodes locally +====================== + +.. contents:: + +Node structure +-------------- +Each Corda node has the following structure: + +.. sourcecode:: none + + . + ├── certificates // The node's certificates + ├── corda-webserver.jar // The built-in node webserver + ├── corda.jar // The core Corda libraries + ├── logs // The node logs + ├── node.conf // The node's configuration files + ├── persistence.mv.db // The node's database + └── cordapps // The CorDapps jars installed on the node + +The node is configured by editing its ``node.conf`` file. You install CorDapps on the node by dropping the CorDapp JARs +into the ``cordapps`` folder. + +Node naming +----------- +A node's name must be a valid X.500 name that obeys the following additional constraints: + +* The fields of the name have the following maximum character lengths: + + * Common name: 64 + * Organisation: 128 + * Organisation unit: 64 + * Locality: 64 + * State: 64 + +* The country code is a valid ISO 3166-1 two letter code in upper-case + +* The organisation, locality and country attributes are present + +* The organisation field of the name obeys the following constraints: + + * Has at least two letters + * No leading or trailing whitespace + * No double-spacing + * Upper-case first letter + * Does not contain the words "node" or "server" + * Does not include the characters ',' or '=' or '$' or '"' or '\'' or '\\' + * Is in NFKC normalization form + * Only the latin, common and inherited unicode scripts are supported + +The Cordform task +----------------- +Corda provides a gradle plugin called ``Cordform`` that allows you to automatically generate and configure a set of +nodes. Here is an example ``Cordform`` task called ``deployNodes`` that creates three nodes, defined in the +`Kotlin CorDapp Template `_: + +.. sourcecode:: groovy + + task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { + directory "./build/nodes" + networkMap "O=Controller,L=London,C=GB" + node { + name "O=Controller,L=London,C=GB" + // The notary will offer a validating notary service. + notary = [validating : true] + p2pPort 10002 + rpcPort 10003 + // No webport property, so no webserver will be created. + h2Port 10004 + // Includes the corda-finance CorDapp on our node. + cordapps = ["net.corda:corda-finance:$corda_release_version"] + } + node { + name "O=PartyA,L=London,C=GB" + advertisedServices = [] + p2pPort 10005 + rpcPort 10006 + webPort 10007 + h2Port 10008 + cordapps = ["net.corda:corda-finance:$corda_release_version"] + // Grants user1 all RPC permissions. + rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]] + } + node { + name "O=PartyB,L=New York,C=US" + advertisedServices = [] + p2pPort 10009 + rpcPort 10010 + webPort 10011 + h2Port 10012 + cordapps = ["net.corda:corda-finance:$corda_release_version"] + // Grants user1 the ability to start the MyFlow flow. + rpcUsers = [[ user: "user1", "password": "test", "permissions": ["StartFlow.net.corda.flows.MyFlow"]]] + } + } + +Running this task will create three nodes in the ``build/nodes`` folder: + +* A ``Controller`` node that: + + * Serves as the network map + * Offers a validating notary service + * Will not have a webserver (since ``webPort`` is not defined) + * Is running the ``corda-finance`` CorDapp + +* ``PartyA`` and ``PartyB`` nodes that: + + * Are pointing at the ``Controller`` as the network map service + * Are not offering any services + * Will have a webserver (since ``webPort`` is defined) + * Are running the ``corda-finance`` CorDapp + * Have an RPC user, ``user1``, that can be used to log into the node via RPC + +Additionally, all three nodes will include any CorDapps defined in the project's source folders, even though these +CorDapps are not listed in each node's ``cordapps`` entry. This means that running the ``deployNodes`` task from the +template CorDapp, for example, would automatically build and add the template CorDapp to each node. + +You can extend ``deployNodes`` to generate additional nodes. The only requirement is that you must specify +a single node to run the network map service, by putting its name in the ``networkMap`` field. + +.. warning:: When adding nodes, make sure that there are no port clashes! + +Running deployNodes +------------------- +To create the nodes defined in our ``deployNodes`` task, run the following command in a terminal window from the root +of the project where the ``deployNodes`` task is defined: + +* Linux/macOS: ``./gradlew deployNodes`` +* Windows: ``gradlew.bat deployNodes`` + +This will create the nodes in the ``build/nodes`` folder. There will be a node folder generated for each node defined +in the ``deployNodes`` task, plus a ``runnodes`` shell script (or batch file on Windows) to run all the nodes at once +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 diff --git a/docs/source/running-a-node.rst b/docs/source/running-a-node.rst index 4373e93a3e..b5c2ddf845 100644 --- a/docs/source/running-a-node.rst +++ b/docs/source/running-a-node.rst @@ -1,25 +1,49 @@ -Running a node -============== +Running nodes locally +===================== -Starting your node ------------------- -After following the steps in :doc:`deploying-a-node`, you should have deployed your node(s) with any chosen CorDapps -already installed. You run each node by navigating to ```` in a terminal window and running: +.. contents:: + +.. note:: You should already have generated your node(s) with their CorDapps installed by following the instructions in + :doc:`generating-a-node`. + +There are several ways to run a Corda node locally for testing purposes. + +Starting all nodes at once +-------------------------- + +.. note:: ``runnodes`` is a shell script (or batch file on Windows) that is generated by ``deployNodes`` to allow you + to quickly start up all nodes and their webservers. ``runnodes`` should only be used for testing purposes. + +Start the nodes with ``runnodes`` by running the following command from the root of the project: + +* Linux/macOS: ``build/nodes/runnodes`` +* Windows: ``call build\nodes\runnodes.bat`` + +.. warn:: On macOS, do not click/change focus until all the node terminal windows have opened, or some processes may + fail to start. + +Starting an individual Corda node +--------------------------------- +Run the node by opening a terminal window in the node's folder and running: .. code-block:: shell java -jar corda.jar -.. warning:: If your working directory is not ```` your cordapps and configuration will not be used. +.. warning:: By default, the node will look for a configuration file called ``node.conf`` and a CorDapps folder called + ``cordapps`` in the current working directory. You can override the configuration file and workspace paths on the + command line (e.g. ``./corda.jar --config-file=test.conf --base-directory=/opt/r3corda/nodes/test``). -The configuration file and workspace paths can be overridden on the command line. For example: +Optionally run the node's webserver as well by opening a terminal window in the node's folder and running: -``./corda.jar --config-file=test.conf --base-directory=/opt/r3corda/nodes/test``. +.. code-block:: shell -Otherwise the workspace folder for the node is the current working path. + java -jar corda-webserver.jar -Debugging your node -------------------- +.. warning:: The node webserver is for testing purposes only and will be removed soon. + +Starting a node with remote debugging enabled +--------------------------------------------- To enable remote debugging of the node, run the following from the terminal window: ``java -Dcapsule.jvm.args="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" -jar corda.jar`` diff --git a/docs/source/shell.rst b/docs/source/shell.rst index 2fc11829b2..d993c5e3bf 100644 --- a/docs/source/shell.rst +++ b/docs/source/shell.rst @@ -137,7 +137,7 @@ which could be represented as ``{ first: foo, second: 123 }``. .. note:: If your CorDapp is written in Java, named arguments won't work unless you compiled using the ``-parameters`` argument to javac. - See :doc:`deploying-a-node` for how to specify it via Gradle. + See :doc:`generating-a-node` for how to specify it via Gradle. The same syntax is also used to specify the parameters for RPCs, accessed via the ``run`` command, like this: diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 51e60133c4..4176f04ed3 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -26,7 +26,8 @@ for gradle and IntelliJ, but it's possible this option is not present in your en "No matching constructor found: - [arg0: int, arg1: Party]: missing parameter arg0" *********************************************************************************** -Your CorDapp is written in Java and you haven't specified the ``-parameters`` compiler argument. See :doc:`deploying-a-node` for how it can be done using Gradle. +Your CorDapp is written in Java and you haven't specified the ``-parameters`` compiler argument. See +:doc:`generating-a-node` for how it can be done using Gradle. IDEA issues ----------- diff --git a/docs/source/tutorial-cordapp.rst b/docs/source/tutorial-cordapp.rst index d8dfcff998..7873819f82 100644 --- a/docs/source/tutorial-cordapp.rst +++ b/docs/source/tutorial-cordapp.rst @@ -176,7 +176,7 @@ There are two ways to run the example CorDapp: * Via IntelliJ In both cases, we will deploy a set of test nodes with our CorDapp installed, then run the nodes. You can read more -about how we define the nodes to be deployed :doc:`here `. +about how we define the nodes to be deployed :doc:`here `. Terminal ~~~~~~~~ From b638b30d2a75bd8791402ff9447f452c0a5200a7 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Tue, 28 Nov 2017 18:12:48 +0000 Subject: [PATCH 16/27] Moved CordaPersistence and dependent classes into internal package in node-api. This is so that the doorman can make use of them without needing a compile dependency to node. --- .../AttachmentSerializationTest.kt | 2 +- .../finance/contracts/asset/CashTests.kt | 7 +- node-api/build.gradle | 2 +- .../internal/persistence}/CordaPersistence.kt | 108 +++++++++--------- .../persistence/DatabaseTransaction.kt | 61 ++++++++++ .../DatabaseTransactionManager.kt | 66 ++--------- .../persistence/HibernateConfiguration.kt | 23 ++-- .../internal/crypto}/X509UtilitiesTest.kt | 3 +- .../net/corda/node/internal/AbstractNode.kt | 35 ++++-- .../corda/node/internal/CordaRPCOpsImpl.kt | 2 +- .../kotlin/net/corda/node/internal/Node.kt | 4 +- .../corda/node/internal/SecureCordaRPCOps.kt | 2 +- .../net/corda/node/internal/StartedNode.kt | 2 +- .../node/internal/cordapp/CordappLoader.kt | 9 +- .../node/services/api/ServiceHubInternal.kt | 4 +- .../node/services/config/NodeConfiguration.kt | 20 +--- .../services/events/NodeSchedulerService.kt | 6 +- .../identity/PersistentIdentityService.kt | 2 +- .../keys/PersistentKeyManagementService.kt | 2 +- .../services/messaging/P2PMessagingClient.kt | 7 +- .../network/PersistentNetworkMapCache.kt | 6 +- .../persistence/DBCheckpointStorage.kt | 4 +- .../DBTransactionMappingStorage.kt | 3 + .../persistence/DBTransactionStorage.kt | 3 + .../persistence/NodeAttachmentService.kt | 6 +- .../node/services/schema/HibernateObserver.kt | 16 +-- .../node/services/schema/NodeSchemaService.kt | 21 ++-- .../statemachine/FlowStateMachineImpl.kt | 8 +- .../statemachine/StateMachineManagerImpl.kt | 11 +- .../BFTNonValidatingNotaryService.kt | 2 +- .../transactions/DistributedImmutableMap.kt | 5 +- .../PersistentUniquenessProvider.kt | 2 +- .../transactions/RaftUniquenessProvider.kt | 4 +- .../upgrade/ContractUpgradeServiceImpl.kt | 2 +- .../node/services/vault/NodeVaultService.kt | 10 +- .../net/corda/node/shell/InteractiveShell.kt | 6 +- .../node/utilities/AppendOnlyPersistentMap.kt | 1 + .../net/corda/node/utilities/PersistentMap.kt | 3 +- .../services/vault/VaultQueryJavaTests.java | 4 +- .../net/corda/node/InteractiveShellTest.kt | 4 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 2 +- .../config/NodeConfigurationImplTest.kt | 1 + .../events/NodeSchedulerServiceTest.kt | 6 +- .../PersistentIdentityServiceTests.kt | 2 +- .../messaging/ArtemisMessagingTests.kt | 6 +- .../persistence/DBCheckpointStorageTests.kt | 6 +- .../persistence/DBTransactionStorageTests.kt | 12 +- .../persistence/HibernateConfigurationTest.kt | 18 ++- .../persistence/NodeAttachmentStorageTest.kt | 6 +- .../services/schema/HibernateObserverTests.kt | 8 +- .../DistributedImmutableMapTests.kt | 6 +- .../PersistentUniquenessProviderTests.kt | 6 +- .../services/vault/NodeVaultServiceTest.kt | 2 +- .../node/services/vault/VaultQueryTests.kt | 6 +- .../vault/VaultSoftLockManagerTest.kt | 2 +- .../node/services/vault/VaultWithCashTest.kt | 2 +- .../corda/node/utilities/ObservablesTests.kt | 3 +- .../corda/irs/api/NodeInterestRatesTest.kt | 6 +- .../kotlin/net/corda/testing/NodeTestUtils.kt | 2 +- .../testing/node/InMemoryMessagingNetwork.kt | 8 +- .../corda/testing/node/MockNetworkMapCache.kt | 2 +- .../kotlin/net/corda/testing/node/MockNode.kt | 2 +- .../net/corda/testing/node/MockServices.kt | 26 +++-- 63 files changed, 331 insertions(+), 297 deletions(-) rename {node/src/main/kotlin/net/corda/node/utilities => node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence}/CordaPersistence.kt (69%) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/DatabaseTransaction.kt rename {node/src/main/kotlin/net/corda/node/utilities => node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence}/DatabaseTransactionManager.kt (58%) rename {node/src/main/kotlin/net/corda/node/services => node-api/src/main/kotlin/net/corda/nodeapi/internal}/persistence/HibernateConfiguration.kt (85%) rename {node/src/test/kotlin/net/corda/node/utilities => node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto}/X509UtilitiesTest.kt (99%) 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 1dd630a2ff..ec5cac3898 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -15,7 +15,7 @@ import net.corda.core.utilities.unwrap import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.StartedNode import net.corda.node.services.persistence.NodeAttachmentService -import net.corda.node.utilities.currentDBSession +import net.corda.nodeapi.internal.persistence.currentDBSession import net.corda.testing.ALICE_NAME import net.corda.testing.BOB_NAME import net.corda.testing.node.MockNetwork 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 c3c17c6661..e2dd05dd2b 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,10 @@ package net.corda.finance.contracts.asset import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash import net.corda.core.crypto.generateKeyPair -import net.corda.core.identity.* +import net.corda.core.identity.AbstractParty +import net.corda.core.identity.AnonymousParty +import net.corda.core.identity.CordaX500Name +import net.corda.core.identity.Party import net.corda.core.node.ServiceHub import net.corda.core.node.services.VaultService import net.corda.core.node.services.queryBy @@ -16,7 +19,7 @@ import net.corda.finance.utils.sumCashBy import net.corda.finance.utils.sumCashOrNull import net.corda.finance.utils.sumCashOrZero import net.corda.node.services.vault.NodeVaultService -import net.corda.node.utilities.CordaPersistence +import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.* import net.corda.testing.contracts.DummyState import net.corda.testing.contracts.VaultFiller diff --git a/node-api/build.gradle b/node-api/build.gradle index 76abc37bea..d2c51dde94 100644 --- a/node-api/build.gradle +++ b/node-api/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'com.jfrog.artifactory' -description 'Corda node Artemis API' +description 'Corda node API' dependencies { compile project(":core") diff --git a/node/src/main/kotlin/net/corda/node/utilities/CordaPersistence.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt similarity index 69% rename from node/src/main/kotlin/net/corda/node/utilities/CordaPersistence.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt index 6e9d7995d6..30c2817fad 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/CordaPersistence.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt @@ -1,63 +1,79 @@ -package net.corda.node.utilities +package net.corda.nodeapi.internal.persistence -import com.zaxxer.hikari.HikariConfig -import com.zaxxer.hikari.HikariDataSource -import net.corda.core.node.services.IdentityService -import net.corda.node.services.api.SchemaService -import net.corda.node.services.config.DatabaseConfig -import net.corda.node.services.persistence.HibernateConfiguration -import net.corda.node.services.schema.NodeSchemaService +import net.corda.core.schemas.MappedSchema import rx.Observable import rx.Subscriber import rx.subjects.UnicastSubject import java.io.Closeable import java.sql.Connection import java.sql.SQLException -import java.util.* import java.util.concurrent.CopyOnWriteArrayList +import javax.persistence.AttributeConverter +import javax.sql.DataSource /** * Table prefix for all tables owned by the node module. */ const val NODE_DATABASE_PREFIX = "node_" -//HikariDataSource implements Closeable which allows CordaPersistence to be Closeable +// This class forms part of the node config and so any changes to it must be handled with care +data class DatabaseConfig( + val initialiseSchema: Boolean = true, + val serverNameTablePrefix: String = "", + val transactionIsolationLevel: TransactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ +) + +// This class forms part of the node config and so any changes to it must be handled with care +enum class TransactionIsolationLevel { + NONE, + READ_UNCOMMITTED, + READ_COMMITTED, + REPEATABLE_READ, + SERIALIZABLE; + + /** + * The JDBC constant value of the same name but prefixed with TRANSACTION_ defined in [java.sql.Connection]. + */ + val jdbcValue: Int = java.sql.Connection::class.java.getField("TRANSACTION_$name").get(null) as Int +} + class CordaPersistence( - val dataSource: HikariDataSource, - private val schemaService: SchemaService, - private val identityService: IdentityService, - databaseConfig: DatabaseConfig + val dataSource: DataSource, + databaseConfig: DatabaseConfig, + schemas: Set, + attributeConverters: Collection> = emptySet() ) : Closeable { - val transactionIsolationLevel = databaseConfig.transactionIsolationLevel.jdbcValue + val defaultIsolationLevel = databaseConfig.transactionIsolationLevel val hibernateConfig: HibernateConfiguration by lazy { transaction { - HibernateConfiguration(schemaService, databaseConfig, identityService) + HibernateConfiguration(schemas, databaseConfig, attributeConverters) } } val entityManagerFactory get() = hibernateConfig.sessionFactoryForRegisteredSchemas - companion object { - fun connect(dataSource: HikariDataSource, schemaService: SchemaService, identityService: IdentityService, databaseConfig: DatabaseConfig): CordaPersistence { - return CordaPersistence(dataSource, schemaService, identityService, databaseConfig).apply { - DatabaseTransactionManager(this) + init { + DatabaseTransactionManager(this) + // Check not in read-only mode. + transaction { + dataSource.connection.use { + check(!it.metaData.isReadOnly) { "Database should not be readonly." } } } } /** - * Creates an instance of [DatabaseTransaction], with the given isolation level. - * @param isolationLevel isolation level for the transaction. If not specified the default (i.e. provided at the creation time) is used. + * Creates an instance of [DatabaseTransaction], with the given transaction isolation level. */ - fun createTransaction(isolationLevel: Int): DatabaseTransaction { + fun createTransaction(isolationLevel: TransactionIsolationLevel): DatabaseTransaction { // We need to set the database for the current [Thread] or [Fiber] here as some tests share threads across databases. DatabaseTransactionManager.dataSource = this return DatabaseTransactionManager.currentOrNew(isolationLevel) } /** - * Creates an instance of [DatabaseTransaction], with the transaction isolation level specified at the creation time. + * Creates an instance of [DatabaseTransaction], with the default transaction isolation level. */ - fun createTransaction(): DatabaseTransaction = createTransaction(transactionIsolationLevel) + fun createTransaction(): DatabaseTransaction = createTransaction(defaultIsolationLevel) fun createSession(): Connection { // We need to set the database for the current [Thread] or [Fiber] here as some tests share threads across databases. @@ -71,7 +87,7 @@ class CordaPersistence( * @param isolationLevel isolation level for the transaction. * @param statement to be executed in the scope of this transaction. */ - fun transaction(isolationLevel: Int, statement: DatabaseTransaction.() -> T): T { + fun transaction(isolationLevel: TransactionIsolationLevel, statement: DatabaseTransaction.() -> T): T { DatabaseTransactionManager.dataSource = this return transaction(isolationLevel, 3, statement) } @@ -80,22 +96,21 @@ class CordaPersistence( * Executes given statement in the scope of transaction with the transaction level specified at the creation time. * @param statement to be executed in the scope of this transaction. */ - fun transaction(statement: DatabaseTransaction.() -> T): T = transaction(transactionIsolationLevel, statement) + fun transaction(statement: DatabaseTransaction.() -> T): T = transaction(defaultIsolationLevel, statement) - private fun transaction(transactionIsolation: Int, repetitionAttempts: Int, statement: DatabaseTransaction.() -> T): T { + private fun transaction(isolationLevel: TransactionIsolationLevel, repetitionAttempts: Int, statement: DatabaseTransaction.() -> T): T { val outer = DatabaseTransactionManager.currentOrNull() - return if (outer != null) { outer.statement() } else { - inTopLevelTransaction(transactionIsolation, repetitionAttempts, statement) + inTopLevelTransaction(isolationLevel, repetitionAttempts, statement) } } - private fun inTopLevelTransaction(transactionIsolation: Int, repetitionAttempts: Int, statement: DatabaseTransaction.() -> T): T { + private fun inTopLevelTransaction(isolationLevel: TransactionIsolationLevel, repetitionAttempts: Int, statement: DatabaseTransaction.() -> T): T { var repetitions = 0 while (true) { - val transaction = DatabaseTransactionManager.currentOrNew(transactionIsolation) + val transaction = DatabaseTransactionManager.currentOrNew(isolationLevel) try { val answer = transaction.statement() transaction.commit() @@ -116,23 +131,11 @@ class CordaPersistence( } override fun close() { - dataSource.close() + // DataSource doesn't implement AutoCloseable so we just have to hope that the implementation does so that we can close it + (dataSource as? AutoCloseable)?.close() } } -fun configureDatabase(dataSourceProperties: Properties, databaseConfig: DatabaseConfig, identityService: IdentityService, schemaService: SchemaService = NodeSchemaService(null)): CordaPersistence { - val config = HikariConfig(dataSourceProperties) - val dataSource = HikariDataSource(config) - val persistence = CordaPersistence.connect(dataSource, schemaService, identityService, databaseConfig) - // Check not in read-only mode. - persistence.transaction { - persistence.dataSource.connection.use { - check(!it.metaData.isReadOnly) { "Database should not be readonly." } - } - } - return persistence -} - /** * Buffer observations until after the current database transaction has been closed. Observations are never * dropped, simply delayed. @@ -144,7 +147,7 @@ fun configureDatabase(dataSourceProperties: Properties, databaseConfig: Database */ fun rx.Observer.bufferUntilDatabaseCommit(): rx.Observer { val currentTxId = DatabaseTransactionManager.transactionId - val databaseTxBoundary: Observable = DatabaseTransactionManager.transactionBoundaries.filter { it.txId == currentTxId }.first() + val databaseTxBoundary: Observable = DatabaseTransactionManager.transactionBoundaries.first { it.txId == currentTxId } val subject = UnicastSubject.create() subject.delaySubscription(databaseTxBoundary).subscribe(this) databaseTxBoundary.doOnCompleted { subject.onCompleted() } @@ -183,14 +186,9 @@ private class DatabaseTransactionWrappingSubscriber(val db: CordaPersistence? // A subscriber that wraps another but does not pass on observations to it. private class NoOpSubscriber(t: Subscriber) : Subscriber(t) { - override fun onCompleted() { - } - - override fun onError(e: Throwable?) { - } - - override fun onNext(s: U) { - } + override fun onCompleted() {} + override fun onError(e: Throwable?) {} + override fun onNext(s: U) {} } /** diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/DatabaseTransaction.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/DatabaseTransaction.kt new file mode 100644 index 0000000000..5c78dc6a40 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/DatabaseTransaction.kt @@ -0,0 +1,61 @@ +package net.corda.nodeapi.internal.persistence + +import org.hibernate.Session +import org.hibernate.Transaction +import rx.subjects.Subject +import java.sql.Connection +import java.util.* + +class DatabaseTransaction( + isolation: Int, + private val threadLocal: ThreadLocal, + private val transactionBoundaries: Subject, + val cordaPersistence: CordaPersistence +) { + val id: UUID = UUID.randomUUID() + + val connection: Connection by lazy(LazyThreadSafetyMode.NONE) { + cordaPersistence.dataSource.connection.apply { + autoCommit = false + transactionIsolation = isolation + } + } + + private val sessionDelegate = lazy { + val session = cordaPersistence.entityManagerFactory.withOptions().connection(connection).openSession() + hibernateTransaction = session.beginTransaction() + session + } + + val session: Session by sessionDelegate + private lateinit var hibernateTransaction: Transaction + + private val outerTransaction: DatabaseTransaction? = threadLocal.get() + + fun commit() { + if (sessionDelegate.isInitialized()) { + hibernateTransaction.commit() + } + connection.commit() + } + + fun rollback() { + if (sessionDelegate.isInitialized() && session.isOpen) { + session.clear() + } + if (!connection.isClosed) { + connection.rollback() + } + } + + fun close() { + if (sessionDelegate.isInitialized() && session.isOpen) { + session.close() + } + connection.close() + threadLocal.set(outerTransaction) + if (outerTransaction == null) { + transactionBoundaries.onNext(DatabaseTransactionManager.Boundary(id)) + } + } +} diff --git a/node/src/main/kotlin/net/corda/node/utilities/DatabaseTransactionManager.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/DatabaseTransactionManager.kt similarity index 58% rename from node/src/main/kotlin/net/corda/node/utilities/DatabaseTransactionManager.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/DatabaseTransactionManager.kt index 6c810a7005..ade1603002 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/DatabaseTransactionManager.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/DatabaseTransactionManager.kt @@ -1,68 +1,14 @@ -package net.corda.node.utilities +package net.corda.nodeapi.internal.persistence import co.paralleluniverse.strands.Strand import org.hibernate.Session -import org.hibernate.Transaction import rx.subjects.PublishSubject import rx.subjects.Subject -import java.sql.Connection import java.util.* import java.util.concurrent.ConcurrentHashMap -class DatabaseTransaction(isolation: Int, val threadLocal: ThreadLocal, - val transactionBoundaries: Subject, - val cordaPersistence: CordaPersistence) { +fun currentDBSession(): Session = DatabaseTransactionManager.current().session - val id: UUID = UUID.randomUUID() - - val connection: Connection by lazy(LazyThreadSafetyMode.NONE) { - cordaPersistence.dataSource.connection - .apply { - autoCommit = false - transactionIsolation = isolation - } - } - - private val sessionDelegate = lazy { - val session = cordaPersistence.entityManagerFactory.withOptions().connection(connection).openSession() - hibernateTransaction = session.beginTransaction() - session - } - - val session: Session by sessionDelegate - private lateinit var hibernateTransaction: Transaction - - private val outerTransaction: DatabaseTransaction? = threadLocal.get() - - fun commit() { - if (sessionDelegate.isInitialized()) { - hibernateTransaction.commit() - } - connection.commit() - } - - fun rollback() { - if (sessionDelegate.isInitialized() && session.isOpen) { - session.clear() - } - if (!connection.isClosed) { - connection.rollback() - } - } - - fun close() { - if (sessionDelegate.isInitialized() && session.isOpen) { - session.close() - } - connection.close() - threadLocal.set(outerTransaction) - if (outerTransaction == null) { - transactionBoundaries.onNext(DatabaseTransactionManager.Boundary(id)) - } - } -} - -fun currentDBSession() = DatabaseTransactionManager.current().session class DatabaseTransactionManager(initDataSource: CordaPersistence) { companion object { private val threadLocalDb = ThreadLocal() @@ -95,11 +41,15 @@ class DatabaseTransactionManager(initDataSource: CordaPersistence) { fun currentOrNull(): DatabaseTransaction? = manager.currentOrNull() - fun currentOrNew(isolation: Int = dataSource.transactionIsolationLevel) = currentOrNull() ?: manager.newTransaction(isolation) + fun currentOrNew(isolation: TransactionIsolationLevel = dataSource.defaultIsolationLevel): DatabaseTransaction { + return currentOrNull() ?: manager.newTransaction(isolation.jdbcValue) + } fun current(): DatabaseTransaction = currentOrNull() ?: error("No transaction in context.") - fun newTransaction(isolation: Int = dataSource.transactionIsolationLevel) = manager.newTransaction(isolation) + fun newTransaction(isolation: TransactionIsolationLevel = dataSource.defaultIsolationLevel): DatabaseTransaction { + return manager.newTransaction(isolation.jdbcValue) + } } data class Boundary(val txId: UUID) diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/HibernateConfiguration.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt similarity index 85% rename from node/src/main/kotlin/net/corda/node/services/persistence/HibernateConfiguration.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt index 89c04f7016..22a664022d 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/HibernateConfiguration.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt @@ -1,13 +1,9 @@ -package net.corda.node.services.persistence +package net.corda.nodeapi.internal.persistence import net.corda.core.internal.castIfPossible -import net.corda.core.node.services.IdentityService import net.corda.core.schemas.MappedSchema import net.corda.core.utilities.contextLogger import net.corda.core.utilities.toHexString -import net.corda.node.services.api.SchemaService -import net.corda.node.services.config.DatabaseConfig -import net.corda.node.utilities.DatabaseTransactionManager import org.hibernate.SessionFactory import org.hibernate.boot.MetadataSources import org.hibernate.boot.model.naming.Identifier @@ -18,14 +14,18 @@ import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment import org.hibernate.service.UnknownUnwrapTypeException import org.hibernate.type.AbstractSingleColumnStandardBasicType -import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor import org.hibernate.type.descriptor.sql.BlobTypeDescriptor import org.hibernate.type.descriptor.sql.VarbinaryTypeDescriptor import java.sql.Connection import java.util.concurrent.ConcurrentHashMap +import javax.persistence.AttributeConverter -class HibernateConfiguration(val schemaService: SchemaService, private val databaseConfig: DatabaseConfig, private val identityService: IdentityService) { +class HibernateConfiguration( + schemas: Set, + private val databaseConfig: DatabaseConfig, + private val attributeConverters: Collection> +) { companion object { private val logger = contextLogger() } @@ -33,13 +33,8 @@ class HibernateConfiguration(val schemaService: SchemaService, private val datab // TODO: make this a guava cache or similar to limit ability for this to grow forever. private val sessionFactories = ConcurrentHashMap, SessionFactory>() - val sessionFactoryForRegisteredSchemas = schemaService.schemaOptions.keys.let { + val sessionFactoryForRegisteredSchemas = schemas.let { logger.info("Init HibernateConfiguration for schemas: $it") - // Register the AbstractPartyDescriptor so Hibernate doesn't warn when encountering AbstractParty. Unfortunately - // Hibernate warns about not being able to find a descriptor if we don't provide one, but won't use it by default - // so we end up providing both descriptor and converter. We should re-examine this in later versions to see if - // either Hibernate can be convinced to stop warning, use the descriptor by default, or something else. - JavaTypeDescriptorRegistry.INSTANCE.addDescriptor(AbstractPartyDescriptor(identityService)) sessionFactoryForSchemas(it) } @@ -78,7 +73,7 @@ class HibernateConfiguration(val schemaService: SchemaService, private val datab } }) // register custom converters - applyAttributeConverter(AbstractPartyToX500NameAsStringConverter(identityService)) + attributeConverters.forEach { applyAttributeConverter(it) } // Register a tweaked version of `org.hibernate.type.MaterializedBlobType` that truncates logged messages. // to avoid OOM when large blobs might get logged. applyBasicType(CordaMaterializedBlobType, CordaMaterializedBlobType.name) diff --git a/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt similarity index 99% rename from node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt rename to node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt index 2ae3da9f74..7ed2ff6527 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt @@ -1,4 +1,4 @@ -package net.corda.node.utilities +package net.corda.nodeapi.internal.crypto import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512 @@ -13,7 +13,6 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.services.config.createKeystoreForCordaNode -import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.nodeapi.internal.serialization.SerializationContextImpl import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 12ecaf9f94..453afefea0 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -3,6 +3,8 @@ package net.corda.node.internal import com.codahale.metrics.MetricRegistry import com.google.common.collect.MutableClassToInstanceMap import com.google.common.util.concurrent.MoreExecutors +import com.zaxxer.hikari.HikariConfig +import com.zaxxer.hikari.HikariDataSource import net.corda.confidential.SwapIdentitiesFlow import net.corda.confidential.SwapIdentitiesHandler import net.corda.core.CordaException @@ -58,10 +60,12 @@ import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.VaultSoftLockManager import net.corda.node.shell.InteractiveShell import net.corda.node.utilities.AffinityExecutor -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.configureDatabase import net.corda.nodeapi.internal.crypto.* +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.DatabaseConfig +import net.corda.nodeapi.internal.persistence.HibernateConfiguration import org.apache.activemq.artemis.utils.ReusableLatch +import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry import org.slf4j.Logger import rx.Observable import java.io.IOException @@ -181,7 +185,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, log.info("Node starting up ...") initCertificate() val (keyPairs, info) = initNodeInfo() - val schemaService = NodeSchemaService(cordappLoader) + val schemaService = NodeSchemaService(cordappLoader.cordappSchemas) val identityService = makeIdentityService(info) // Do all of this in a database transaction so anything that might need a connection has one. val (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService, identityService) { database -> @@ -207,7 +211,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, MoreExecutors.shutdownAndAwaitTermination(serverThread as ExecutorService, 50, SECONDS) } } - makeVaultObservers(schedulerService, database.hibernateConfig, smm) + makeVaultObservers(schedulerService, database.hibernateConfig, smm, schemaService) val rpcOps = makeRPCOps(flowStarter, database, smm) startMessagingService(rpcOps) installCoreFlows() @@ -513,10 +517,11 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } protected open fun makeTransactionStorage(database: CordaPersistence): WritableTransactionStorage = DBTransactionStorage() - private fun makeVaultObservers(schedulerService: SchedulerService, hibernateConfig: HibernateConfiguration, smm: StateMachineManager) { + + private fun makeVaultObservers(schedulerService: SchedulerService, hibernateConfig: HibernateConfiguration, smm: StateMachineManager, schemaService: SchemaService) { VaultSoftLockManager.install(services.vaultService, smm) ScheduledActivityObserver.install(services.vaultService, schedulerService) - HibernateObserver.install(services.vaultService.rawUpdates, hibernateConfig) + HibernateObserver.install(services.vaultService.rawUpdates, hibernateConfig, schemaService) } @VisibleForTesting @@ -753,7 +758,23 @@ internal class FlowStarterImpl(private val serverThread: AffinityExecutor, priva } class ConfigurationException(message: String) : CordaException(message) + /** * Thrown when a node is about to start and its network map cache doesn't contain any node. */ -internal class NetworkMapCacheEmptyException : Exception() \ No newline at end of file +internal class NetworkMapCacheEmptyException : Exception() + +fun configureDatabase(dataSourceProperties: Properties, + databaseConfig: DatabaseConfig, + identityService: IdentityService, + schemaService: SchemaService = NodeSchemaService()): CordaPersistence { + // Register the AbstractPartyDescriptor so Hibernate doesn't warn when encountering AbstractParty. Unfortunately + // Hibernate warns about not being able to find a descriptor if we don't provide one, but won't use it by default + // so we end up providing both descriptor and converter. We should re-examine this in later versions to see if + // either Hibernate can be convinced to stop warning, use the descriptor by default, or something else. + JavaTypeDescriptorRegistry.INSTANCE.addDescriptor(AbstractPartyDescriptor(identityService)) + val config = HikariConfig(dataSourceProperties) + val dataSource = HikariDataSource(config) + val attributeConverters = listOf(AbstractPartyToX500NameAsStringConverter(identityService)) + return CordaPersistence(dataSource, databaseConfig, schemaService.schemaOptions.keys, attributeConverters) +} diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt index ef0597c675..b3f7ebbec3 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -26,7 +26,7 @@ import net.corda.node.services.api.FlowStarter import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.messaging.context import net.corda.node.services.statemachine.StateMachineManager -import net.corda.node.utilities.CordaPersistence +import net.corda.nodeapi.internal.persistence.CordaPersistence import rx.Observable import java.io.InputStream import java.security.PublicKey diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index f7824796a4..71f3ae581a 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -10,9 +10,9 @@ import net.corda.core.node.NodeInfo import net.corda.core.node.ServiceHub import net.corda.core.node.services.IdentityService import net.corda.core.node.services.TransactionVerifierService -import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.serialization.internal.SerializationEnvironmentImpl import net.corda.core.serialization.internal.nodeSerializationEnv +import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.node.VersionInfo import net.corda.node.internal.cordapp.CordappLoader @@ -25,10 +25,10 @@ import net.corda.node.services.messaging.* import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.utilities.AddressUtils import net.corda.node.utilities.AffinityExecutor -import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.DemoClock import net.corda.nodeapi.internal.ShutdownHook import net.corda.nodeapi.internal.addShutdownHook +import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.serialization.* import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme import org.slf4j.Logger diff --git a/node/src/main/kotlin/net/corda/node/internal/SecureCordaRPCOps.kt b/node/src/main/kotlin/net/corda/node/internal/SecureCordaRPCOps.kt index 7083a434d2..8394c35ae9 100644 --- a/node/src/main/kotlin/net/corda/node/internal/SecureCordaRPCOps.kt +++ b/node/src/main/kotlin/net/corda/node/internal/SecureCordaRPCOps.kt @@ -5,7 +5,7 @@ import net.corda.node.services.api.FlowStarter import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.messaging.rpcContext import net.corda.node.services.statemachine.StateMachineManager -import net.corda.node.utilities.CordaPersistence +import net.corda.nodeapi.internal.persistence.CordaPersistence /** * Implementation of [CordaRPCOps] that checks authorisation. diff --git a/node/src/main/kotlin/net/corda/node/internal/StartedNode.kt b/node/src/main/kotlin/net/corda/node/internal/StartedNode.kt index 5335ecd228..e0c63a038f 100644 --- a/node/src/main/kotlin/net/corda/node/internal/StartedNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/StartedNode.kt @@ -14,7 +14,7 @@ import net.corda.node.services.api.StartedNodeServices import net.corda.node.services.messaging.MessagingService import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.services.statemachine.StateMachineManager -import net.corda.node.utilities.CordaPersistence +import net.corda.nodeapi.internal.persistence.CordaPersistence import rx.Observable interface StartedNode { diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt index 0719da9830..4863471957 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt @@ -52,12 +52,14 @@ class CordappLoader private constructor(private val cordappJarPaths: List get() = cordapps.flatMap { it.customSchemas }.toSet() + companion object { private val logger = contextLogger() /** * Default cordapp dir name */ - val CORDAPPS_DIR_NAME = "cordapps" + private const val CORDAPPS_DIR_NAME = "cordapps" /** * Creates a default CordappLoader intended to be used in non-dev or non-test environments. @@ -94,8 +96,9 @@ class CordappLoader private constructor(private val cordappJarPaths: List) - = cordappLoadersCache.computeIfAbsent(testPackages, { CordappLoader(testPackages.flatMap(this::createScanPackage)) }) + fun createWithTestPackages(testPackages: List): CordappLoader { + return cordappLoadersCache.computeIfAbsent(testPackages, { CordappLoader(testPackages.flatMap(this::createScanPackage)) }) + } /** * Creates a dev mode CordappLoader intended only to be used in test environments diff --git a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt index a5999d2026..e9794842d6 100644 --- a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt +++ b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt @@ -1,11 +1,11 @@ package net.corda.node.services.api import net.corda.core.concurrent.CordaFuture +import net.corda.core.context.InvocationContext import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic import net.corda.core.flows.StateMachineRunId import net.corda.core.internal.FlowStateMachine -import net.corda.core.context.InvocationContext import net.corda.core.internal.uncheckedCast import net.corda.core.messaging.DataFeed import net.corda.core.messaging.StateMachineTransactionMapping @@ -23,7 +23,7 @@ import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.messaging.MessagingService import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl import net.corda.node.services.statemachine.FlowStateMachineImpl -import net.corda.node.utilities.CordaPersistence +import net.corda.nodeapi.internal.persistence.CordaPersistence interface NetworkMapCacheInternal : NetworkMapCache, NetworkMapCacheBaseInternal interface NetworkMapCacheBaseInternal : NetworkMapCacheBase { diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index 86124da7fc..666a14a001 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -5,6 +5,7 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.seconds import net.corda.node.services.messaging.CertificateChainCheckPolicy +import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.User import net.corda.nodeapi.config.NodeSSLConfiguration import net.corda.nodeapi.config.parseAs @@ -43,25 +44,6 @@ interface NodeConfiguration : NodeSSLConfiguration { data class DevModeOptions(val disableCheckpointChecker: Boolean = false) -data class DatabaseConfig( - val initialiseSchema: Boolean = true, - val serverNameTablePrefix: String = "", - val transactionIsolationLevel: TransactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ -) - -enum class TransactionIsolationLevel { - NONE, - READ_UNCOMMITTED, - READ_COMMITTED, - REPEATABLE_READ, - SERIALIZABLE; - - /** - * The JDBC constant value of the same name but with prefixed with TRANSACTION_ defined in [java.sql.Connection]. - */ - val jdbcValue: Int = java.sql.Connection::class.java.getField("TRANSACTION_$name").get(null) as Int -} - fun NodeConfiguration.shouldCheckCheckpoints(): Boolean { return this.devMode && this.devModeOptions?.disableCheckpointChecker != true } diff --git a/node/src/main/kotlin/net/corda/node/services/events/NodeSchedulerService.kt b/node/src/main/kotlin/net/corda/node/services/events/NodeSchedulerService.kt index ec4fdd855d..31badf6ec0 100644 --- a/node/src/main/kotlin/net/corda/node/services/events/NodeSchedulerService.kt +++ b/node/src/main/kotlin/net/corda/node/services/events/NodeSchedulerService.kt @@ -3,6 +3,7 @@ package net.corda.node.services.events import co.paralleluniverse.fibers.Suspendable import com.google.common.util.concurrent.ListenableFuture import net.corda.core.context.InvocationContext +import net.corda.core.context.Origin import net.corda.core.contracts.SchedulableState import net.corda.core.contracts.ScheduledActivity import net.corda.core.contracts.ScheduledStateRef @@ -12,7 +13,6 @@ import net.corda.core.flows.FlowLogic import net.corda.core.internal.ThreadBox import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.concurrent.flatMap -import net.corda.core.context.Origin import net.corda.core.internal.until import net.corda.core.node.StateLoader import net.corda.core.schemas.PersistentStateRef @@ -24,9 +24,9 @@ import net.corda.node.services.api.FlowStarter import net.corda.node.services.api.SchedulerService import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl import net.corda.node.utilities.AffinityExecutor -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.NODE_DATABASE_PREFIX import net.corda.node.utilities.PersistentMap +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import org.apache.activemq.artemis.utils.ReusableLatch import java.time.Clock import java.time.Instant diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index 282e519a38..da2415aad0 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -13,7 +13,7 @@ import net.corda.core.utilities.MAX_HASH_HEX_SIZE import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug import net.corda.node.utilities.AppendOnlyPersistentMap -import net.corda.node.utilities.NODE_DATABASE_PREFIX +import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import net.corda.nodeapi.internal.crypto.X509CertificateFactory import org.bouncycastle.cert.X509CertificateHolder import java.security.InvalidAlgorithmParameterException diff --git a/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt b/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt index b449653085..96f41e9670 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt @@ -7,7 +7,7 @@ import net.corda.core.node.services.KeyManagementService import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.MAX_HASH_HEX_SIZE import net.corda.node.utilities.AppendOnlyPersistentMap -import net.corda.node.utilities.NODE_DATABASE_PREFIX +import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import org.bouncycastle.operator.ContentSigner import java.security.KeyPair import java.security.PrivateKey diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt index 8a045265dd..f4976610d8 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt @@ -17,10 +17,13 @@ import net.corda.core.utilities.trace import net.corda.node.VersionInfo import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.statemachine.StateMachineManagerImpl -import net.corda.node.utilities.* -import net.corda.nodeapi.internal.ArtemisMessagingComponent +import net.corda.node.utilities.AffinityExecutor +import net.corda.node.utilities.AppendOnlyPersistentMap +import net.corda.node.utilities.PersistentMap import net.corda.nodeapi.internal.ArtemisMessagingComponent.* import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_QUEUE +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import org.apache.activemq.artemis.api.core.ActiveMQObjectClosedException import org.apache.activemq.artemis.api.core.Message.* import org.apache.activemq.artemis.api.core.RoutingType diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index 0b04b0eff3..3efb461a81 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -22,9 +22,9 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.node.services.api.NetworkMapCacheBaseInternal import net.corda.node.services.api.NetworkMapCacheInternal -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.bufferUntilDatabaseCommit -import net.corda.node.utilities.wrapWithDatabaseTransaction +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit +import net.corda.nodeapi.internal.persistence.wrapWithDatabaseTransaction import org.hibernate.Session import rx.Observable import rx.subjects.PublishSubject diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/DBCheckpointStorage.kt b/node/src/main/kotlin/net/corda/node/services/persistence/DBCheckpointStorage.kt index bf3ac2bf09..5e92461c18 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/DBCheckpointStorage.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/DBCheckpointStorage.kt @@ -3,8 +3,8 @@ package net.corda.node.services.persistence import net.corda.core.serialization.SerializedBytes import net.corda.node.services.api.Checkpoint import net.corda.node.services.api.CheckpointStorage -import net.corda.node.utilities.NODE_DATABASE_PREFIX -import net.corda.node.utilities.currentDBSession +import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX +import net.corda.nodeapi.internal.persistence.currentDBSession import javax.persistence.Column import javax.persistence.Entity import javax.persistence.Id diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt index 6c24e81ab0..3ed86c65af 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt @@ -7,6 +7,9 @@ import net.corda.core.messaging.DataFeed import net.corda.core.messaging.StateMachineTransactionMapping import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage import net.corda.node.utilities.* +import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX +import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit +import net.corda.nodeapi.internal.persistence.wrapWithDatabaseTransaction import rx.subjects.PublishSubject import java.util.* import javax.annotation.concurrent.ThreadSafe diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt index 8c6c5c666e..f8c887d073 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionStorage.kt @@ -8,6 +8,9 @@ import net.corda.core.serialization.* import net.corda.core.transactions.SignedTransaction import net.corda.node.services.api.WritableTransactionStorage import net.corda.node.utilities.* +import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX +import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit +import net.corda.nodeapi.internal.persistence.wrapWithDatabaseTransaction import rx.Observable import rx.subjects.PublishSubject import javax.persistence.* diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt b/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt index 6c4d4a9e75..da6dd489f8 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt @@ -17,9 +17,9 @@ import net.corda.core.node.services.vault.AttachmentSort import net.corda.core.serialization.* import net.corda.core.utilities.contextLogger import net.corda.node.services.vault.HibernateAttachmentQueryCriteriaParser -import net.corda.node.utilities.DatabaseTransactionManager -import net.corda.node.utilities.NODE_DATABASE_PREFIX -import net.corda.node.utilities.currentDBSession +import net.corda.nodeapi.internal.persistence.DatabaseTransactionManager +import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX +import net.corda.nodeapi.internal.persistence.currentDBSession import java.io.* import java.nio.file.Paths import java.time.Instant diff --git a/node/src/main/kotlin/net/corda/node/services/schema/HibernateObserver.kt b/node/src/main/kotlin/net/corda/node/services/schema/HibernateObserver.kt index 41e6b6d641..17fab1f4ac 100644 --- a/node/src/main/kotlin/net/corda/node/services/schema/HibernateObserver.kt +++ b/node/src/main/kotlin/net/corda/node/services/schema/HibernateObserver.kt @@ -9,8 +9,9 @@ import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentStateRef import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug -import net.corda.node.services.persistence.HibernateConfiguration -import net.corda.node.utilities.DatabaseTransactionManager +import net.corda.node.services.api.SchemaService +import net.corda.nodeapi.internal.persistence.HibernateConfiguration +import net.corda.nodeapi.internal.persistence.DatabaseTransactionManager import org.hibernate.FlushMode import rx.Observable @@ -18,12 +19,11 @@ import rx.Observable * A vault observer that extracts Object Relational Mappings for contract states that support it, and persists them with Hibernate. */ // TODO: Manage version evolution of the schemas via additional tooling. -class HibernateObserver private constructor(private val config: HibernateConfiguration) { +class HibernateObserver private constructor(private val config: HibernateConfiguration, private val schemaService: SchemaService) { companion object { private val log = contextLogger() - @JvmStatic - fun install(vaultUpdates: Observable>, config: HibernateConfiguration): HibernateObserver { - val observer = HibernateObserver(config) + fun install(vaultUpdates: Observable>, config: HibernateConfiguration, schemaService: SchemaService): HibernateObserver { + val observer = HibernateObserver(config, schemaService) vaultUpdates.subscribe { observer.persist(it.produced) } return observer } @@ -36,7 +36,7 @@ class HibernateObserver private constructor(private val config: HibernateConfigu private fun persistState(stateAndRef: StateAndRef) { val state = stateAndRef.state.data log.debug { "Asked to persist state ${stateAndRef.ref}" } - config.schemaService.selectSchemas(state).forEach { persistStateWithSchema(state, stateAndRef.ref, it) } + schemaService.selectSchemas(state).forEach { persistStateWithSchema(state, stateAndRef.ref, it) } } @VisibleForTesting @@ -47,7 +47,7 @@ class HibernateObserver private constructor(private val config: HibernateConfigu flushMode(FlushMode.MANUAL). openSession() session.use { - val mappedObject = config.schemaService.generateMappedObject(state, schema) + val mappedObject = schemaService.generateMappedObject(state, schema) mappedObject.stateRef = PersistentStateRef(stateRef) it.persist(mappedObject) it.flush() diff --git a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt index 99282681b8..df4b85c3c5 100644 --- a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt +++ b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt @@ -9,8 +9,8 @@ import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState import net.corda.core.schemas.QueryableState import net.corda.core.serialization.SingletonSerializeAsToken -import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.services.api.SchemaService +import net.corda.node.services.api.SchemaService.SchemaOptions import net.corda.node.services.events.NodeSchedulerService import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.keys.PersistentKeyManagementService @@ -27,14 +27,12 @@ import net.corda.node.services.vault.VaultSchemaV1 /** * Most basic implementation of [SchemaService]. - * @param cordappLoader if not null, custom schemas will be extracted from its cordapps. * TODO: support loading schema options from node configuration. * TODO: support configuring what schemas are to be selected for persistence. * TODO: support plugins for schema version upgrading or custom mapping not supported by original [QueryableState]. * TODO: create whitelisted tables when a CorDapp is first installed */ -class NodeSchemaService(cordappLoader: CordappLoader?) : SchemaService, SingletonSerializeAsToken() { - +class NodeSchemaService(extraSchemas: Set = emptySet()) : SchemaService, SingletonSerializeAsToken() { // Entities for compulsory services object NodeServices @@ -60,17 +58,12 @@ class NodeSchemaService(cordappLoader: CordappLoader?) : SchemaService, Singleto // Required schemas are those used by internal Corda services // For example, cash is used by the vault for coin selection (but will be extracted as a standalone CorDapp in future) private val requiredSchemas: Map = - mapOf(Pair(CommonSchemaV1, SchemaService.SchemaOptions()), - Pair(VaultSchemaV1, SchemaService.SchemaOptions()), - Pair(NodeInfoSchemaV1, SchemaService.SchemaOptions()), - Pair(NodeServicesV1, SchemaService.SchemaOptions())) + mapOf(Pair(CommonSchemaV1, SchemaOptions()), + Pair(VaultSchemaV1, SchemaOptions()), + Pair(NodeInfoSchemaV1, SchemaOptions()), + Pair(NodeServicesV1, SchemaOptions())) - override val schemaOptions: Map = if (cordappLoader == null) { - requiredSchemas - } else { - val customSchemas = cordappLoader.cordapps.flatMap { it.customSchemas }.toSet() - requiredSchemas.plus(customSchemas.map { mappedSchema -> Pair(mappedSchema, SchemaService.SchemaOptions()) }) - } + override val schemaOptions: Map = requiredSchemas + extraSchemas.associateBy({ it }, { SchemaOptions() }) // Currently returns all schemas supported by the state, with no filtering or enrichment. override fun selectSchemas(state: ContractState): Iterable { diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt index 69bc41778d..2fb26c4f23 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt @@ -7,6 +7,7 @@ import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.strands.Strand import com.google.common.primitives.Primitives import net.corda.core.concurrent.CordaFuture +import net.corda.core.context.InvocationContext import net.corda.core.crypto.SecureHash import net.corda.core.crypto.random63BitValue import net.corda.core.flows.* @@ -15,7 +16,6 @@ import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.* import net.corda.core.internal.concurrent.OpenFuture import net.corda.core.internal.concurrent.openFuture -import net.corda.core.context.InvocationContext import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.serialize import net.corda.core.transactions.SignedTransaction @@ -25,9 +25,9 @@ import net.corda.node.services.api.FlowPermissionAuditEvent import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.logging.pushToLoggingContext import net.corda.node.services.statemachine.FlowSessionState.Initiating -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.DatabaseTransaction -import net.corda.node.utilities.DatabaseTransactionManager +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.DatabaseTransaction +import net.corda.nodeapi.internal.persistence.DatabaseTransactionManager import org.slf4j.Logger import org.slf4j.LoggerFactory import java.nio.file.Paths diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManagerImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManagerImpl.kt index 73fe043ee8..500529aa5c 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManagerImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManagerImpl.kt @@ -27,7 +27,10 @@ import net.corda.core.serialization.SerializationDefaults.SERIALIZATION_FACTORY import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize -import net.corda.core.utilities.* +import net.corda.core.utilities.Try +import net.corda.core.utilities.contextLogger +import net.corda.core.utilities.debug +import net.corda.core.utilities.trace import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.services.api.Checkpoint import net.corda.node.services.api.CheckpointStorage @@ -35,7 +38,11 @@ import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.config.shouldCheckCheckpoints import net.corda.node.services.messaging.ReceivedMessage import net.corda.node.services.messaging.TopicSession -import net.corda.node.utilities.* +import net.corda.node.utilities.AffinityExecutor +import net.corda.node.utilities.newNamedSingleThreadExecutor +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit +import net.corda.nodeapi.internal.persistence.wrapWithDatabaseTransaction import net.corda.nodeapi.internal.serialization.SerializeAsTokenContextImpl import net.corda.nodeapi.internal.serialization.withTokenContext import org.apache.activemq.artemis.utils.ReusableLatch diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt index adf1afd69d..780c4815c6 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt @@ -23,7 +23,7 @@ import net.corda.core.utilities.* import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.config.BFTSMaRtConfiguration import net.corda.node.utilities.AppendOnlyPersistentMap -import net.corda.node.utilities.NODE_DATABASE_PREFIX +import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import java.security.PublicKey import javax.persistence.Entity import javax.persistence.Table diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/DistributedImmutableMap.kt b/node/src/main/kotlin/net/corda/node/services/transactions/DistributedImmutableMap.kt index 5a67023df9..4e6bc0ac89 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/DistributedImmutableMap.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/DistributedImmutableMap.kt @@ -5,8 +5,9 @@ import io.atomix.copycat.Query import io.atomix.copycat.server.Commit import io.atomix.copycat.server.StateMachine import net.corda.core.utilities.contextLogger -import net.corda.node.utilities.* -import java.util.LinkedHashMap +import net.corda.node.utilities.AppendOnlyPersistentMap +import net.corda.nodeapi.internal.persistence.CordaPersistence +import java.util.* /** * A distributed map state machine that doesn't allow overriding values. The state machine is replicated diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt b/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt index dd0e527ba1..a38e6bd446 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt @@ -12,7 +12,7 @@ import net.corda.core.schemas.PersistentStateRef import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.contextLogger import net.corda.node.utilities.AppendOnlyPersistentMap -import net.corda.node.utilities.NODE_DATABASE_PREFIX +import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import java.io.Serializable import java.util.* import javax.annotation.concurrent.ThreadSafe diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt index 5b1e1743d1..53e7c19fbe 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/RaftUniquenessProvider.kt @@ -29,10 +29,10 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.contextLogger import net.corda.node.services.config.RaftConfig import net.corda.node.utilities.AppendOnlyPersistentMap -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.NODE_DATABASE_PREFIX import net.corda.nodeapi.config.NodeSSLConfiguration import net.corda.nodeapi.config.SSLConfiguration +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import java.nio.file.Path import java.util.concurrent.CompletableFuture import javax.annotation.concurrent.ThreadSafe diff --git a/node/src/main/kotlin/net/corda/node/services/upgrade/ContractUpgradeServiceImpl.kt b/node/src/main/kotlin/net/corda/node/services/upgrade/ContractUpgradeServiceImpl.kt index 506160a6e9..162d8d945b 100644 --- a/node/src/main/kotlin/net/corda/node/services/upgrade/ContractUpgradeServiceImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/upgrade/ContractUpgradeServiceImpl.kt @@ -4,7 +4,7 @@ import net.corda.core.contracts.StateRef import net.corda.core.contracts.UpgradedContract import net.corda.core.node.services.ContractUpgradeService import net.corda.core.serialization.SingletonSerializeAsToken -import net.corda.node.utilities.NODE_DATABASE_PREFIX +import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import net.corda.node.utilities.PersistentMap import javax.persistence.Column import javax.persistence.Entity diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index c3c2e8e8b7..478e139e06 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -17,12 +17,12 @@ import net.corda.core.transactions.NotaryChangeWireTransaction import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.* import net.corda.node.services.api.VaultServiceInternal -import net.corda.node.services.persistence.HibernateConfiguration +import net.corda.nodeapi.internal.persistence.HibernateConfiguration import net.corda.node.services.statemachine.FlowStateMachineImpl -import net.corda.node.utilities.DatabaseTransactionManager -import net.corda.node.utilities.bufferUntilDatabaseCommit -import net.corda.node.utilities.currentDBSession -import net.corda.node.utilities.wrapWithDatabaseTransaction +import net.corda.nodeapi.internal.persistence.DatabaseTransactionManager +import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit +import net.corda.nodeapi.internal.persistence.currentDBSession +import net.corda.nodeapi.internal.persistence.wrapWithDatabaseTransaction import org.hibernate.Session import rx.Observable import rx.subjects.PublishSubject diff --git a/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt b/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt index 755b47e3a0..3772e15290 100644 --- a/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt +++ b/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt @@ -22,9 +22,7 @@ import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.DataFeed import net.corda.core.messaging.FlowProgressHandle import net.corda.core.messaging.StateMachineUpdate -import net.corda.core.utilities.getOrThrow import net.corda.core.node.services.IdentityService -import net.corda.core.utilities.loggerFor import net.corda.node.internal.Node import net.corda.node.internal.StartedNode import net.corda.node.services.RPCUserService @@ -33,8 +31,8 @@ import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT import net.corda.node.services.messaging.RpcAuthContext import net.corda.node.services.messaging.RpcPermissions import net.corda.node.utilities.ANSIProgressRenderer -import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.StdoutANSIProgressRenderer +import net.corda.nodeapi.internal.persistence.CordaPersistence import org.crsh.command.InvocationContext import org.crsh.console.jline.JLineProcessor import org.crsh.console.jline.TerminalFactory @@ -94,7 +92,7 @@ object InteractiveShell { * Starts an interactive shell connected to the local terminal. This shell gives administrator access to the node * internals. */ - fun startShell(configuration:NodeConfiguration, cordaRPCOps: CordaRPCOps, userService: RPCUserService, identityService: IdentityService, database:CordaPersistence) { + fun startShell(configuration:NodeConfiguration, cordaRPCOps: CordaRPCOps, userService: RPCUserService, identityService: IdentityService, database: CordaPersistence) { this.rpcOps = cordaRPCOps this.userService = userService this.identityService = identityService diff --git a/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt b/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt index 90f2d018bb..a21da310ee 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt @@ -1,6 +1,7 @@ package net.corda.node.utilities import net.corda.core.utilities.contextLogger +import net.corda.nodeapi.internal.persistence.currentDBSession import java.util.* diff --git a/node/src/main/kotlin/net/corda/node/utilities/PersistentMap.kt b/node/src/main/kotlin/net/corda/node/utilities/PersistentMap.kt index b0dbd2208b..c023d113fb 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/PersistentMap.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/PersistentMap.kt @@ -1,13 +1,12 @@ package net.corda.node.utilities - import com.google.common.cache.RemovalCause import com.google.common.cache.RemovalListener import com.google.common.cache.RemovalNotification import net.corda.core.utilities.contextLogger +import net.corda.nodeapi.internal.persistence.currentDBSession import java.util.* - /** * Implements an unbound caching layer on top of a table accessed via Hibernate mapping. */ 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 be124c6bd0..c1f2a539eb 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 @@ -20,8 +20,8 @@ import net.corda.finance.contracts.DealState; import net.corda.finance.contracts.asset.Cash; import net.corda.finance.schemas.CashSchemaV1; import net.corda.node.services.identity.InMemoryIdentityService; -import net.corda.node.utilities.CordaPersistence; -import net.corda.node.utilities.DatabaseTransaction; +import net.corda.nodeapi.internal.persistence.CordaPersistence; +import net.corda.nodeapi.internal.persistence.DatabaseTransaction; import net.corda.testing.SerializationEnvironmentRule; import net.corda.testing.contracts.DummyLinearContract; import net.corda.testing.contracts.VaultFiller; diff --git a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt index 93a2d39dc5..3f0a7d40c1 100644 --- a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt +++ b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt @@ -10,10 +10,10 @@ import net.corda.core.identity.Party import net.corda.core.internal.concurrent.openFuture import net.corda.core.messaging.FlowProgressHandleImpl import net.corda.core.utilities.ProgressTracker -import net.corda.node.services.config.DatabaseConfig +import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.shell.InteractiveShell -import net.corda.node.utilities.configureDatabase +import net.corda.node.internal.configureDatabase import net.corda.testing.DEV_TRUST_ROOT import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP_IDENTITY 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 7696190b3c..9196c1d293 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -36,7 +36,7 @@ import net.corda.node.internal.StartedNode import net.corda.node.services.api.WritableTransactionStorage import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.persistence.checkpoints -import net.corda.node.utilities.CordaPersistence +import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.* import net.corda.testing.contracts.VaultFiller import net.corda.testing.node.* 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 45ff894ae2..65b85a51cf 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,6 +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.node.MockServices.Companion.makeTestDataSourceProperties import org.assertj.core.api.Assertions.assertThatThrownBy 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 92127f94f9..5796609f1c 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 @@ -23,7 +23,6 @@ import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProviderImpl import net.corda.node.services.api.MonitoringService import net.corda.node.services.api.ServiceHubInternal -import net.corda.node.services.config.DatabaseConfig import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.services.network.NetworkMapCacheImpl import net.corda.node.services.persistence.DBCheckpointStorage @@ -32,8 +31,9 @@ import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.statemachine.StateMachineManagerImpl import net.corda.node.services.vault.NodeVaultService import net.corda.node.utilities.AffinityExecutor -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.configureDatabase +import net.corda.node.internal.configureDatabase +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.node.* 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 04ac68ed84..f3185b58eb 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 @@ -10,11 +10,11 @@ import net.corda.core.internal.cert import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException -import net.corda.node.utilities.CordaPersistence import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.* import net.corda.testing.node.MockServices import org.junit.After 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 a4ef0e5dd0..4b7972d02d 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 @@ -4,15 +4,15 @@ import net.corda.core.crypto.generateKeyPair import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.services.RPCUserService import net.corda.node.services.RPCUserServiceImpl -import net.corda.node.services.config.DatabaseConfig import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.configureWithDevSSLCertificate import net.corda.node.services.network.NetworkMapCacheImpl import net.corda.node.services.network.PersistentNetworkMapCache import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.configureDatabase +import net.corda.node.internal.configureDatabase +import net.corda.nodeapi.internal.persistence.CordaPersistence +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 diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt index 52a91ef87f..41190a8355 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt @@ -4,10 +4,10 @@ import com.google.common.primitives.Ints import net.corda.core.serialization.SerializedBytes import net.corda.node.services.api.Checkpoint import net.corda.node.services.api.CheckpointStorage -import net.corda.node.services.config.DatabaseConfig import net.corda.node.services.transactions.PersistentUniquenessProvider -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.configureDatabase +import net.corda.node.internal.configureDatabase +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.LogHelper import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties 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 fcd85e05c6..f70118e16a 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 @@ -10,12 +10,13 @@ import net.corda.core.toFuture import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.WireTransaction import net.corda.node.services.api.VaultServiceInternal -import net.corda.node.services.config.DatabaseConfig import net.corda.node.services.schema.HibernateObserver +import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.node.services.vault.NodeVaultService -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.configureDatabase +import net.corda.node.internal.configureDatabase +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.* import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties @@ -40,13 +41,14 @@ class DBTransactionStorageTests { fun setUp() { LogHelper.setLevel(PersistentUniquenessProvider::class) val dataSourceProps = makeTestDataSourceProperties() - database = configureDatabase(dataSourceProps, DatabaseConfig(), rigorousMock()) + val schemaService = NodeSchemaService() + database = configureDatabase(dataSourceProps, DatabaseConfig(), rigorousMock(), schemaService) database.transaction { services = object : MockServices(BOB_KEY) { override val vaultService: VaultServiceInternal get() { val vaultService = NodeVaultService(clock, keyManagementService, stateLoader, database.hibernateConfig) - hibernatePersister = HibernateObserver.install(vaultService.rawUpdates, database.hibernateConfig) + hibernatePersister = HibernateObserver.install(vaultService.rawUpdates, database.hibernateConfig, schemaService) return vaultService } 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 3709de0e4b..3d627eaf24 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 @@ -23,16 +23,21 @@ import net.corda.core.utilities.toBase58String import net.corda.finance.DOLLARS import net.corda.finance.POUNDS import net.corda.finance.SWISS_FRANCS -import net.corda.finance.contracts.asset.* +import net.corda.finance.contracts.asset.Cash +import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY +import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_NAME +import net.corda.finance.contracts.asset.DummyFungibleContract import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.schemas.SampleCashSchemaV2 import net.corda.finance.schemas.SampleCashSchemaV3 import net.corda.finance.utils.sumCash -import net.corda.node.services.config.DatabaseConfig import net.corda.node.services.schema.HibernateObserver +import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.vault.VaultSchemaV1 -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.configureDatabase +import net.corda.node.internal.configureDatabase +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.contracts.* import net.corda.testing.node.MockServices @@ -92,12 +97,13 @@ class HibernateConfigurationTest { doReturn(it).whenever(mock).wellKnownPartyFromX500Name(it.name) } } - database = configureDatabase(dataSourceProps, DatabaseConfig(), identityService) + val schemaService = NodeSchemaService() + database = configureDatabase(dataSourceProps, DatabaseConfig(), identityService, schemaService) database.transaction { hibernateConfig = database.hibernateConfig // `consumeCash` expects we can self-notarise transactions services = object : MockServices(cordappPackages, BOB_NAME, generateKeyPair(), DUMMY_NOTARY_KEY) { - override val vaultService = makeVaultService(database.hibernateConfig) + override val vaultService = makeVaultService(database.hibernateConfig, schemaService) override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable) { for (stx in txs) { validatedTransactions.addTransaction(stx) diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt index 658be5b8c8..d21cd6d6db 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt @@ -13,10 +13,10 @@ import net.corda.core.node.services.vault.AttachmentQueryCriteria import net.corda.core.node.services.vault.AttachmentSort import net.corda.core.node.services.vault.Builder import net.corda.core.node.services.vault.Sort -import net.corda.node.services.config.DatabaseConfig import net.corda.node.services.transactions.PersistentUniquenessProvider -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.configureDatabase +import net.corda.node.internal.configureDatabase +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.LogHelper import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.rigorousMock 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 8ba9fb8f49..5f12128d53 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 @@ -11,9 +11,9 @@ import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState import net.corda.core.schemas.QueryableState import net.corda.node.services.api.SchemaService -import net.corda.node.services.config.DatabaseConfig -import net.corda.node.utilities.DatabaseTransactionManager -import net.corda.node.utilities.configureDatabase +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.contracts.DummyContract @@ -66,7 +66,7 @@ class HibernateObserverTests { } } val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock(), schemaService) - HibernateObserver.install(rawUpdatesPublisher, database.hibernateConfig) + HibernateObserver.install(rawUpdatesPublisher, database.hibernateConfig, schemaService) database.transaction { 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() diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt index 4aff179a2f..ff8a0780c1 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt @@ -10,9 +10,9 @@ import net.corda.core.internal.concurrent.asCordaFuture import net.corda.core.internal.concurrent.transpose import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow -import net.corda.node.services.config.DatabaseConfig -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.configureDatabase +import net.corda.node.internal.configureDatabase +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.LogHelper import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.freeLocalHostAndPort 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 4c15bb37e7..75f7b88fd4 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 @@ -2,9 +2,9 @@ package net.corda.node.services.transactions import net.corda.core.crypto.SecureHash import net.corda.core.node.services.UniquenessException -import net.corda.node.services.config.DatabaseConfig -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.configureDatabase +import net.corda.node.internal.configureDatabase +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.* import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import org.junit.After 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 4ffdfd05d1..e8dbd4be40 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 @@ -34,7 +34,7 @@ import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_NAME import net.corda.finance.contracts.getCashBalance import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.utils.sumCash -import net.corda.node.utilities.CordaPersistence +import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.* import net.corda.testing.contracts.VaultFiller import net.corda.testing.node.MockServices 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 6586686921..0580cca49d 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 @@ -28,9 +28,9 @@ import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.schemas.CashSchemaV1.PersistentCashState import net.corda.finance.schemas.CommercialPaperSchemaV1 import net.corda.finance.schemas.SampleCashSchemaV3 -import net.corda.node.services.config.DatabaseConfig -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.configureDatabase +import net.corda.node.internal.configureDatabase +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.* import net.corda.testing.contracts.* import net.corda.testing.node.MockServices 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 c6fd88d4ae..0163cc210b 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 @@ -25,7 +25,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.services.api.VaultServiceInternal -import net.corda.node.services.persistence.HibernateConfiguration +import net.corda.nodeapi.internal.persistence.HibernateConfiguration import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.rigorousMock 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 361519e41d..862a420a63 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 @@ -24,7 +24,7 @@ import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_NAME import net.corda.finance.contracts.getCashBalance import net.corda.finance.schemas.CashSchemaV1 -import net.corda.node.utilities.CordaPersistence +import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.* import net.corda.testing.contracts.* import net.corda.testing.node.MockServices diff --git a/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt b/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt index 85c7be0eb3..f48a38ac0c 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt @@ -3,7 +3,8 @@ package net.corda.node.utilities import com.google.common.util.concurrent.SettableFuture import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.internal.tee -import net.corda.node.services.config.DatabaseConfig +import net.corda.node.internal.configureDatabase +import net.corda.nodeapi.internal.persistence.* import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.rigorousMock import org.assertj.core.api.Assertions.assertThat 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 27318e4598..97729cadf0 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 @@ -15,9 +15,9 @@ import net.corda.finance.contracts.FixOf import net.corda.finance.contracts.asset.CASH import net.corda.finance.contracts.asset.Cash import net.corda.irs.flows.RatesFixFlow -import net.corda.node.services.config.DatabaseConfig -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.configureDatabase +import net.corda.node.internal.configureDatabase +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.* import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNodeParameters diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt index b76d47efa3..09f1e7918a 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt @@ -18,7 +18,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.node.services.api.StartedNodeServices import net.corda.node.services.config.CertChainPolicyConfig -import net.corda.node.services.config.DatabaseConfig +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.User diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt index 11fc77758d..427179a1ef 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt @@ -2,14 +2,14 @@ package net.corda.testing.node import net.corda.core.crypto.CompositeKey import net.corda.core.identity.CordaX500Name +import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.ThreadBox +import net.corda.core.internal.concurrent.openFuture import net.corda.core.messaging.AllPossibleRecipients import net.corda.core.messaging.MessageRecipientGroup import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.SingleMessageRecipient -import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.concurrent.openFuture import net.corda.core.node.services.PartyInfo import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SingletonSerializeAsToken @@ -17,7 +17,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.trace import net.corda.node.services.messaging.* import net.corda.node.utilities.AffinityExecutor -import net.corda.node.utilities.CordaPersistence +import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.node.InMemoryMessagingNetwork.InMemoryMessaging import org.apache.activemq.artemis.utils.ReusableLatch import org.slf4j.LoggerFactory diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt index e70a9d4237..058e504962 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt @@ -8,7 +8,7 @@ import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.services.network.PersistentNetworkMapCache -import net.corda.node.utilities.CordaPersistence +import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.getTestPartyAndCertificate import rx.Observable import rx.subjects.PublishSubject 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 5db9f55572..d2452cab8a 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 @@ -36,7 +36,7 @@ import net.corda.node.services.transactions.BFTSMaRt import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor -import net.corda.node.utilities.CordaPersistence +import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.DUMMY_NOTARY import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties 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 30b6735913..5dada032a6 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 @@ -18,21 +18,22 @@ import net.corda.core.transactions.SignedTransaction import net.corda.node.VersionInfo import net.corda.node.internal.StateLoaderImpl import net.corda.node.internal.cordapp.CordappLoader +import net.corda.node.services.api.SchemaService import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage import net.corda.node.services.api.VaultServiceInternal import net.corda.node.services.api.WritableTransactionStorage -import net.corda.node.services.config.DatabaseConfig import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.services.keys.freshCertificate import net.corda.node.services.keys.getSigner -import net.corda.node.services.persistence.HibernateConfiguration import net.corda.node.services.persistence.InMemoryStateMachineRecordedTransactionMappingStorage import net.corda.node.services.schema.HibernateObserver import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.services.vault.NodeVaultService -import net.corda.node.utilities.CordaPersistence -import net.corda.node.utilities.configureDatabase +import net.corda.node.internal.configureDatabase +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 org.bouncycastle.operator.ContentSigner import rx.Observable @@ -78,6 +79,7 @@ open class MockServices( } private fun makeTestIdentityService() = InMemoryIdentityService(MOCK_IDENTITIES, trustRoot = DEV_TRUST_ROOT) + /** * Makes database and mock services appropriate for unit tests. * @param keys a list of [KeyPair] instances to be used by [MockServices]. Defaults to [MEGA_CORP_KEY] @@ -88,8 +90,9 @@ open class MockServices( @JvmStatic fun makeTestDatabaseAndMockServices(keys: List = listOf(MEGA_CORP_KEY), identityService: IdentityService = makeTestIdentityService(), - cordappPackages: List = emptyList()): Pair - = makeTestDatabaseAndMockServices(keys, identityService, cordappPackages, MEGA_CORP.name) + cordappPackages: List = emptyList()): Pair { + return makeTestDatabaseAndMockServices(keys, identityService, cordappPackages, MEGA_CORP.name) + } /** * Makes database and mock services appropriate for unit tests. @@ -105,11 +108,12 @@ open class MockServices( initialIdentityName: CordaX500Name): Pair { val cordappLoader = CordappLoader.createWithTestPackages(cordappPackages) val dataSourceProps = makeTestDataSourceProperties() - val database = configureDatabase(dataSourceProps, DatabaseConfig(), identityService, NodeSchemaService(cordappLoader)) + val schemaService = NodeSchemaService(cordappLoader.cordappSchemas) + val database = configureDatabase(dataSourceProps, DatabaseConfig(), identityService, schemaService) val mockService = database.transaction { object : MockServices(cordappLoader, initialIdentityName = initialIdentityName, keys = *(keys.toTypedArray())) { override val identityService get() = identityService - override val vaultService: VaultServiceInternal = makeVaultService(database.hibernateConfig) + override val vaultService: VaultServiceInternal = makeVaultService(database.hibernateConfig, schemaService) override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable) { for (stx in txs) { @@ -161,13 +165,13 @@ open class MockServices( override val cordappProvider: CordappProvider get() = mockCordappProvider lateinit var hibernatePersister: HibernateObserver - fun makeVaultService(hibernateConfig: HibernateConfiguration): VaultServiceInternal { + fun makeVaultService(hibernateConfig: HibernateConfiguration, schemaService: SchemaService): VaultServiceInternal { val vaultService = NodeVaultService(Clock.systemUTC(), keyManagementService, stateLoader, hibernateConfig) - hibernatePersister = HibernateObserver.install(vaultService.rawUpdates, hibernateConfig) + hibernatePersister = HibernateObserver.install(vaultService.rawUpdates, hibernateConfig, schemaService) return vaultService } - val cordappServices = MutableClassToInstanceMap.create() + val cordappServices: MutableClassToInstanceMap = MutableClassToInstanceMap.create() override fun cordaService(type: Class): T { require(type.isAnnotationPresent(CordaService::class.java)) { "${type.name} is not a Corda service" } return cordappServices.getInstance(type) ?: throw IllegalArgumentException("Corda service ${type.name} does not exist") From 3c31fdf31d3610d9d46238fce090d5f565bb0d9d Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Wed, 29 Nov 2017 17:42:39 +0000 Subject: [PATCH 17/27] CORDA-806 Remove initialiseSerialization from rpcDriver (#2084) and fix a leak or two --- .../net/corda/client/rpc/RPCStabilityTests.kt | 19 +++++-- .../net/corda/client/rpc/AbstractRPCTest.kt | 6 ++ .../corda/client/rpc/RPCConcurrencyTests.kt | 55 ++++++++++--------- .../net/corda/client/rpc/RPCFailureTests.kt | 6 ++ .../net/corda/core/internal/InternalUtils.kt | 9 +++ .../core/flows/ContractUpgradeFlowTest.kt | 2 +- .../corda/core/internal/ToggleFieldTest.kt | 6 +- .../concurrent/CordaFutureImplTest.kt | 7 +-- .../node/services/messaging/RPCServer.kt | 2 + .../net/corda/testing/internal/RPCDriver.kt | 3 +- .../corda/testing/SerializationTestHelpers.kt | 33 +++++++++-- .../testing/internal/TestThreadFactory.kt | 15 +++++ 12 files changed, 117 insertions(+), 46 deletions(-) create mode 100644 testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestThreadFactory.kt 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 8d6885f0b3..811d77a46e 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 @@ -12,11 +12,14 @@ import net.corda.core.serialization.serialize 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.driver.poll import net.corda.testing.internal.* import org.apache.activemq.artemis.api.core.SimpleString +import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue +import org.junit.Rule import org.junit.Test import rx.Observable import rx.subjects.PublishSubject @@ -26,6 +29,14 @@ import java.util.concurrent.* import java.util.concurrent.atomic.AtomicInteger class RPCStabilityTests { + @Rule + @JvmField + val testSerialization = SerializationEnvironmentRule(true) + private val pool = Executors.newFixedThreadPool(10, testThreadFactory()) + @After + fun shutdown() { + pool.shutdown() + } object DummyOps : RPCOps { override val protocolVersion = 0 @@ -197,9 +208,9 @@ class RPCStabilityTests { val proxy = startRpcClient(server.get().broker.hostAndPort!!).get() // Leak many observables val N = 200 - (1..N).toList().parallelStream().forEach { - proxy.leakObservable() - } + (1..N).map { + pool.fork { proxy.leakObservable(); Unit } + }.transpose().getOrThrow() // In a loop force GC and check whether the server is notified while (true) { System.gc() @@ -231,7 +242,7 @@ class RPCStabilityTests { assertEquals("pong", client.ping()) serverFollower.shutdown() startRpcServer(ops = ops, customPort = serverPort).getOrThrow() - val pingFuture = ForkJoinPool.commonPool().fork(client::ping) + val pingFuture = pool.fork(client::ping) assertEquals("pong", pingFuture.getOrThrow(10.seconds)) clientFollower.shutdown() // Driver would do this after the new server, causing hang. } 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 62981daeef..0c552f8128 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 @@ -6,14 +6,20 @@ import net.corda.core.internal.concurrent.map import net.corda.core.messaging.RPCOps import net.corda.node.services.messaging.RPCServerConfiguration import net.corda.nodeapi.User +import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.internal.RPCDriverExposedDSLInterface import net.corda.testing.internal.rpcTestUser import net.corda.testing.internal.startInVmRpcClient import net.corda.testing.internal.startRpcClient import org.apache.activemq.artemis.api.core.client.ClientSession +import org.junit.Rule import org.junit.runners.Parameterized open class AbstractRPCTest { + @Rule + @JvmField + val testSerialization = SerializationEnvironmentRule(true) + enum class RPCTestMode { InVm, Netty 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 9bc4ea24ea..7ba003f910 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 @@ -5,19 +5,22 @@ import net.corda.core.messaging.RPCOps import net.corda.core.utilities.millis import net.corda.core.crypto.random63BitValue import net.corda.core.internal.concurrent.fork +import net.corda.core.internal.concurrent.transpose import net.corda.core.serialization.CordaSerializable +import net.corda.core.utilities.getOrThrow import net.corda.node.services.messaging.RPCServerConfiguration import net.corda.testing.internal.RPCDriverExposedDSLInterface import net.corda.testing.internal.rpcDriver +import net.corda.testing.internal.testThreadFactory +import org.apache.activemq.artemis.utils.collections.ConcurrentHashSet +import org.junit.After import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized import rx.Observable import rx.subjects.UnicastSubject import java.util.* -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.CountDownLatch -import java.util.concurrent.ForkJoinPool +import java.util.concurrent.* @RunWith(Parameterized::class) class RPCConcurrencyTests : AbstractRPCTest() { @@ -36,7 +39,7 @@ class RPCConcurrencyTests : AbstractRPCTest() { fun getParallelObservableTree(depth: Int, branchingFactor: Int): ObservableRose } - class TestOpsImpl : TestOps { + class TestOpsImpl(private val pool: Executor) : TestOps { private val latches = ConcurrentHashMap() override val protocolVersion = 0 @@ -68,24 +71,22 @@ class RPCConcurrencyTests : AbstractRPCTest() { val branches = if (depth == 0) { Observable.empty>() } else { - val publish = UnicastSubject.create>() - ForkJoinPool.commonPool().fork { - (1..branchingFactor).toList().parallelStream().forEach { - publish.onNext(getParallelObservableTree(depth - 1, branchingFactor)) + UnicastSubject.create>().also { publish -> + (1..branchingFactor).map { + pool.fork { publish.onNext(getParallelObservableTree(depth - 1, branchingFactor)) } + }.transpose().then { + it.getOrThrow() + publish.onCompleted() } - publish.onCompleted() } - publish } return ObservableRose(depth, branches) } } - private lateinit var testOpsImpl: TestOpsImpl private fun RPCDriverExposedDSLInterface.testProxy(): TestProxy { - testOpsImpl = TestOpsImpl() return testProxy( - testOpsImpl, + TestOpsImpl(pool), clientConfiguration = RPCClientConfiguration.default.copy( reapInterval = 100.millis, cacheConcurrencyLevel = 16 @@ -96,6 +97,12 @@ class RPCConcurrencyTests : AbstractRPCTest() { ) } + private val pool = Executors.newFixedThreadPool(10, testThreadFactory()) + @After + fun shutdown() { + pool.shutdown() + } + @Test fun `call multiple RPCs in parallel`() { rpcDriver { @@ -103,19 +110,17 @@ class RPCConcurrencyTests : AbstractRPCTest() { val numberOfBlockedCalls = 2 val numberOfDownsRequired = 100 val id = proxy.ops.newLatch(numberOfDownsRequired) - val done = CountDownLatch(numberOfBlockedCalls) // Start a couple of blocking RPC calls - (1..numberOfBlockedCalls).forEach { - ForkJoinPool.commonPool().fork { + val done = (1..numberOfBlockedCalls).map { + pool.fork { proxy.ops.waitLatch(id) - done.countDown() } - } + }.transpose() // Down the latch that the others are waiting for concurrently - (1..numberOfDownsRequired).toList().parallelStream().forEach { - proxy.ops.downLatch(id) - } - done.await() + (1..numberOfDownsRequired).map { + pool.fork { proxy.ops.downLatch(id) } + }.transpose().getOrThrow() + done.getOrThrow() } } @@ -146,7 +151,7 @@ class RPCConcurrencyTests : AbstractRPCTest() { fun ObservableRose.subscribeToAll() { remainingLatch.countDown() this.branches.subscribe { tree -> - (tree.value + 1..treeDepth - 1).forEach { + (tree.value + 1 until treeDepth).forEach { require(it in depthsSeen) { "Got ${tree.value} before $it" } } depthsSeen.add(tree.value) @@ -165,11 +170,11 @@ class RPCConcurrencyTests : AbstractRPCTest() { val treeDepth = 2 val treeBranchingFactor = 10 val remainingLatch = CountDownLatch((intPower(treeBranchingFactor, treeDepth + 1) - 1) / (treeBranchingFactor - 1)) - val depthsSeen = Collections.synchronizedSet(HashSet()) + val depthsSeen = ConcurrentHashSet() fun ObservableRose.subscribeToAll() { remainingLatch.countDown() branches.subscribe { tree -> - (tree.value + 1..treeDepth - 1).forEach { + (tree.value + 1 until treeDepth).forEach { require(it in depthsSeen) { "Got ${tree.value} before $it" } } depthsSeen.add(tree.value) 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 c328aac77f..5894520661 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 @@ -5,12 +5,18 @@ import net.corda.core.concurrent.CordaFuture 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 org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.Rule import org.junit.Test class RPCFailureTests { + @Rule + @JvmField + val testSerialization = SerializationEnvironmentRule(true) + class Unserializable interface Ops : RPCOps { fun getUnserializable(): Unserializable diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index 63582c164b..33306a984a 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -31,6 +31,8 @@ import java.time.Duration import java.time.temporal.Temporal import java.util.* import java.util.Spliterator.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.TimeUnit import java.util.stream.IntStream import java.util.stream.Stream import java.util.stream.StreamSupport @@ -307,3 +309,10 @@ fun TransactionBuilder.toLedgerTransaction(services: ServiceHub, serializationCo val KClass<*>.packageName: String get() = java.`package`.name fun URL.openHttpConnection(): HttpURLConnection = openConnection() as HttpURLConnection +/** Analogous to [Thread.join]. */ +fun ExecutorService.join() { + shutdown() // Do not change to shutdownNow, tests use this method to assert the executor has no more tasks. + while (!awaitTermination(1, TimeUnit.SECONDS)) { + // Try forever. Do not give up, tests use this method to assert the executor has no more tasks. + } +} 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 ba0cd17038..3287e4f260 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -133,7 +133,7 @@ class ContractUpgradeFlowTest { @Test fun `2 parties contract upgrade using RPC`() { - rpcDriver(initialiseSerialization = false) { + rpcDriver { // Create dummy contract. val twoPartyDummyContract = DummyContract.generateInitial(0, notary, alice.ref(1), bob.ref(1)) val signedByA = aliceNode.services.signInitialTransaction(twoPartyDummyContract) diff --git a/core/src/test/kotlin/net/corda/core/internal/ToggleFieldTest.kt b/core/src/test/kotlin/net/corda/core/internal/ToggleFieldTest.kt index dd60591af3..40a750f785 100644 --- a/core/src/test/kotlin/net/corda/core/internal/ToggleFieldTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/ToggleFieldTest.kt @@ -14,7 +14,6 @@ import org.junit.runners.model.Statement import org.slf4j.Logger import java.util.concurrent.ExecutorService import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit import kotlin.test.assertEquals import kotlin.test.assertNull @@ -23,10 +22,7 @@ private fun withSingleThreadExecutor(callable: ExecutorService.() -> T) = Ex fork {}.getOrThrow() // Start the thread. callable() } finally { - shutdown() - while (!awaitTermination(1, TimeUnit.SECONDS)) { - // Do nothing. - } + join() } } diff --git a/core/src/test/kotlin/net/corda/core/internal/concurrent/CordaFutureImplTest.kt b/core/src/test/kotlin/net/corda/core/internal/concurrent/CordaFutureImplTest.kt index 6ca2319239..0b52c89491 100644 --- a/core/src/test/kotlin/net/corda/core/internal/concurrent/CordaFutureImplTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/concurrent/CordaFutureImplTest.kt @@ -2,13 +2,13 @@ package net.corda.core.internal.concurrent import com.nhaarman.mockito_kotlin.* import net.corda.core.concurrent.CordaFuture +import net.corda.core.internal.join import net.corda.core.utilities.getOrThrow import net.corda.testing.rigorousMock import org.assertj.core.api.Assertions import org.junit.Test import org.slf4j.Logger import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -108,10 +108,7 @@ class CordaFutureTest { val throwable = Exception("Boom") val executor = Executors.newSingleThreadExecutor() executor.fork { throw throwable }.andForget(log) - executor.shutdown() - while (!executor.awaitTermination(1, TimeUnit.SECONDS)) { - // Do nothing. - } + executor.join() verify(log).error(any(), same(throwable)) } diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt index 2f2716dd76..54526a76be 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt @@ -20,6 +20,7 @@ import net.corda.core.context.Trace.InvocationId import net.corda.core.identity.CordaX500Name import net.corda.core.internal.LazyStickyPool import net.corda.core.internal.LifeCycle +import net.corda.core.internal.join import net.corda.core.messaging.RPCOps import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationDefaults.RPC_SERVER_CONTEXT @@ -207,6 +208,7 @@ class RPCServer( } fun close() { + observationSendExecutor?.join() reaperScheduledFuture?.cancel(false) rpcExecutor?.shutdownNow() reaperExecutor?.shutdownNow() 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/internal/RPCDriver.kt index 3146a2c821..5956c09c32 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/RPCDriver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/RPCDriver.kt @@ -230,7 +230,6 @@ fun rpcDriver( debugPortAllocation: PortAllocation = globalDebugPortAllocation, systemProperties: Map = emptyMap(), useTestClock: Boolean = false, - initialiseSerialization: Boolean = true, startNodesInProcess: Boolean = false, waitForNodesToFinish: Boolean = false, extraCordappPackagesToScan: List = emptyList(), @@ -254,7 +253,7 @@ fun rpcDriver( ), coerce = { it }, dsl = dsl, - initialiseSerialization = initialiseSerialization + initialiseSerialization = false ) private class SingleUserSecurityManager(val rpcUser: User) : ActiveMQSecurityManager3 { diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt index c1a28a4f17..17f9a83295 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/SerializationTestHelpers.kt @@ -1,26 +1,50 @@ package net.corda.testing -import com.nhaarman.mockito_kotlin.doNothing -import com.nhaarman.mockito_kotlin.whenever +import com.nhaarman.mockito_kotlin.* import net.corda.client.rpc.internal.KryoClientSerializationScheme +import net.corda.core.internal.staticField import net.corda.core.serialization.internal.* import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.nodeapi.internal.serialization.* import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme import net.corda.testing.common.internal.asContextEnv +import net.corda.testing.internal.testThreadFactory +import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnector import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +private val inVMExecutors = ConcurrentHashMap() /** @param inheritable whether new threads inherit the environment, use sparingly. */ class SerializationEnvironmentRule(private val inheritable: Boolean = false) : TestRule { + companion object { + init { + // Can't turn it off, and it creates threads that do serialization, so hack it: + InVMConnector::class.staticField("threadPoolExecutor").value = rigorousMock().also { + doAnswer { + inVMExecutors.computeIfAbsent(effectiveSerializationEnv) { + Executors.newCachedThreadPool(testThreadFactory(true)) // Close enough to what InVMConnector makes normally. + }.execute(it.arguments[0] as Runnable) + }.whenever(it).execute(any()) + } + } + } + lateinit var env: SerializationEnvironment override fun apply(base: Statement, description: Description): Statement { env = createTestSerializationEnv(description.toString()) return object : Statement() { - override fun evaluate() = env.asContextEnv(inheritable) { - base.evaluate() + override fun evaluate() { + try { + env.asContextEnv(inheritable) { base.evaluate() } + } finally { + inVMExecutors.remove(env) + } } } } @@ -59,6 +83,7 @@ fun setGlobalSerialization(armed: Boolean): GlobalSerializationEnvironment { object : GlobalSerializationEnvironment, SerializationEnvironment by createTestSerializationEnv("") { override fun unset() { _globalSerializationEnv.set(null) + inVMExecutors.remove(this) } }.also { _globalSerializationEnv.set(it) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestThreadFactory.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestThreadFactory.kt new file mode 100644 index 0000000000..0d25111ca4 --- /dev/null +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestThreadFactory.kt @@ -0,0 +1,15 @@ +package net.corda.testing.internal + +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ThreadFactory +import java.util.concurrent.atomic.AtomicInteger + +private val familyToNextPoolNumber = ConcurrentHashMap() +fun Any.testThreadFactory(useEnclosingClassName: Boolean = false): ThreadFactory { + val poolFamily = javaClass.let { (if (useEnclosingClassName) it.enclosingClass else it).simpleName } + val poolNumber = familyToNextPoolNumber.computeIfAbsent(poolFamily) { AtomicInteger(1) }.getAndIncrement() + val nextThreadNumber = AtomicInteger(1) + return ThreadFactory { task -> + Thread(task, "$poolFamily-$poolNumber-${nextThreadNumber.getAndIncrement()}") + } +} From 0db7dce985329e161b9b5e225688c4e446fd2998 Mon Sep 17 00:00:00 2001 From: igor nitto Date: Thu, 30 Nov 2017 15:06:53 +0000 Subject: [PATCH 18/27] =?UTF-8?q?Dynamic=20registration=20of=20RPC=20users?= =?UTF-8?q?=20security=20roles=20in=20ArtemisMessagingS=E2=80=A6=20(#2140)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dynamic registration of RPC users security roles in ArtemisMessagingServer [ENT-1000] --- .../messaging/ArtemisMessagingServer.kt | 73 ++++++++++++++++--- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index 5fa25f3d86..b87ac5f582 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -43,7 +43,9 @@ import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration import org.apache.activemq.artemis.core.remoting.impl.netty.* import org.apache.activemq.artemis.core.security.Role import org.apache.activemq.artemis.core.server.ActiveMQServer +import org.apache.activemq.artemis.core.server.SecuritySettingPlugin import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl +import org.apache.activemq.artemis.core.settings.HierarchicalRepository import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy import org.apache.activemq.artemis.core.settings.impl.AddressSettings import org.apache.activemq.artemis.spi.core.remoting.* @@ -139,8 +141,8 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, // Artemis IO errors @Throws(IOException::class, KeyStoreException::class) private fun configureAndStartServer() { - val artemisConfig = createArtemisConfig() - val securityManager = createArtemisSecurityManager() + val (artemisConfig, securityPlugin) = createArtemisConfig() + val securityManager = createArtemisSecurityManager(securityPlugin) activeMQServer = ActiveMQServerImpl(artemisConfig, securityManager).apply { // Throw any exceptions which are detected during startup registerActivationFailureListener { exception -> throw exception } @@ -156,7 +158,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, } } - private fun createArtemisConfig(): Configuration = ConfigurationImpl().apply { + private fun createArtemisConfig() = ConfigurationImpl().apply { val artemisDir = config.baseDirectory / "artemis" bindingsDirectory = (artemisDir / "bindings").toString() journalDirectory = (artemisDir / "journal").toString() @@ -208,8 +210,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, addressFullMessagePolicy = AddressFullMessagePolicy.FAIL } ) - configureAddressSecurity() - } + }.configureAddressSecurity() private fun queueConfig(name: String, address: String = name, filter: String? = null, durable: Boolean): CoreQueueConfiguration { return CoreQueueConfiguration().apply { @@ -227,7 +228,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, * 3. RPC users. These are only given sufficient access to perform RPC with us. * 4. Verifiers. These are given read access to the verification request queue and write access to the response queue. */ - private fun ConfigurationImpl.configureAddressSecurity() { + private fun ConfigurationImpl.configureAddressSecurity() : Pair { val nodeInternalRole = Role(NODE_ROLE, true, true, true, true, true, true, true, true) securityRoles["$INTERNAL_PREFIX#"] = setOf(nodeInternalRole) // Do not add any other roles here as it's only for the node securityRoles[P2P_QUEUE] = setOf(nodeInternalRole, restrictedRole(PEER_ROLE, send = true)) @@ -236,13 +237,22 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, securityRoles["${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$NODE_USER.#"] = setOf(nodeInternalRole) // Each RPC user must have its own role and its own queue. This prevents users accessing each other's queues // and stealing RPC responses. - for ((username) in userService.users) { - securityRoles["${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username.#"] = setOf( - nodeInternalRole, - restrictedRole("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username", consume = true, createNonDurableQueue = true, deleteNonDurableQueue = true)) + val rolesAdderOnLogin = RolesAdderOnLogin { username -> + Pair( + "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username.#", + setOf( + nodeInternalRole, + restrictedRole( + "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username", + consume = true, + createNonDurableQueue = true, + deleteNonDurableQueue = true))) } + securitySettingPlugins.add(rolesAdderOnLogin) securityRoles[VerifierApi.VERIFICATION_REQUESTS_QUEUE_NAME] = setOf(nodeInternalRole, restrictedRole(VERIFIER_ROLE, consume = true)) securityRoles["${VerifierApi.VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX}.#"] = setOf(nodeInternalRole, restrictedRole(VERIFIER_ROLE, send = true)) + val onLoginListener = { username: String -> rolesAdderOnLogin.onLogin(username) } + return Pair(this, onLoginListener) } private fun restrictedRole(name: String, send: Boolean = false, consume: Boolean = false, createDurableQueue: Boolean = false, @@ -253,7 +263,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, } @Throws(IOException::class, KeyStoreException::class) - private fun createArtemisSecurityManager(): ActiveMQJAASSecurityManager { + private fun createArtemisSecurityManager(loginListener: LoginListener): ActiveMQJAASSecurityManager { val keyStore = loadKeyStore(config.sslKeystore, config.keyStorePassword) val trustStore = loadKeyStore(config.trustStoreFile, config.trustStorePassword) @@ -270,6 +280,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration, // Override to make it work with our login module override fun getAppConfigurationEntry(name: String): Array { val options = mapOf( + LoginListener::javaClass.name to loginListener, RPCUserService::class.java.name to userService, NodeLoginModule.CERT_CHAIN_CHECKS_OPTION_NAME to certChecks) return arrayOf(AppConfigurationEntry(name, REQUIRED, options)) @@ -546,6 +557,7 @@ class NodeLoginModule : LoginModule { private lateinit var subject: Subject private lateinit var callbackHandler: CallbackHandler private lateinit var userService: RPCUserService + private lateinit var loginListener: LoginListener private lateinit var peerCertCheck: CertificateChainCheckPolicy.Check private lateinit var nodeCertCheck: CertificateChainCheckPolicy.Check private lateinit var verifierCertCheck: CertificateChainCheckPolicy.Check @@ -555,6 +567,7 @@ class NodeLoginModule : LoginModule { this.subject = subject this.callbackHandler = callbackHandler userService = options[RPCUserService::class.java.name] as RPCUserService + loginListener = options[LoginListener::javaClass.name] as LoginListener val certChainChecks: Map = uncheckedCast(options[CERT_CHAIN_CHECKS_OPTION_NAME]) peerCertCheck = certChainChecks[PEER_ROLE]!! nodeCertCheck = certChainChecks[NODE_ROLE]!! @@ -622,6 +635,7 @@ class NodeLoginModule : LoginModule { // TODO Retrieve client IP address to include in exception message throw FailedLoginException("Password for user $username does not match") } + loginListener(username) principals += RolePrincipal(RPC_ROLE) // This enables the RPC client to send requests principals += RolePrincipal("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username") // This enables the RPC client to receive responses return username @@ -676,3 +690,40 @@ class NodeLoginModule : LoginModule { loginSucceeded = false } } + +typealias LoginListener = (String) -> Unit +typealias RolesRepository = HierarchicalRepository> + +/** + * Helper class to dynamically assign security roles to RPC users + * on their authentication. This object is plugged into the server + * as [SecuritySettingPlugin]. It responds to authentication events + * from [NodeLoginModule] by adding the address -> roles association + * generated by the given [source], unless already done before. + */ +private class RolesAdderOnLogin(val source: (String) -> Pair>) + : SecuritySettingPlugin { + + // Artemis internal container storing roles association + private lateinit var repository: RolesRepository + + fun onLogin(username: String) { + val (address, roles) = source(username) + val entry = repository.getMatch(address) + if (entry == null || entry.isEmpty()) { + repository.addMatch(address, roles.toMutableSet()) + } + } + + // Initializer called by the Artemis framework + override fun setSecurityRepository(repository: RolesRepository) { + this.repository = repository + } + + // Part of SecuritySettingPlugin interface which is no-op in this case + override fun stop() = this + + override fun init(options: MutableMap?) = this + + override fun getSecurityRoles() = null +} From d1f3ec11d5cc96c35008e652e20d2924db3f9376 Mon Sep 17 00:00:00 2001 From: jamescarlyle Date: Mon, 13 Nov 2017 20:32:21 +0000 Subject: [PATCH 19/27] Initial commit of list of contributors to Corda --- CONTRIBUTORS.md | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 CONTRIBUTORS.md diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000000..22e9078227 --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,75 @@ +# List of Contributors + +We'd like to thank the following people for contributing ideas to Corda, +either during architecture review sessions of the R3 Architecture Working Group, +or in design reviews since Corda has been open-sourced. Please forgive any omissions, +and use a pull request if you wish to see changes to this list. + +* Anthony Coates (Deutsche Bank) +* Anton Semenov (Commerzbank) +* Antonio Cerrato (SEB) +* Anthony Woolley (Société Générale) +* Arnaud Stevens (Natixis) +* Arijit Das (Northern Trust) +* Arun Battu (BNY Mellon) +* Barry Childe (HSBC) +* Barry Flower (Westpac) +* Benoit Lafontaine (OCTO) +* Berit Bourgonje (ING) +* Bob Crozier (AIA) +* Chaitanya Jadhav (HSBC) +* Christian Kaufmann (Credit Suisse) +* Christopher Saunders (Credit Suisse) +* Christopher Swanson (US Bank) +* Daniel Roig (SEB) +* Farzad Pezeshkpour (RBS) +* Frederic Dalibard (Natixis) +* Garrett Macey (Wells Fargo) +* George Marcel Smetana (Bradesco) +* Giulio Katis (Westpac) +* Giuseppe Cardone (Intesa Sanpaolo) +* Guy Hochstetler (IBM) +* Ian Cusden (UBS) +* Igor Panov (CIBC) +* Jared Harwayne-Gidansky (BNY Mellon) +* Johan Hörmark (SEB) +* Johann Palychata (BNP Paribas) +* Jose Luu (Natixis) +* Justin Chapman (Northern Trust) +* Kai-Michael Schramm (Credit Suisse) +* Khaild Ahmed (Northern Trust) +* Klaus Apolinario (Bradesco) +* Koen Vingerhoets (KBC) +* Lars Stage Thomsen (Danske Bank) +* Lee Braine (Barclays) +* Lucas Salmen (Itau) +* Mark Lauer (Westpac) +* Mark Raynes (Thomson Reuters) +* Mark Simpson (RBS) +* Mark Tiggas (Wells Fargo) +* Massimo Morini (Banca IMI) +* Matthijs van den Bos (ING) +* Mike Reichelt (US Bank) +* Mustafa Ozturk (Natixis) +* Nick Skinner (Northern Trust) +* Nuam Athaweth (MUFG) +* Pekka Kaipio (OP Financial) +* Piotr Piskorski (Nordea) +* Rex Maudsley (Société Générale) +* Rhett Brewer (Goldman Sachs) +* Roberto Karpinski (Bradesco) +* Robin Green (CIBC) +* Ross Burnett (Macquarie) +* Sajindra Jayasena (Deutsche Bank) +* Saket Sharma (BNY Mellon) +* Sam Chadwick (Thomson Reuters) +* Sasmit Sahu (Credit Suisse) +* Scott James (Credit Suisse) +* Simon Taylor (Barclays) +* Sofus Mortensen (Digital Asset Holdings) +* Stephen Lane-Smith (BMO) +* Thomas O'Donnell (Macquarie) +* Tim Swanson (R3) +* Timothy Smith (Credit Suisse) +* Wei Wu Zhang (Commonwealth Bank of Australia) +* Zabrina Smith (Northern Trust) \ No newline at end of file From b18bf94d423d8f58bcabbad8e6525af1b579f657 Mon Sep 17 00:00:00 2001 From: jamescarlyle Date: Mon, 27 Nov 2017 11:48:37 +0000 Subject: [PATCH 20/27] Initial commit of list of contributors to Corda --- CONTRIBUTORS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 22e9078227..d1df0204df 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -3,7 +3,7 @@ We'd like to thank the following people for contributing ideas to Corda, either during architecture review sessions of the R3 Architecture Working Group, or in design reviews since Corda has been open-sourced. Please forgive any omissions, -and use a pull request if you wish to see changes to this list. +and create a pull request, or email , if you wish to see changes to this list. * Anthony Coates (Deutsche Bank) * Anton Semenov (Commerzbank) From 6a3435f07c9df2121908fa85debe626577ccc91a Mon Sep 17 00:00:00 2001 From: jamescarlyle Date: Mon, 27 Nov 2017 12:24:12 +0000 Subject: [PATCH 21/27] Added R3 contributors to the overall attribution list as well --- CONTRIBUTORS.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index d1df0204df..5503b8c20d 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -5,6 +5,10 @@ either during architecture review sessions of the R3 Architecture Working Group, or in design reviews since Corda has been open-sourced. Please forgive any omissions, and create a pull request, or email , if you wish to see changes to this list. +* Alberto Arri (R3) +* Andras Slemmer (R3) +* Andrius Dagys (R3) +* Andrzej Cichocki (R3) * Anthony Coates (Deutsche Bank) * Anton Semenov (Commerzbank) * Antonio Cerrato (SEB) @@ -14,62 +18,105 @@ and create a pull request, or email , if you wish to see changes t * Arun Battu (BNY Mellon) * Barry Childe (HSBC) * Barry Flower (Westpac) +* Benjamin Abineri (R3) * Benoit Lafontaine (OCTO) * Berit Bourgonje (ING) * Bob Crozier (AIA) +* Bogdan Paunescu (R3) +* Cais Manai (R3) +* Carl Worral (BCS) * Chaitanya Jadhav (HSBC) +* Chris Akers (R3) +* Chris Burlinchon (R3) +* Chris Rankin (R3) * Christian Kaufmann (Credit Suisse) +* Christian Sailer (R3) * Christopher Saunders (Credit Suisse) * Christopher Swanson (US Bank) +* Clinton Alexander (R3) * Daniel Roig (SEB) +* David Lee (BCS) * Farzad Pezeshkpour (RBS) * Frederic Dalibard (Natixis) * Garrett Macey (Wells Fargo) +* Gavin Thomas (R3) * George Marcel Smetana (Bradesco) * Giulio Katis (Westpac) * Giuseppe Cardone (Intesa Sanpaolo) * Guy Hochstetler (IBM) * Ian Cusden (UBS) +* Igor Nitto (R3) * Igor Panov (CIBC) +* James Brown (R3) +* James Carlyle (R3) * Jared Harwayne-Gidansky (BNY Mellon) +* Joel Dudley (R3) * Johan Hörmark (SEB) * Johann Palychata (BNP Paribas) +* Jonathan Sartin (R3) +* Jose Coll (R3) * Jose Luu (Natixis) +* Josh Lindl (BCS) * Justin Chapman (Northern Trust) * Kai-Michael Schramm (Credit Suisse) +* Kasia Streich (R3) +* Kat Baker (R3) * Khaild Ahmed (Northern Trust) * Klaus Apolinario (Bradesco) * Koen Vingerhoets (KBC) +* Kostas Chalkias (R3) * Lars Stage Thomsen (Danske Bank) * Lee Braine (Barclays) * Lucas Salmen (Itau) +* Maksymillian Pawlak (R3) * Mark Lauer (Westpac) +* Mark Oldfield (R3) * Mark Raynes (Thomson Reuters) * Mark Simpson (RBS) * Mark Tiggas (Wells Fargo) * Massimo Morini (Banca IMI) +* Mat Rizzo (R3) +* Matt Britton (BCS) +* Matthew Nesbit (R3) * Matthijs van den Bos (ING) +* Michal Kit (R3) +* Michelle Sollecito (R3) +* Mike Hearn (R3) * Mike Reichelt (US Bank) * Mustafa Ozturk (Natixis) * Nick Skinner (Northern Trust) +* Nigel King (R3) * Nuam Athaweth (MUFG) +* Patrick Kuo (R3) * Pekka Kaipio (OP Financial) * Piotr Piskorski (Nordea) +* Przemyslaw Bak (R3) * Rex Maudsley (Société Générale) +* Richard Green (R3) +* Rick Parker (R3) * Rhett Brewer (Goldman Sachs) * Roberto Karpinski (Bradesco) * Robin Green (CIBC) +* Roger Willis (R3) * Ross Burnett (Macquarie) +* Ross Nicoll (R3) * Sajindra Jayasena (Deutsche Bank) * Saket Sharma (BNY Mellon) * Sam Chadwick (Thomson Reuters) * Sasmit Sahu (Credit Suisse) * Scott James (Credit Suisse) +* Shams Asari (R3) * Simon Taylor (Barclays) * Sofus Mortensen (Digital Asset Holdings) +* Szymon Sztuka (R3) * Stephen Lane-Smith (BMO) * Thomas O'Donnell (Macquarie) +* Thomas Schroeter (R3) +* Tudor Malene (R3) * Tim Swanson (R3) * Timothy Smith (Credit Suisse) +* Tommy Lillehagen (R3) +* Viktor Kolomeyko (R3) +* Wawrzek Niewodniczanski (R3) * Wei Wu Zhang (Commonwealth Bank of Australia) * Zabrina Smith (Northern Trust) \ No newline at end of file From b84fdd3ffc47004d04c96af4bcbfe1bf8e271751 Mon Sep 17 00:00:00 2001 From: jamescarlyle Date: Mon, 27 Nov 2017 15:26:44 +0000 Subject: [PATCH 22/27] Some additional contributors recognised --- CONTRIBUTORS.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 5503b8c20d..2d31baad9b 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -2,8 +2,10 @@ We'd like to thank the following people for contributing ideas to Corda, either during architecture review sessions of the R3 Architecture Working Group, -or in design reviews since Corda has been open-sourced. Please forgive any omissions, -and create a pull request, or email , if you wish to see changes to this list. +or in design reviews since Corda has been open-sourced. Some people have moved to +a different organisation since their contribution. Please forgive any omissions, and +create a pull request, or email , if you wish to see +changes to this list. * Alberto Arri (R3) * Andras Slemmer (R3) @@ -16,6 +18,7 @@ and create a pull request, or email , if you wish to see changes t * Arnaud Stevens (Natixis) * Arijit Das (Northern Trust) * Arun Battu (BNY Mellon) +* Austin Moothart (R3) * Barry Childe (HSBC) * Barry Flower (Westpac) * Benjamin Abineri (R3) @@ -24,7 +27,7 @@ and create a pull request, or email , if you wish to see changes t * Bob Crozier (AIA) * Bogdan Paunescu (R3) * Cais Manai (R3) -* Carl Worral (BCS) +* Carl Worrall (BCS) * Chaitanya Jadhav (HSBC) * Chris Akers (R3) * Chris Burlinchon (R3) @@ -33,8 +36,12 @@ and create a pull request, or email , if you wish to see changes t * Christian Sailer (R3) * Christopher Saunders (Credit Suisse) * Christopher Swanson (US Bank) +* Clark Thompson (R3) +* Clay Ratliff (Thoughtworks) +* Clemens Wan (R3) * Clinton Alexander (R3) * Daniel Roig (SEB) +* Dave Hudson (R3) * David Lee (BCS) * Farzad Pezeshkpour (RBS) * Frederic Dalibard (Natixis) @@ -45,8 +52,10 @@ and create a pull request, or email , if you wish to see changes t * Giuseppe Cardone (Intesa Sanpaolo) * Guy Hochstetler (IBM) * Ian Cusden (UBS) +* Ian Grigg (R3) * Igor Nitto (R3) * Igor Panov (CIBC) +* Ivan Schasny (R3) * James Brown (R3) * James Carlyle (R3) * Jared Harwayne-Gidansky (BNY Mellon) @@ -59,6 +68,7 @@ and create a pull request, or email , if you wish to see changes t * Josh Lindl (BCS) * Justin Chapman (Northern Trust) * Kai-Michael Schramm (Credit Suisse) +* Karel Hajek (Barclays Capital) * Kasia Streich (R3) * Kat Baker (R3) * Khaild Ahmed (Northern Trust) @@ -69,6 +79,7 @@ and create a pull request, or email , if you wish to see changes t * Lee Braine (Barclays) * Lucas Salmen (Itau) * Maksymillian Pawlak (R3) +* Marek Scocovsky (ABSA) * Mark Lauer (Westpac) * Mark Oldfield (R3) * Mark Raynes (Thomson Reuters) @@ -80,6 +91,7 @@ and create a pull request, or email , if you wish to see changes t * Matthew Nesbit (R3) * Matthijs van den Bos (ING) * Michal Kit (R3) +* Micheal Hinstridge (Thoughtworks) * Michelle Sollecito (R3) * Mike Hearn (R3) * Mike Reichelt (US Bank) @@ -87,6 +99,7 @@ and create a pull request, or email , if you wish to see changes t * Nick Skinner (Northern Trust) * Nigel King (R3) * Nuam Athaweth (MUFG) +* Oscar Zibordi de Paiva (Bradesco) * Patrick Kuo (R3) * Pekka Kaipio (OP Financial) * Piotr Piskorski (Nordea) @@ -97,6 +110,7 @@ and create a pull request, or email , if you wish to see changes t * Rhett Brewer (Goldman Sachs) * Roberto Karpinski (Bradesco) * Robin Green (CIBC) +* Rodrigo Bueno (Itau) * Roger Willis (R3) * Ross Burnett (Macquarie) * Ross Nicoll (R3) @@ -112,6 +126,7 @@ and create a pull request, or email , if you wish to see changes t * Stephen Lane-Smith (BMO) * Thomas O'Donnell (Macquarie) * Thomas Schroeter (R3) +* Tom Menner (R3) * Tudor Malene (R3) * Tim Swanson (R3) * Timothy Smith (Credit Suisse) From 5a6f2a19b3511956c75846235f189f14ba7b3d4c Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Thu, 30 Nov 2017 16:17:18 +0000 Subject: [PATCH 23/27] [CORDA-824]: fix resource leak in Cash selection (#2155) [CORDA-824]: fix resource leak in Cash selection and some example class --- .../kotlin/net/corda/docs/CustomVaultQuery.kt | 22 ++++--- .../cash/selection/AbstractCashSelection.kt | 63 ++++++++++--------- .../cash/selection/CashSelectionH2Impl.kt | 34 +++++----- .../cash/selection/CashSelectionMySQLImpl.kt | 2 +- .../selection/CashSelectionPostgreSQLImpl.kt | 52 +++++++-------- 5 files changed, 94 insertions(+), 79 deletions(-) diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt index 506dc8bc02..ac592eaa8f 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt @@ -24,6 +24,7 @@ object CustomVaultQuery { private companion object { private val log = contextLogger() } + fun rebalanceCurrencyReserves(): List> { val nativeQuery = """ select @@ -44,16 +45,18 @@ object CustomVaultQuery { """ log.info("SQL to execute: $nativeQuery") val session = services.jdbcSession() - val prepStatement = session.prepareStatement(nativeQuery) - val rs = prepStatement.executeQuery() - val topUpLimits: MutableList> = mutableListOf() - while (rs.next()) { - val currencyStr = rs.getString(1) - val amount = rs.getLong(2) - log.info("$currencyStr : $amount") - topUpLimits.add(Amount(amount, Currency.getInstance(currencyStr))) + return session.prepareStatement(nativeQuery).use { prepStatement -> + prepStatement.executeQuery().use { rs -> + val topUpLimits: MutableList> = mutableListOf() + while (rs.next()) { + val currencyStr = rs.getString(1) + val amount = rs.getLong(2) + log.info("$currencyStr : $amount") + topUpLimits.add(Amount(amount, Currency.getInstance(currencyStr))) + } + topUpLimits + } } - return topUpLimits } } } @@ -69,6 +72,7 @@ object TopupIssuerFlow { data class TopupRequest(val issueToParty: Party, val issuerPartyRef: OpaqueBytes, val notaryParty: Party) + @InitiatingFlow @StartableByRPC class TopupIssuanceRequester(val issueToParty: Party, diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/AbstractCashSelection.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/AbstractCashSelection.kt index b5d2113f8d..eff93f6d3f 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/AbstractCashSelection.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/AbstractCashSelection.kt @@ -69,13 +69,14 @@ abstract class AbstractCashSelection { * with this notary are included. * @param onlyFromIssuerParties Optional issuer parties to match against. * @param withIssuerRefs Optional issuer references to match against. - * @return JDBC ResultSet with the matching states that were found. If sufficient funds were found these will be locked, + * @param withResultSet Function that contains the business logic. The JDBC ResultSet with the matching states that were found. If sufficient funds were found these will be locked, * otherwise what is available is returned unlocked for informational purposes. + * @return The result of the withResultSet function */ abstract fun executeQuery(connection: Connection, amount: Amount, lockId: UUID, notary: Party?, - onlyFromIssuerParties: Set, withIssuerRefs: Set) : ResultSet + onlyFromIssuerParties: Set, withIssuerRefs: Set, withResultSet: (ResultSet) -> Boolean): Boolean - override abstract fun toString() : String + override abstract fun toString(): String /** * Query to gather Cash states that are available and retry if they are temporarily unavailable. @@ -124,34 +125,40 @@ abstract class AbstractCashSelection { try { // we select spendable states irrespective of lock but prioritised by unlocked ones (Eg. null) // the softLockReserve update will detect whether we try to lock states locked by others - val rs = executeQuery(connection, amount, lockId, notary, onlyFromIssuerParties, withIssuerRefs) - stateAndRefs.clear() + return executeQuery(connection, amount, lockId, notary, onlyFromIssuerParties, withIssuerRefs) { rs -> + stateAndRefs.clear() - var totalPennies = 0L - val stateRefs = mutableSetOf() - while (rs.next()) { - val txHash = SecureHash.parse(rs.getString(1)) - val index = rs.getInt(2) - val pennies = rs.getLong(3) - totalPennies = rs.getLong(4) - val rowLockId = rs.getString(5) - stateRefs.add(StateRef(txHash, index)) - log.trace { "ROW: $rowLockId ($lockId): ${StateRef(txHash, index)} : $pennies ($totalPennies)" } + var totalPennies = 0L + val stateRefs = mutableSetOf() + while (rs.next()) { + val txHash = SecureHash.parse(rs.getString(1)) + val index = rs.getInt(2) + val pennies = rs.getLong(3) + totalPennies = rs.getLong(4) + val rowLockId = rs.getString(5) + stateRefs.add(StateRef(txHash, index)) + log.trace { "ROW: $rowLockId ($lockId): ${StateRef(txHash, index)} : $pennies ($totalPennies)" } + } + + if (stateRefs.isNotEmpty()) { + // TODO: future implementation to retrieve contract states from a Vault BLOB store + stateAndRefs.addAll(services.loadStates(stateRefs) as Collection>) + } + + val success = stateAndRefs.isNotEmpty() && totalPennies >= amount.quantity + if (success) { + // we should have a minimum number of states to satisfy our selection `amount` criteria + log.trace("Coin selection for $amount retrieved ${stateAndRefs.count()} states totalling $totalPennies pennies: $stateAndRefs") + + // With the current single threaded state machine available states are guaranteed to lock. + // TODO However, we will have to revisit these methods in the future multi-threaded. + services.vaultService.softLockReserve(lockId, (stateAndRefs.map { it.ref }).toNonEmptySet()) + } else { + log.trace("Coin selection requested $amount but retrieved $totalPennies pennies with state refs: ${stateAndRefs.map { it.ref }}") + } + success } - if (stateRefs.isNotEmpty()) - // TODO: future implementation to retrieve contract states from a Vault BLOB store - stateAndRefs.addAll(services.loadStates(stateRefs) as Collection>) - if (stateAndRefs.isNotEmpty() && totalPennies >= amount.quantity) { - // we should have a minimum number of states to satisfy our selection `amount` criteria - log.trace("Coin selection for $amount retrieved ${stateAndRefs.count()} states totalling $totalPennies pennies: $stateAndRefs") - - // With the current single threaded state machine available states are guaranteed to lock. - // TODO However, we will have to revisit these methods in the future multi-threaded. - services.vaultService.softLockReserve(lockId, (stateAndRefs.map { it.ref }).toNonEmptySet()) - return true - } - log.trace("Coin selection requested $amount but retrieved $totalPennies pennies with state refs: ${stateAndRefs.map { it.ref }}") // retry as more states may become available } catch (e: SQLException) { log.error("""Failed retrieving unconsumed states for: amount [$amount], onlyFromIssuerParties [$onlyFromIssuerParties], notary [$notary], lockId [$lockId] diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2Impl.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2Impl.kt index 440047f19d..3c21246dee 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2Impl.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2Impl.kt @@ -30,9 +30,8 @@ class CashSelectionH2Impl : AbstractCashSelection() { // 2) H2 uses session variables to perform this accumulator function: // http://www.h2database.com/html/functions.html#set // 3) H2 does not support JOIN's in FOR UPDATE (hence we are forced to execute 2 queries) - override fun executeQuery(connection: Connection, amount: Amount, lockId: UUID, notary: Party?, - onlyFromIssuerParties: Set, withIssuerRefs: Set) : ResultSet { - connection.createStatement().execute("CALL SET(@t, CAST(0 AS BIGINT));") + override fun executeQuery(connection: Connection, amount: Amount, lockId: UUID, notary: Party?, onlyFromIssuerParties: Set, withIssuerRefs: Set, withResultSet: (ResultSet) -> Boolean): Boolean { + connection.createStatement().use { it.execute("CALL SET(@t, CAST(0 AS BIGINT));") } val selectJoin = """ SELECT vs.transaction_id, vs.output_index, ccs.pennies, SET(@t, ifnull(@t,0)+ccs.pennies) total_pennies, vs.lock_id @@ -50,19 +49,22 @@ class CashSelectionH2Impl : AbstractCashSelection() { " AND ccs.issuer_ref IN (?)" else "") // Use prepared statement for protection against SQL Injection (http://www.h2database.com/html/advanced.html#sql_injection) - val psSelectJoin = connection.prepareStatement(selectJoin) - var pIndex = 0 - psSelectJoin.setString(++pIndex, amount.token.currencyCode) - psSelectJoin.setLong(++pIndex, amount.quantity) - psSelectJoin.setString(++pIndex, lockId.toString()) - if (notary != null) - psSelectJoin.setString(++pIndex, notary.name.toString()) - if (onlyFromIssuerParties.isNotEmpty()) - psSelectJoin.setObject(++pIndex, onlyFromIssuerParties.map { it.owningKey.toStringShort() as Any}.toTypedArray() ) - if (withIssuerRefs.isNotEmpty()) - psSelectJoin.setObject(++pIndex, withIssuerRefs.map { it.bytes as Any }.toTypedArray()) - log.debug { psSelectJoin.toString() } + connection.prepareStatement(selectJoin).use { psSelectJoin -> + var pIndex = 0 + psSelectJoin.setString(++pIndex, amount.token.currencyCode) + psSelectJoin.setLong(++pIndex, amount.quantity) + psSelectJoin.setString(++pIndex, lockId.toString()) + if (notary != null) + psSelectJoin.setString(++pIndex, notary.name.toString()) + if (onlyFromIssuerParties.isNotEmpty()) + psSelectJoin.setObject(++pIndex, onlyFromIssuerParties.map { it.owningKey.toStringShort() as Any }.toTypedArray()) + if (withIssuerRefs.isNotEmpty()) + psSelectJoin.setObject(++pIndex, withIssuerRefs.map { it.bytes as Any }.toTypedArray()) + log.debug { psSelectJoin.toString() } - return psSelectJoin.executeQuery() + psSelectJoin.executeQuery().use { rs -> + return withResultSet(rs) + } + } } } \ No newline at end of file diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionMySQLImpl.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionMySQLImpl.kt index 853ba23d07..e197e5a962 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionMySQLImpl.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionMySQLImpl.kt @@ -19,7 +19,7 @@ class CashSelectionMySQLImpl : AbstractCashSelection() { return metadata.driverName == JDBC_DRIVER_NAME } - override fun executeQuery(statement: Connection, amount: Amount, lockId: UUID, notary: Party?, issuerKeysStr: Set, issuerRefsStr: Set): ResultSet { + override fun executeQuery(statement: Connection, amount: Amount, lockId: UUID, notary: Party?, issuerKeysStr: Set, issuerRefsStr: Set, withResultSet: (ResultSet) -> Boolean): Boolean { TODO("MySQL cash selection not implemented") } diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionPostgreSQLImpl.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionPostgreSQLImpl.kt index f96ef6f441..47e59386c3 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionPostgreSQLImpl.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionPostgreSQLImpl.kt @@ -27,8 +27,7 @@ class CashSelectionPostgreSQLImpl : AbstractCashSelection() { // 2) The window function accumulated column (`total`) does not include the current row (starts from 0) and cannot // appear in the WHERE clause, hence restricting row selection and adjusting the returned total in the outer query. // 3) Currently (version 9.6), FOR UPDATE cannot be specified with window functions - override fun executeQuery(connection: Connection, amount: Amount, lockId: UUID, notary: Party?, - onlyFromIssuerParties: Set, withIssuerRefs: Set) : ResultSet { + override fun executeQuery(connection: Connection, amount: Amount, lockId: UUID, notary: Party?, onlyFromIssuerParties: Set, withIssuerRefs: Set, withResultSet: (ResultSet) -> Boolean): Boolean { val selectJoin = """SELECT nested.transaction_id, nested.output_index, nested.pennies, nested.total+nested.pennies as total_pennies, nested.lock_id FROM @@ -51,29 +50,32 @@ class CashSelectionPostgreSQLImpl : AbstractCashSelection() { nested WHERE nested.total < ? """ - val statement = connection.prepareStatement(selectJoin) - statement.setString(1, amount.token.toString()) - statement.setString(2, lockId.toString()) - var paramOffset = 0 - if (notary != null) { - statement.setString(3, notary.name.toString()) - paramOffset += 1 - } - if (onlyFromIssuerParties.isNotEmpty()) { - val issuerKeys = connection.createArrayOf("VARCHAR", onlyFromIssuerParties.map - { it.owningKey.toBase58String() }.toTypedArray()) - statement.setArray(3 + paramOffset, issuerKeys) - paramOffset += 1 - } - if (withIssuerRefs.isNotEmpty()) { - val issuerRefs = connection.createArrayOf("BYTEA", withIssuerRefs.map - { it.bytes }.toTypedArray()) - statement.setArray(3 + paramOffset, issuerRefs) - paramOffset += 1 - } - statement.setLong(3 + paramOffset, amount.quantity) - log.debug { statement.toString() } + connection.prepareStatement(selectJoin).use { statement -> + statement.setString(1, amount.token.toString()) + statement.setString(2, lockId.toString()) + var paramOffset = 0 + if (notary != null) { + statement.setString(3, notary.name.toString()) + paramOffset += 1 + } + if (onlyFromIssuerParties.isNotEmpty()) { + val issuerKeys = connection.createArrayOf("VARCHAR", onlyFromIssuerParties.map + { it.owningKey.toBase58String() }.toTypedArray()) + statement.setArray(3 + paramOffset, issuerKeys) + paramOffset += 1 + } + if (withIssuerRefs.isNotEmpty()) { + val issuerRefs = connection.createArrayOf("BYTEA", withIssuerRefs.map + { it.bytes }.toTypedArray()) + statement.setArray(3 + paramOffset, issuerRefs) + paramOffset += 1 + } + statement.setLong(3 + paramOffset, amount.quantity) + log.debug { statement.toString() } - return statement.executeQuery() + statement.executeQuery().use { rs -> + return withResultSet(rs) + } + } } } From a314a6a125cbef8f174393c0f7c3b74eba25f64f Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Thu, 30 Nov 2017 16:28:44 +0000 Subject: [PATCH 24/27] CORDA-654 Simplify TransactionDSL API (#2152) --- .../core/crypto/PartialMerkleTreeTest.kt | 14 +- .../TransactionEncumbranceTests.kt | 56 ++-- .../docs/tutorial/testdsl/TutorialTestDSL.kt | 72 ++--- .../corda/finance/contracts/universal/Cap.kt | 76 ++--- .../finance/contracts/universal/Caplet.kt | 40 +-- .../contracts/universal/FXFwdTimeOption.kt | 48 ++-- .../finance/contracts/universal/FXSwap.kt | 86 +++--- .../corda/finance/contracts/universal/IRS.kt | 43 ++- .../contracts/universal/RollOutTests.kt | 18 +- .../finance/contracts/universal/Swaption.kt | 8 +- .../contracts/universal/ZeroCouponBond.kt | 59 ++-- .../contracts/asset/CashTestsJava.java | 12 +- .../finance/contracts/CommercialPaperTests.kt | 40 ++- .../finance/contracts/asset/CashTests.kt | 227 +++++++-------- .../contracts/asset/ObligationTests.kt | 272 ++++++++---------- .../node/messaging/TwoPartyTradeFlowTests.kt | 25 +- .../kotlin/net/corda/irs/contract/IRSTests.kt | 102 +++---- .../main/kotlin/net/corda/testing/TestDSL.kt | 4 +- .../testing/TransactionDSLInterpreter.kt | 72 ++--- 19 files changed, 567 insertions(+), 707 deletions(-) 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 58501ad790..c0346e30fd 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt @@ -38,24 +38,20 @@ class PartialMerkleTreeTest { testLedger = ledger { unverifiedTransaction { attachments(Cash.PROGRAM_ID) - output(Cash.PROGRAM_ID, "MEGA_CORP cash") { + output(Cash.PROGRAM_ID, "MEGA_CORP cash", Cash.State( amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), - owner = MEGA_CORP - ) - } - output(Cash.PROGRAM_ID, "dummy cash 1") { + owner = MEGA_CORP)) + output(Cash.PROGRAM_ID, "dummy cash 1", Cash.State( amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1), - owner = MINI_CORP - ) - } + owner = MINI_CORP)) } transaction { attachments(Cash.PROGRAM_ID) input("MEGA_CORP cash") output(Cash.PROGRAM_ID, "MEGA_CORP cash".output().copy(owner = MINI_CORP)) - command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } + command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) timeWindow(TEST_TX_TIME) this.verifies() } 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 87015f9eb9..4cb955faa5 100644 --- a/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/TransactionEncumbranceTests.kt @@ -55,10 +55,10 @@ class TransactionEncumbranceTests { ledger { transaction { attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) - input(Cash.PROGRAM_ID) { state } - output(Cash.PROGRAM_ID, encumbrance = 1) { stateWithNewOwner } - output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } - command(MEGA_CORP.owningKey) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, state) + output(Cash.PROGRAM_ID, encumbrance = 1, contractState = stateWithNewOwner) + output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock) + command(MEGA_CORP.owningKey, Cash.Commands.Move()) verifies() } } @@ -69,16 +69,16 @@ class TransactionEncumbranceTests { ledger { unverifiedTransaction { attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) - output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock") { state } - output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } + output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", state) + output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock) } // Un-encumber the output if the time of the transaction is later than the timelock. transaction { attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) input("state encumbered by 5pm time-lock") input("5pm time-lock") - output(Cash.PROGRAM_ID) { stateWithNewOwner } - command(MEGA_CORP.owningKey) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, stateWithNewOwner) + command(MEGA_CORP.owningKey, Cash.Commands.Move()) timeWindow(FIVE_PM) verifies() } @@ -90,16 +90,16 @@ class TransactionEncumbranceTests { ledger { unverifiedTransaction { attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) - output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock") { state } - output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } + output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", state) + output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock) } // The time of the transaction is earlier than the time specified in the encumbering timelock. transaction { attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) input("state encumbered by 5pm time-lock") input("5pm time-lock") - output(Cash.PROGRAM_ID) { state } - command(MEGA_CORP.owningKey) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, state) + command(MEGA_CORP.owningKey, Cash.Commands.Move()) timeWindow(FOUR_PM) this `fails with` "the time specified in the time-lock has passed" } @@ -111,14 +111,14 @@ class TransactionEncumbranceTests { ledger { unverifiedTransaction { attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) - output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", encumbrance = 1) { state } - output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } + output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", encumbrance = 1, contractState = state) + output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock) } transaction { attachments(Cash.PROGRAM_ID) input("state encumbered by 5pm time-lock") - output(Cash.PROGRAM_ID) { stateWithNewOwner } - command(MEGA_CORP.owningKey) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, stateWithNewOwner) + command(MEGA_CORP.owningKey, Cash.Commands.Move()) timeWindow(FIVE_PM) this `fails with` "Missing required encumbrance 1 in INPUT" } @@ -130,9 +130,9 @@ class TransactionEncumbranceTests { ledger { transaction { attachments(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { state } - output(Cash.PROGRAM_ID, encumbrance = 0) { stateWithNewOwner } - command(MEGA_CORP.owningKey) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, state) + output(Cash.PROGRAM_ID, encumbrance = 0, contractState = stateWithNewOwner) + command(MEGA_CORP.owningKey, Cash.Commands.Move()) this `fails with` "Missing required encumbrance 0 in OUTPUT" } } @@ -143,10 +143,10 @@ class TransactionEncumbranceTests { ledger { transaction { attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) - input(Cash.PROGRAM_ID) { state } - output(TEST_TIMELOCK_ID, encumbrance = 2) { stateWithNewOwner } - output(TEST_TIMELOCK_ID) { timeLock } - command(MEGA_CORP.owningKey) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, state) + output(TEST_TIMELOCK_ID, encumbrance = 2, contractState = stateWithNewOwner) + output(TEST_TIMELOCK_ID, timeLock) + command(MEGA_CORP.owningKey, Cash.Commands.Move()) this `fails with` "Missing required encumbrance 2 in OUTPUT" } } @@ -157,16 +157,16 @@ class TransactionEncumbranceTests { ledger { unverifiedTransaction { attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) - output(Cash.PROGRAM_ID, "state encumbered by some other state", encumbrance = 1) { state } - output(Cash.PROGRAM_ID, "some other state") { state } - output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } + output(Cash.PROGRAM_ID, "state encumbered by some other state", encumbrance = 1, contractState = state) + output(Cash.PROGRAM_ID, "some other state", state) + output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock) } transaction { attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) input("state encumbered by some other state") input("5pm time-lock") - output(Cash.PROGRAM_ID) { stateWithNewOwner } - command(MEGA_CORP.owningKey) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, stateWithNewOwner) + command(MEGA_CORP.owningKey, Cash.Commands.Move()) timeWindow(FIVE_PM) this `fails with` "Missing required encumbrance 1 in INPUT" } 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 00ff7f02c6..cfde0084a1 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 @@ -33,7 +33,7 @@ class CommercialPaperTest { ledger { transaction { attachments(CP_PROGRAM_ID) - input(CP_PROGRAM_ID) { inState } + input(CP_PROGRAM_ID, inState) verifies() } } @@ -46,8 +46,8 @@ class CommercialPaperTest { val inState = getPaper() ledger { transaction { - input(CP_PROGRAM_ID) { inState } - command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } + input(CP_PROGRAM_ID, inState) + command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move()) attachments(CP_PROGRAM_ID) verifies() } @@ -61,8 +61,8 @@ class CommercialPaperTest { val inState = getPaper() ledger { transaction { - input(CP_PROGRAM_ID) { inState } - command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } + input(CP_PROGRAM_ID, inState) + command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move()) attachments(CP_PROGRAM_ID) `fails with`("the state is propagated") } @@ -76,11 +76,11 @@ class CommercialPaperTest { val inState = getPaper() ledger { transaction { - input(CP_PROGRAM_ID) { inState } - command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } + input(CP_PROGRAM_ID, inState) + command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move()) attachments(CP_PROGRAM_ID) `fails with`("the state is propagated") - output(CP_PROGRAM_ID, "alice's paper") { inState.withOwner(ALICE) } + output(CP_PROGRAM_ID, "alice's paper", inState.withOwner(ALICE)) verifies() } } @@ -92,15 +92,15 @@ class CommercialPaperTest { fun `simple issuance with tweak`() { ledger { transaction { - output(CP_PROGRAM_ID, "paper") { getPaper() } // Some CP is issued onto the ledger by MegaCorp. + output(CP_PROGRAM_ID, "paper", getPaper()) // Some CP is issued onto the ledger by MegaCorp. attachments(CP_PROGRAM_ID) tweak { // The wrong pubkey. - command(BIG_CORP_PUBKEY) { CommercialPaper.Commands.Issue() } + command(BIG_CORP_PUBKEY, CommercialPaper.Commands.Issue()) timeWindow(TEST_TX_TIME) `fails with`("output states are issued by a command signer") } - command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() } + command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Issue()) timeWindow(TEST_TX_TIME) verifies() } @@ -112,15 +112,15 @@ class CommercialPaperTest { @Test fun `simple issuance with tweak and top level transaction`() { transaction { - output(CP_PROGRAM_ID, "paper") { getPaper() } // Some CP is issued onto the ledger by MegaCorp. + output(CP_PROGRAM_ID, "paper", getPaper()) // Some CP is issued onto the ledger by MegaCorp. attachments(CP_PROGRAM_ID) tweak { // The wrong pubkey. - command(BIG_CORP_PUBKEY) { CommercialPaper.Commands.Issue() } + command(BIG_CORP_PUBKEY, CommercialPaper.Commands.Issue()) timeWindow(TEST_TX_TIME) `fails with`("output states are issued by a command signer") } - command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() } + command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Issue()) timeWindow(TEST_TX_TIME) verifies() } @@ -140,8 +140,8 @@ class CommercialPaperTest { // Some CP is issued onto the ledger by MegaCorp. transaction("Issuance") { - output(CP_PROGRAM_ID, "paper") { getPaper() } - command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() } + output(CP_PROGRAM_ID, "paper", getPaper()) + command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Issue()) attachments(CP_PROGRAM_ID) timeWindow(TEST_TX_TIME) verifies() @@ -151,10 +151,10 @@ class CommercialPaperTest { transaction("Trade") { input("paper") input("alice's $900") - output(Cash.PROGRAM_ID, "borrowed $900") { 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP } - output(CP_PROGRAM_ID, "alice's paper") { "paper".output().withOwner(ALICE) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } - command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } + output(Cash.PROGRAM_ID, "borrowed $900", 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP) + output(CP_PROGRAM_ID, "alice's paper", "paper".output().withOwner(ALICE)) + command(ALICE_PUBKEY, Cash.Commands.Move()) + command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move()) verifies() } } @@ -173,8 +173,8 @@ class CommercialPaperTest { // Some CP is issued onto the ledger by MegaCorp. transaction("Issuance") { - output(CP_PROGRAM_ID, "paper") { getPaper() } - command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() } + output(CP_PROGRAM_ID, "paper", getPaper()) + command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Issue()) attachments(CP_PROGRAM_ID) timeWindow(TEST_TX_TIME) verifies() @@ -183,18 +183,18 @@ class CommercialPaperTest { transaction("Trade") { input("paper") input("alice's $900") - output(Cash.PROGRAM_ID, "borrowed $900") { 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP } - output(CP_PROGRAM_ID, "alice's paper") { "paper".output().withOwner(ALICE) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } - command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } + output(Cash.PROGRAM_ID, "borrowed $900", 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP) + output(CP_PROGRAM_ID, "alice's paper", "paper".output().withOwner(ALICE)) + command(ALICE_PUBKEY, Cash.Commands.Move()) + command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move()) verifies() } transaction { input("paper") // We moved a paper to another pubkey. - output(CP_PROGRAM_ID, "bob's paper") { "paper".output().withOwner(BOB) } - command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } + output(CP_PROGRAM_ID, "bob's paper", "paper".output().withOwner(BOB)) + command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move()) verifies() } @@ -215,8 +215,8 @@ class CommercialPaperTest { // Some CP is issued onto the ledger by MegaCorp. transaction("Issuance") { - output(CP_PROGRAM_ID, "paper") { getPaper() } - command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() } + output(CP_PROGRAM_ID, "paper", getPaper()) + command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Issue()) attachments(CP_PROGRAM_ID) timeWindow(TEST_TX_TIME) verifies() @@ -225,10 +225,10 @@ class CommercialPaperTest { transaction("Trade") { input("paper") input("alice's $900") - output(Cash.PROGRAM_ID, "borrowed $900") { 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP } - output(CP_PROGRAM_ID, "alice's paper") { "paper".output().withOwner(ALICE) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } - command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } + output(Cash.PROGRAM_ID, "borrowed $900", 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP) + output(CP_PROGRAM_ID, "alice's paper", "paper".output().withOwner(ALICE)) + command(ALICE_PUBKEY, Cash.Commands.Move()) + command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move()) verifies() } @@ -236,8 +236,8 @@ class CommercialPaperTest { transaction { input("paper") // We moved a paper to another pubkey. - output(CP_PROGRAM_ID, "bob's paper") { "paper".output().withOwner(BOB) } - command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } + output(CP_PROGRAM_ID, "bob's paper", "paper".output().withOwner(BOB)) + command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move()) verifies() } fails() 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 be94480f2a..9d683682a6 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 @@ -172,16 +172,14 @@ class Cap { @Test fun issue() { transaction { - output(UNIVERSAL_PROGRAM_ID) { stateInitial } + output(UNIVERSAL_PROGRAM_ID, stateInitial) timeWindow(TEST_TX_TIME_1) tweak { - command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } + command(acmeCorp.owningKey, UniversalContract.Commands.Issue()) this `fails with` "the transaction is signed by all liable parties" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() } - + command(highStreetBank.owningKey, UniversalContract.Commands.Issue()) this.verifies() } } @@ -189,44 +187,38 @@ class Cap { @Test fun `first fixing`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { stateInitial } - output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } + input(UNIVERSAL_PROGRAM_ID, stateInitial) + output(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst) timeWindow(TEST_TX_TIME_1) tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } + command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name")) this `fails with` "action must be defined" } tweak { // wrong source - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("3M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("3M")), 1.0.bd)))) this `fails with` "relevant fixing must be included" } tweak { // wrong date - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("3M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("3M")), 1.0.bd)))) this `fails with` "relevant fixing must be included" } tweak { // wrong tenor - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("9M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("9M")), 1.0.bd)))) this `fails with` "relevant fixing must be included" } tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.5.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.5.bd)))) this `fails with` "output state does not reflect fix command" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd)))) this.verifies() } } @@ -234,19 +226,16 @@ class Cap { @Test fun `first execute`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } - output(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst } - output(UNIVERSAL_PROGRAM_ID) { statePaymentFirst } - + input(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst) + output(UNIVERSAL_PROGRAM_ID, stateAfterExecutionFirst) + output(UNIVERSAL_PROGRAM_ID, statePaymentFirst) timeWindow(TEST_TX_TIME_1) tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } + command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name")) this `fails with` "action must be defined" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") } - + command(highStreetBank.owningKey, UniversalContract.Commands.Action("exercise")) this.verifies() } } @@ -254,18 +243,15 @@ class Cap { @Test fun `final execute`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFinal } - output(UNIVERSAL_PROGRAM_ID) { statePaymentFinal } - + input(UNIVERSAL_PROGRAM_ID, stateAfterFixingFinal) + output(UNIVERSAL_PROGRAM_ID, statePaymentFinal) timeWindow(TEST_TX_TIME_1) tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } + command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name")) this `fails with` "action must be defined" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") } - + command(highStreetBank.owningKey, UniversalContract.Commands.Action("exercise")) this.verifies() } } @@ -273,44 +259,38 @@ class Cap { @Test fun `second fixing`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst } - output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFinal } + input(UNIVERSAL_PROGRAM_ID, stateAfterExecutionFirst) + output(UNIVERSAL_PROGRAM_ID, stateAfterFixingFinal) timeWindow(TEST_TX_TIME_1) tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } + command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name")) this `fails with` "action must be defined" } tweak { // wrong source - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("3M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("3M")), 1.0.bd)))) this `fails with` "relevant fixing must be included" } tweak { // wrong date - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01").plusYears(1), Tenor("3M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01").plusYears(1), Tenor("3M")), 1.0.bd)))) this `fails with` "relevant fixing must be included" } tweak { // wrong tenor - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("9M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("9M")), 1.0.bd)))) this `fails with` "relevant fixing must be included" } tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("3M")), 1.5.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("3M")), 1.5.bd)))) this `fails with` "output state does not reflect fix command" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("3M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("3M")), 1.0.bd)))) this.verifies() } } 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 1bf39cfacd..7436161cd4 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 @@ -55,16 +55,14 @@ class Caplet { @Test fun issue() { transaction { - output(UNIVERSAL_PROGRAM_ID) { stateStart } + output(UNIVERSAL_PROGRAM_ID, stateStart) timeWindow(TEST_TX_TIME_1) tweak { - command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } + command(acmeCorp.owningKey, UniversalContract.Commands.Issue()) this `fails with` "the transaction is signed by all liable parties" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() } - + command(highStreetBank.owningKey, UniversalContract.Commands.Issue()) this.verifies() } } @@ -72,17 +70,15 @@ class Caplet { @Test fun `execute`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { stateFixed } - output(UNIVERSAL_PROGRAM_ID) { stateFinal } + input(UNIVERSAL_PROGRAM_ID, stateFixed) + output(UNIVERSAL_PROGRAM_ID, stateFinal) timeWindow(TEST_TX_TIME_1) tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } + command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name")) this `fails with` "action must be defined" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") } - + command(highStreetBank.owningKey, UniversalContract.Commands.Action("exercise")) this.verifies() } } @@ -90,44 +86,38 @@ class Caplet { @Test fun `fixing`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { stateStart } - output(UNIVERSAL_PROGRAM_ID) { stateFixed } + input(UNIVERSAL_PROGRAM_ID, stateStart) + output(UNIVERSAL_PROGRAM_ID, stateFixed) timeWindow(TEST_TX_TIME_1) tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } + command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name")) this `fails with` "action must be defined" } tweak { // wrong source - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("6M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("6M")), 1.0.bd)))) this `fails with` "relevant fixing must be included" } tweak { // wrong date - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("6M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("6M")), 1.0.bd)))) this `fails with` "relevant fixing must be included" } tweak { // wrong tenor - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd)))) this `fails with` "relevant fixing must be included" } tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("6M")), 1.5.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("6M")), 1.5.bd)))) this `fails with` "output state does not reflect fix command" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("6M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("6M")), 1.0.bd)))) this.verifies() } } 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 603eceb0f7..1d05fe061a 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 @@ -52,20 +52,18 @@ class FXFwdTimeOption { @Test fun `issue - signature`() { transaction { - output(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID, inState) timeWindow(TEST_TX_TIME_1) tweak { - command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } + command(acmeCorp.owningKey, UniversalContract.Commands.Issue()) this `fails with` "the transaction is signed by all liable parties" } tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() } + command(highStreetBank.owningKey, UniversalContract.Commands.Issue()) this `fails with` "the transaction is signed by all liable parties" } - - command(highStreetBank.owningKey, acmeCorp.owningKey) { UniversalContract.Commands.Issue() } - + command(listOf(highStreetBank.owningKey, acmeCorp.owningKey), UniversalContract.Commands.Issue()) this.verifies() } } @@ -73,31 +71,28 @@ class FXFwdTimeOption { @Test fun `maturity, bank exercise`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { inState } - output(UNIVERSAL_PROGRAM_ID) { outState1 } - output(UNIVERSAL_PROGRAM_ID) { outState2 } - + input(UNIVERSAL_PROGRAM_ID, inState) + output(UNIVERSAL_PROGRAM_ID, outState1) + output(UNIVERSAL_PROGRAM_ID, outState2) timeWindow(TEST_TX_TIME_AFTER_MATURITY) tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } + command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name")) this `fails with` "action must be defined" } tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") } + command(highStreetBank.owningKey, UniversalContract.Commands.Action("exercise")) this `fails with` "condition must be met" } tweak { - command(acmeCorp.owningKey) { UniversalContract.Commands.Action("exercise") } + command(acmeCorp.owningKey, UniversalContract.Commands.Action("exercise")) this `fails with` "condition must be met" } tweak { - command(acmeCorp.owningKey) { UniversalContract.Commands.Action("expire") } + command(acmeCorp.owningKey, UniversalContract.Commands.Action("expire")) this `fails with` "condition must be met" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("expire") } - + command(highStreetBank.owningKey, UniversalContract.Commands.Action("expire")) this.verifies() } } @@ -105,31 +100,28 @@ class FXFwdTimeOption { @Test fun `maturity, corp exercise`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { inState } - output(UNIVERSAL_PROGRAM_ID) { outState1 } - output(UNIVERSAL_PROGRAM_ID) { outState2 } - + input(UNIVERSAL_PROGRAM_ID, inState) + output(UNIVERSAL_PROGRAM_ID, outState1) + output(UNIVERSAL_PROGRAM_ID, outState2) timeWindow(TEST_TX_TIME_BEFORE_MATURITY) tweak { - command(acmeCorp.owningKey) { UniversalContract.Commands.Action("some undefined name") } + command(acmeCorp.owningKey, UniversalContract.Commands.Action("some undefined name")) this `fails with` "action must be defined" } tweak { - command(acmeCorp.owningKey) { UniversalContract.Commands.Action("expire") } + command(acmeCorp.owningKey, UniversalContract.Commands.Action("expire")) this `fails with` "condition must be met" } tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("expire") } + command(highStreetBank.owningKey, UniversalContract.Commands.Action("expire")) this `fails with` "condition must be met" } tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") } + command(highStreetBank.owningKey, UniversalContract.Commands.Action("exercise")) this `fails with` "condition must be met" } - - command(acmeCorp.owningKey) { UniversalContract.Commands.Action("exercise") } - + command(acmeCorp.owningKey, UniversalContract.Commands.Action("exercise")) this.verifies() } } 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 dc0d27a258..fc91215046 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 @@ -44,20 +44,18 @@ class FXSwap { fun `issue - signature`() { transaction { - output(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID, inState) timeWindow(TEST_TX_TIME_1) tweak { - command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } + command(acmeCorp.owningKey, UniversalContract.Commands.Issue()) this `fails with` "the transaction is signed by all liable parties" } tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() } + command(highStreetBank.owningKey, UniversalContract.Commands.Issue()) this `fails with` "the transaction is signed by all liable parties" } - - command(highStreetBank.owningKey, acmeCorp.owningKey) { UniversalContract.Commands.Issue() } - + command(listOf(highStreetBank.owningKey, acmeCorp.owningKey), UniversalContract.Commands.Issue()) this.verifies() } } @@ -65,18 +63,16 @@ class FXSwap { @Test fun `execute`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { inState } - output(UNIVERSAL_PROGRAM_ID) { outState1 } - output(UNIVERSAL_PROGRAM_ID) { outState2 } + input(UNIVERSAL_PROGRAM_ID, inState) + output(UNIVERSAL_PROGRAM_ID, outState1) + output(UNIVERSAL_PROGRAM_ID, outState2) timeWindow(TEST_TX_TIME_1) tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } + command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name")) this `fails with` "action must be defined" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("execute") } - + command(highStreetBank.owningKey, UniversalContract.Commands.Action("execute")) this.verifies() } } @@ -84,18 +80,16 @@ class FXSwap { @Test fun `execute - reversed order`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { inState } - output(UNIVERSAL_PROGRAM_ID) { outState2 } - output(UNIVERSAL_PROGRAM_ID) { outState1 } + input(UNIVERSAL_PROGRAM_ID, inState) + output(UNIVERSAL_PROGRAM_ID, outState2) + output(UNIVERSAL_PROGRAM_ID, outState1) timeWindow(TEST_TX_TIME_1) tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } + command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name")) this `fails with` "action must be defined" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("execute") } - + command(highStreetBank.owningKey, UniversalContract.Commands.Action("execute")) this.verifies() } } @@ -103,12 +97,11 @@ class FXSwap { @Test fun `execute - not authorized`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { inState } - output(UNIVERSAL_PROGRAM_ID) { outState1 } - output(UNIVERSAL_PROGRAM_ID) { outState2 } + input(UNIVERSAL_PROGRAM_ID, inState) + output(UNIVERSAL_PROGRAM_ID, outState1) + output(UNIVERSAL_PROGRAM_ID, outState2) timeWindow(TEST_TX_TIME_1) - - command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") } + command(momAndPop.owningKey, UniversalContract.Commands.Action("execute")) this `fails with` "condition must be met" } } @@ -116,12 +109,11 @@ class FXSwap { @Test fun `execute - before maturity`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { inState } - output(UNIVERSAL_PROGRAM_ID) { outState1 } - output(UNIVERSAL_PROGRAM_ID) { outState2 } + input(UNIVERSAL_PROGRAM_ID, inState) + output(UNIVERSAL_PROGRAM_ID, outState1) + output(UNIVERSAL_PROGRAM_ID, outState2) timeWindow(TEST_TX_TIME_TOO_EARLY) - - command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } + command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute")) this `fails with` "condition must be met" } } @@ -129,11 +121,10 @@ class FXSwap { @Test fun `execute - outState mismatch 1`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { inState } - output(UNIVERSAL_PROGRAM_ID) { outState1 } + input(UNIVERSAL_PROGRAM_ID, inState) + output(UNIVERSAL_PROGRAM_ID, outState1) timeWindow(TEST_TX_TIME_1) - - command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } + command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute")) this `fails with` "output state must match action result state" } } @@ -141,12 +132,11 @@ class FXSwap { @Test fun `execute - outState mismatch 2`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { inState } - output(UNIVERSAL_PROGRAM_ID) { outState1 } - output(UNIVERSAL_PROGRAM_ID) { outStateBad2 } + input(UNIVERSAL_PROGRAM_ID, inState) + output(UNIVERSAL_PROGRAM_ID, outState1) + output(UNIVERSAL_PROGRAM_ID, outStateBad2) timeWindow(TEST_TX_TIME_1) - - command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } + command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute")) this `fails with` "output states must match action result state" } } @@ -154,12 +144,11 @@ class FXSwap { @Test fun `execute - outState mismatch 3`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { inState } - output(UNIVERSAL_PROGRAM_ID) { outStateBad1 } - output(UNIVERSAL_PROGRAM_ID) { outState2 } + input(UNIVERSAL_PROGRAM_ID, inState) + output(UNIVERSAL_PROGRAM_ID, outStateBad1) + output(UNIVERSAL_PROGRAM_ID, outState2) timeWindow(TEST_TX_TIME_1) - - command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } + command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute")) this `fails with` "output states must match action result state" } } @@ -167,12 +156,11 @@ class FXSwap { @Test fun `execute - outState mismatch 4`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { inState } - output(UNIVERSAL_PROGRAM_ID) { outState1 } - output(UNIVERSAL_PROGRAM_ID) { outStateBad3 } + input(UNIVERSAL_PROGRAM_ID, inState) + output(UNIVERSAL_PROGRAM_ID, outState1) + output(UNIVERSAL_PROGRAM_ID, outStateBad3) timeWindow(TEST_TX_TIME_1) - - command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } + command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute")) this `fails with` "output states must match action result state" } } 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 c2a2653318..e2f0d4f942 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 @@ -134,16 +134,14 @@ class IRS { @Test fun issue() { transaction { - output(UNIVERSAL_PROGRAM_ID) { stateInitial } + output(UNIVERSAL_PROGRAM_ID, stateInitial) timeWindow(TEST_TX_TIME_1) tweak { - command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } + command(acmeCorp.owningKey, UniversalContract.Commands.Issue()) this `fails with` "the transaction is signed by all liable parties" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() } - + command(highStreetBank.owningKey, UniversalContract.Commands.Issue()) this.verifies() } } @@ -151,44 +149,38 @@ class IRS { @Test fun `first fixing`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { stateInitial } - output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } + input(UNIVERSAL_PROGRAM_ID, stateInitial) + output(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst) timeWindow(TEST_TX_TIME_1) tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } + command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name")) this `fails with` "action must be defined" } tweak { // wrong source - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("3M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("3M")), 1.0.bd)))) this `fails with` "relevant fixing must be included" } tweak { // wrong date - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("3M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("3M")), 1.0.bd)))) this `fails with` "relevant fixing must be included" } tweak { // wrong tenor - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("9M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("9M")), 1.0.bd)))) this `fails with` "relevant fixing must be included" } tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.5.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.5.bd)))) this `fails with` "output state does not reflect fix command" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd))) } - + command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd)))) this.verifies() } } @@ -196,19 +188,16 @@ class IRS { @Test fun `first execute`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } - output(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst } - output(UNIVERSAL_PROGRAM_ID) { statePaymentFirst } - + input(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst) + output(UNIVERSAL_PROGRAM_ID, stateAfterExecutionFirst) + output(UNIVERSAL_PROGRAM_ID, statePaymentFirst) timeWindow(TEST_TX_TIME_1) tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } + command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name")) this `fails with` "action must be defined" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("pay floating") } - + command(highStreetBank.owningKey, UniversalContract.Commands.Action("pay floating")) this.verifies() } } 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 289b58c4d0..34ba966a0c 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 @@ -145,16 +145,14 @@ class RollOutTests { @Test fun issue() { transaction { - output(UNIVERSAL_PROGRAM_ID) { stateStart } + output(UNIVERSAL_PROGRAM_ID, stateStart) timeWindow(TEST_TX_TIME_1) tweak { - command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } + command(acmeCorp.owningKey, UniversalContract.Commands.Issue()) this `fails with` "the transaction is signed by all liable parties" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() } - + command(highStreetBank.owningKey, UniversalContract.Commands.Issue()) this.verifies() } } @@ -162,18 +160,16 @@ class RollOutTests { @Test fun `execute`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { stateStart } - output(UNIVERSAL_PROGRAM_ID) { stateStep1a } - output(UNIVERSAL_PROGRAM_ID) { stateStep1b } + input(UNIVERSAL_PROGRAM_ID, stateStart) + output(UNIVERSAL_PROGRAM_ID, stateStep1a) + output(UNIVERSAL_PROGRAM_ID, stateStep1b) timeWindow(TEST_TX_TIME_1) /* tweak { command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } this `fails with` "action must be defined" }*/ - - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("transfer") } - + command(highStreetBank.owningKey, UniversalContract.Commands.Action("transfer")) this.verifies() } } 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 c1e25625e5..f80c3d5fd3 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 @@ -61,16 +61,14 @@ class Swaption { @Test fun issue() { transaction { - output(UNIVERSAL_PROGRAM_ID) { stateInitial } + output(UNIVERSAL_PROGRAM_ID, stateInitial) timeWindow(TEST_TX_TIME_1) tweak { - command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } + command(acmeCorp.owningKey, UniversalContract.Commands.Issue()) this `fails with` "the transaction is signed by all liable parties" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() } - + command(highStreetBank.owningKey, UniversalContract.Commands.Issue()) this.verifies() } } 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 0b3898176d..2554b0c58b 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 @@ -51,15 +51,12 @@ class ZeroCouponBond { @Test fun `issue - signature`() { transaction { - output(UNIVERSAL_PROGRAM_ID) { inState } - + output(UNIVERSAL_PROGRAM_ID, inState) tweak { - command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } + command(acmeCorp.owningKey, UniversalContract.Commands.Issue()) this `fails with` "the transaction is signed by all liable parties" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() } - + command(highStreetBank.owningKey, UniversalContract.Commands.Issue()) this.verifies() } } @@ -67,17 +64,15 @@ class ZeroCouponBond { @Test fun `execute`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { inState } - output(UNIVERSAL_PROGRAM_ID) { outState } + input(UNIVERSAL_PROGRAM_ID, inState) + output(UNIVERSAL_PROGRAM_ID, outState) timeWindow(TEST_TX_TIME_1) tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } + command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name")) this `fails with` "action must be defined" } - - command(highStreetBank.owningKey) { UniversalContract.Commands.Action("execute") } - + command(highStreetBank.owningKey, UniversalContract.Commands.Action("execute")) this.verifies() } } @@ -85,11 +80,10 @@ class ZeroCouponBond { @Test fun `execute - not authorized`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { inState } - output(UNIVERSAL_PROGRAM_ID) { outState } + input(UNIVERSAL_PROGRAM_ID, inState) + output(UNIVERSAL_PROGRAM_ID, outState) timeWindow(TEST_TX_TIME_1) - - command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") } + command(momAndPop.owningKey, UniversalContract.Commands.Action("execute")) this `fails with` "condition must be met" } } @@ -97,11 +91,10 @@ class ZeroCouponBond { @Test fun `execute - outState mismatch`() { transaction { - input(UNIVERSAL_PROGRAM_ID) { inState } - output(UNIVERSAL_PROGRAM_ID) { outStateWrong } + input(UNIVERSAL_PROGRAM_ID, inState) + output(UNIVERSAL_PROGRAM_ID, outStateWrong) timeWindow(TEST_TX_TIME_1) - - command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } + command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute")) this `fails with` "output state must match action result state" } } @@ -109,29 +102,23 @@ class ZeroCouponBond { @Test fun move() { transaction { - input(UNIVERSAL_PROGRAM_ID) { inState } - + input(UNIVERSAL_PROGRAM_ID, inState) tweak { - output(UNIVERSAL_PROGRAM_ID) { outStateMove } - command(acmeCorp.owningKey) { - UniversalContract.Commands.Move(acmeCorp, momAndPop) - } + output(UNIVERSAL_PROGRAM_ID, outStateMove) + command(acmeCorp.owningKey, + UniversalContract.Commands.Move(acmeCorp, momAndPop)) this `fails with` "the transaction is signed by all liable parties" } tweak { - output(UNIVERSAL_PROGRAM_ID) { inState } - command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) { - UniversalContract.Commands.Move(acmeCorp, momAndPop) - } + output(UNIVERSAL_PROGRAM_ID, inState) + command(listOf(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey), + UniversalContract.Commands.Move(acmeCorp, momAndPop)) this `fails with` "output state does not reflect move command" } - - output(UNIVERSAL_PROGRAM_ID) { outStateMove } - - command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) { - UniversalContract.Commands.Move(acmeCorp, momAndPop) - } + output(UNIVERSAL_PROGRAM_ID, outStateMove) + command(listOf(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey), + UniversalContract.Commands.Move(acmeCorp, momAndPop)) this.verifies() } } 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 0a4f3b9849..bfff1987d1 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 @@ -32,34 +32,34 @@ public class CashTestsJava { 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.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()); return tw.failsWith("the amounts balance"); }); tx.tweak(tw -> { - tw.output(Cash.PROGRAM_ID, () -> outState); + tw.output(Cash.PROGRAM_ID, outState); tw.command(getMEGA_CORP_PUBKEY(), 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.output(Cash.PROGRAM_ID, outState); tw.command(getMINI_CORP_PUBKEY(), 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); + 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.output(Cash.PROGRAM_ID, outState.issuedBy(getMINI_CORP())); tw.command(getMEGA_CORP_PUBKEY(), 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.output(Cash.PROGRAM_ID, outState); tw.command(getMEGA_CORP_PUBKEY(), 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 e4056fe36c..0684675724 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt @@ -106,8 +106,8 @@ class CommercialPaperTestsGeneric { // Some CP is issued onto the ledger by MegaCorp. transaction("Issuance") { attachments(CP_PROGRAM_ID, JavaCommercialPaper.JCP_PROGRAM_ID) - output(thisTest.getContract(), "paper") { thisTest.getPaper() } - command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } + output(thisTest.getContract(), "paper", thisTest.getPaper()) + command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY)) timeWindow(TEST_TX_TIME) this.verifies() } @@ -118,10 +118,10 @@ class CommercialPaperTestsGeneric { attachments(Cash.PROGRAM_ID, JavaCommercialPaper.JCP_PROGRAM_ID) input("paper") input("alice's $900") - output(Cash.PROGRAM_ID, "borrowed $900") { 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP } - output(thisTest.getContract(), "alice's paper") { "paper".output().withOwner(ALICE) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } - command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() } + output(Cash.PROGRAM_ID, "borrowed $900", 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP) + output(thisTest.getContract(), "alice's paper", "paper".output().withOwner(ALICE)) + command(ALICE_PUBKEY, Cash.Commands.Move()) + command(MEGA_CORP_PUBKEY, thisTest.getMoveCommand()) this.verifies() } @@ -133,13 +133,11 @@ class CommercialPaperTestsGeneric { input("some profits") fun TransactionDSL.outputs(aliceGetsBack: Amount>) { - output(Cash.PROGRAM_ID, "Alice's profit") { aliceGetsBack.STATE ownedBy ALICE } - output(Cash.PROGRAM_ID, "Change") { (someProfits - aliceGetsBack).STATE ownedBy MEGA_CORP } + output(Cash.PROGRAM_ID, "Alice's profit", aliceGetsBack.STATE ownedBy ALICE) + output(Cash.PROGRAM_ID, "Change", (someProfits - aliceGetsBack).STATE ownedBy MEGA_CORP) } - - command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } - command(ALICE_PUBKEY) { thisTest.getRedeemCommand(DUMMY_NOTARY) } - + command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) + command(ALICE_PUBKEY, thisTest.getRedeemCommand(DUMMY_NOTARY)) tweak { outputs(700.DOLLARS `issued by` issuer) timeWindow(TEST_TX_TIME + 8.days) @@ -155,7 +153,7 @@ class CommercialPaperTestsGeneric { timeWindow(TEST_TX_TIME + 8.days) tweak { - output(thisTest.getContract()) { "paper".output() } + output(thisTest.getContract(), "paper".output()) this `fails with` "must be destroyed" } @@ -169,8 +167,8 @@ class CommercialPaperTestsGeneric { transaction { attachment(CP_PROGRAM_ID) attachment(JavaCommercialPaper.JCP_PROGRAM_ID) - output(thisTest.getContract()) { thisTest.getPaper() } - command(MINI_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } + output(thisTest.getContract(), thisTest.getPaper()) + command(MINI_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY)) timeWindow(TEST_TX_TIME) this `fails with` "output states are issued by a command signer" } @@ -181,8 +179,8 @@ class CommercialPaperTestsGeneric { transaction { attachment(CP_PROGRAM_ID) attachment(JavaCommercialPaper.JCP_PROGRAM_ID) - output(thisTest.getContract()) { thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer) } - command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } + output(thisTest.getContract(), thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer)) + command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY)) timeWindow(TEST_TX_TIME) this `fails with` "output values sum to more than the inputs" } @@ -193,8 +191,8 @@ class CommercialPaperTestsGeneric { transaction { attachment(CP_PROGRAM_ID) attachment(JavaCommercialPaper.JCP_PROGRAM_ID) - output(thisTest.getContract()) { thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days) } - command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } + output(thisTest.getContract(), thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days)) + command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY)) timeWindow(TEST_TX_TIME) this `fails with` "maturity date is not in the past" } @@ -206,8 +204,8 @@ class CommercialPaperTestsGeneric { attachment(CP_PROGRAM_ID) attachment(JavaCommercialPaper.JCP_PROGRAM_ID) input(thisTest.getContract(), thisTest.getPaper()) - output(thisTest.getContract()) { thisTest.getPaper() } - command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } + output(thisTest.getContract(), thisTest.getPaper()) + command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY)) timeWindow(TEST_TX_TIME) this `fails with` "output values sum to more than the inputs" } 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 e2dd05dd2b..faa9665f63 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 @@ -111,34 +111,33 @@ class CashTests { fun trivial() { transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - + input(Cash.PROGRAM_ID, inState) tweak { - output(Cash.PROGRAM_ID) { outState.copy(amount = 2000.DOLLARS `issued by` defaultIssuer) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, outState.copy(amount = 2000.DOLLARS `issued by` defaultIssuer)) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } tweak { - output(Cash.PROGRAM_ID) { outState } - command(ALICE_PUBKEY) { DummyCommandData } + output(Cash.PROGRAM_ID, outState) + command(ALICE_PUBKEY, DummyCommandData) // Invalid command this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command" } tweak { - output(Cash.PROGRAM_ID) { outState } - command(BOB_PUBKEY) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, outState) + command(BOB_PUBKEY, Cash.Commands.Move()) this `fails with` "the owning keys are a subset of the signing keys" } tweak { - output(Cash.PROGRAM_ID) { outState } - output(Cash.PROGRAM_ID) { outState issuedBy MINI_CORP } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, outState) + output(Cash.PROGRAM_ID, outState issuedBy MINI_CORP) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "at least one cash input" } // Simple reallocation works. tweak { - output(Cash.PROGRAM_ID) { outState } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, outState) + command(ALICE_PUBKEY, Cash.Commands.Move()) this.verifies() } } @@ -149,10 +148,9 @@ class CashTests { // Check we can't "move" money into existence. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { DummyState() } - output(Cash.PROGRAM_ID) { outState } - command(MINI_CORP_PUBKEY) { Cash.Commands.Move() } - + input(Cash.PROGRAM_ID, DummyState()) + output(Cash.PROGRAM_ID, outState) + command(MINI_CORP_PUBKEY, Cash.Commands.Move()) this `fails with` "there is at least one cash input for this group" } } @@ -163,19 +161,17 @@ class CashTests { // institution is allowed to issue as much cash as they want. transaction { attachment(Cash.PROGRAM_ID) - output(Cash.PROGRAM_ID) { outState } - command(ALICE_PUBKEY) { Cash.Commands.Issue() } + output(Cash.PROGRAM_ID, outState) + command(ALICE_PUBKEY, Cash.Commands.Issue()) this `fails with` "output states are issued by a command signer" } transaction { attachment(Cash.PROGRAM_ID) - output(Cash.PROGRAM_ID) { + output(Cash.PROGRAM_ID, Cash.State( amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34), - owner = AnonymousParty(ALICE_PUBKEY) - ) - } - command(MINI_CORP_PUBKEY) { Cash.Commands.Issue() } + owner = AnonymousParty(ALICE_PUBKEY))) + command(MINI_CORP_PUBKEY, Cash.Commands.Issue()) this.verifies() } } @@ -211,18 +207,17 @@ class CashTests { // We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { issuerInState } - output(Cash.PROGRAM_ID) { inState.copy(amount = inState.amount * 2) } - + input(Cash.PROGRAM_ID, issuerInState) + output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount * 2)) // Move fails: not allowed to summon money. tweak { - command(ALICE_PUBKEY) { Cash.Commands.Move() } + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } // Issue works. tweak { - command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } + command(MEGA_CORP_PUBKEY, Cash.Commands.Issue()) this.verifies() } } @@ -230,29 +225,29 @@ class CashTests { // Can't use an issue command to lower the amount. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { inState.copy(amount = inState.amount.splitEvenly(2).first()) } - command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } + input(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount.splitEvenly(2).first())) + command(MEGA_CORP_PUBKEY, Cash.Commands.Issue()) this `fails with` "output values sum to more than the inputs" } // Can't have an issue command that doesn't actually issue money. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { inState } - command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } + input(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, inState) + command(MEGA_CORP_PUBKEY, Cash.Commands.Issue()) this `fails with` "output values sum to more than the inputs" } // Can't have any other commands if we have an issue command (because the issue command overrules them) transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { inState.copy(amount = inState.amount * 2) } - command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } + input(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount * 2)) + command(MEGA_CORP_PUBKEY, Cash.Commands.Issue()) tweak { - command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } + command(MEGA_CORP_PUBKEY, Cash.Commands.Issue()) this `fails with` "there is only a single issue command" } this.verifies() @@ -282,26 +277,26 @@ class CashTests { // Splitting value works. transaction { attachment(Cash.PROGRAM_ID) - command(ALICE_PUBKEY) { Cash.Commands.Move() } + command(ALICE_PUBKEY, Cash.Commands.Move()) tweak { - input(Cash.PROGRAM_ID) { inState } + input(Cash.PROGRAM_ID, inState) val splits4 = inState.amount.splitEvenly(4) - for (i in 0..3) output(Cash.PROGRAM_ID) { inState.copy(amount = splits4[i]) } + for (i in 0..3) output(Cash.PROGRAM_ID, inState.copy(amount = splits4[i])) this.verifies() } // Merging 4 inputs into 2 outputs works. tweak { val splits2 = inState.amount.splitEvenly(2) val splits4 = inState.amount.splitEvenly(4) - for (i in 0..3) input(Cash.PROGRAM_ID) { inState.copy(amount = splits4[i]) } - for (i in 0..1) output(Cash.PROGRAM_ID) { inState.copy(amount = splits2[i]) } + for (i in 0..3) input(Cash.PROGRAM_ID, inState.copy(amount = splits4[i])) + for (i in 0..1) output(Cash.PROGRAM_ID, inState.copy(amount = splits2[i])) this.verifies() } // Merging 2 inputs into 1 works. tweak { val splits2 = inState.amount.splitEvenly(2) - for (i in 0..1) input(Cash.PROGRAM_ID) { inState.copy(amount = splits2[i]) } - output(Cash.PROGRAM_ID) { inState } + for (i in 0..1) input(Cash.PROGRAM_ID, inState.copy(amount = splits2[i])) + output(Cash.PROGRAM_ID, inState) this.verifies() } } @@ -311,17 +306,17 @@ class CashTests { fun zeroSizedValues() { transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - input(Cash.PROGRAM_ID) { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + input(Cash.PROGRAM_ID, inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer)) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "zero sized inputs" } transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer)) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "zero sized outputs" } } @@ -331,58 +326,56 @@ class CashTests { // Can't change issuer. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { outState issuedBy MINI_CORP } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, outState issuedBy MINI_CORP) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } // Can't change deposit reference when splitting. transaction { attachment(Cash.PROGRAM_ID) val splits2 = inState.amount.splitEvenly(2) - input(Cash.PROGRAM_ID) { inState } - for (i in 0..1) output(Cash.PROGRAM_ID) { outState.copy(amount = splits2[i]).editDepositRef(i.toByte()) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + for (i in 0..1) output(Cash.PROGRAM_ID, outState.copy(amount = splits2[i]).editDepositRef(i.toByte())) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } // Can't mix currencies. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer) } - output(Cash.PROGRAM_ID) { outState.copy(amount = 200.POUNDS `issued by` defaultIssuer) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer)) + output(Cash.PROGRAM_ID, outState.copy(amount = 200.POUNDS `issued by` defaultIssuer)) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - input(Cash.PROGRAM_ID) { + input(Cash.PROGRAM_ID, inState) + input(Cash.PROGRAM_ID, inState.copy( amount = 150.POUNDS `issued by` defaultIssuer, - owner = AnonymousParty(BOB_PUBKEY) - ) - } - output(Cash.PROGRAM_ID) { outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + owner = AnonymousParty(BOB_PUBKEY))) + output(Cash.PROGRAM_ID, outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer)) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } // Can't have superfluous input states from different issuers. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - input(Cash.PROGRAM_ID) { inState issuedBy MINI_CORP } - output(Cash.PROGRAM_ID) { outState } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + input(Cash.PROGRAM_ID, inState issuedBy MINI_CORP) + output(Cash.PROGRAM_ID, outState) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } // Can't combine two different deposits at the same issuer. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - input(Cash.PROGRAM_ID) { inState.editDepositRef(3) } - output(Cash.PROGRAM_ID) { outState.copy(amount = inState.amount * 2).editDepositRef(3) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + input(Cash.PROGRAM_ID, inState.editDepositRef(3)) + output(Cash.PROGRAM_ID, outState.copy(amount = inState.amount * 2).editDepositRef(3)) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "for reference [01]" } } @@ -392,21 +385,20 @@ class CashTests { // Single input/output straightforward case. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { issuerInState } - output(Cash.PROGRAM_ID) { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } - + input(Cash.PROGRAM_ID, issuerInState) + output(Cash.PROGRAM_ID, issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer))) tweak { - command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(100.DOLLARS `issued by` defaultIssuer) } - command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } + command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(100.DOLLARS `issued by` defaultIssuer)) + command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } tweak { - command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer) } + command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer)) this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command" tweak { - command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } + command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) this.verifies() } } @@ -418,20 +410,15 @@ class CashTests { // Multi-issuer case. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { issuerInState } - input(Cash.PROGRAM_ID) { issuerInState.copy(owner = MINI_CORP) issuedBy MINI_CORP } - - output(Cash.PROGRAM_ID) { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) issuedBy MINI_CORP } - output(Cash.PROGRAM_ID) { issuerInState.copy(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } - - command(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY) { Cash.Commands.Move() } - + input(Cash.PROGRAM_ID, issuerInState) + input(Cash.PROGRAM_ID, issuerInState.copy(owner = MINI_CORP) issuedBy MINI_CORP) + output(Cash.PROGRAM_ID, issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) issuedBy MINI_CORP) + output(Cash.PROGRAM_ID, issuerInState.copy(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer))) + command(listOf(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY), Cash.Commands.Move()) this `fails with` "the amounts balance" - - command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer) } + command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer)) this `fails with` "the amounts balance" - - command(MINI_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` MINI_CORP.ref(defaultRef)) } + command(MINI_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` MINI_CORP.ref(defaultRef))) this.verifies() } } @@ -441,10 +428,10 @@ class CashTests { // Single input/output straightforward case. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { outState.copy(amount = inState.amount - (200.DOLLARS `issued by` defaultIssuer)) } - command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, outState.copy(amount = inState.amount - (200.DOLLARS `issued by` defaultIssuer))) + command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer)) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } } @@ -454,25 +441,24 @@ class CashTests { transaction { attachment(Cash.PROGRAM_ID) // Gather 2000 dollars from two different issuers. - input(Cash.PROGRAM_ID) { inState } - input(Cash.PROGRAM_ID) { inState issuedBy MINI_CORP } - command(ALICE_PUBKEY) { Cash.Commands.Move() } - + input(Cash.PROGRAM_ID, inState) + input(Cash.PROGRAM_ID, inState issuedBy MINI_CORP) + command(ALICE_PUBKEY, Cash.Commands.Move()) // Can't merge them together. tweak { - output(Cash.PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY), amount = 2000.DOLLARS `issued by` defaultIssuer) } + output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY), amount = 2000.DOLLARS `issued by` defaultIssuer)) this `fails with` "the amounts balance" } // Missing MiniCorp deposit tweak { - output(Cash.PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } - output(Cash.PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } + output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY))) + output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY))) this `fails with` "the amounts balance" } // This works. - output(Cash.PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } - output(Cash.PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) issuedBy MINI_CORP } + output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY))) + output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY)) issuedBy MINI_CORP) this.verifies() } } @@ -483,12 +469,11 @@ class CashTests { transaction { attachment(Cash.PROGRAM_ID) val pounds = Cash.State(658.POUNDS `issued by` MINI_CORP.ref(3, 4, 5), AnonymousParty(BOB_PUBKEY)) - input(Cash.PROGRAM_ID) { inState ownedBy AnonymousParty(ALICE_PUBKEY) } - input(Cash.PROGRAM_ID) { pounds } - output(Cash.PROGRAM_ID) { inState ownedBy AnonymousParty(BOB_PUBKEY) } - output(Cash.PROGRAM_ID) { pounds ownedBy AnonymousParty(ALICE_PUBKEY) } - command(ALICE_PUBKEY, BOB_PUBKEY) { Cash.Commands.Move() } - + input(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(ALICE_PUBKEY)) + input(Cash.PROGRAM_ID, pounds) + output(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(BOB_PUBKEY)) + output(Cash.PROGRAM_ID, pounds ownedBy AnonymousParty(ALICE_PUBKEY)) + command(listOf(ALICE_PUBKEY, BOB_PUBKEY), Cash.Commands.Move()) this.verifies() } } @@ -792,19 +777,17 @@ class CashTests { ledger(mockService) { unverifiedTransaction { attachment(Cash.PROGRAM_ID) - output(Cash.PROGRAM_ID, "MEGA_CORP cash") { + output(Cash.PROGRAM_ID, "MEGA_CORP cash", Cash.State( amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), - owner = MEGA_CORP - ) - } + owner = MEGA_CORP)) } transaction { attachment(Cash.PROGRAM_ID) input("MEGA_CORP cash") output(Cash.PROGRAM_ID, "MEGA_CORP cash 2", "MEGA_CORP cash".output().copy(owner = AnonymousParty(ALICE_PUBKEY))) - command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } + command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) this.verifies() } @@ -814,7 +797,7 @@ class CashTests { input("MEGA_CORP cash") // We send it to another pubkey so that the transaction is not identical to the previous one output(Cash.PROGRAM_ID, "MEGA_CORP cash 3", "MEGA_CORP cash".output().copy(owner = ALICE)) - command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } + command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) this.verifies() } this.fails() 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 b927f09516..22e7db887f 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 @@ -71,34 +71,33 @@ class ObligationTests { fun trivial() { transaction { attachments(Obligation.PROGRAM_ID) - input(Obligation.PROGRAM_ID) { inState } - + input(Obligation.PROGRAM_ID, inState) tweak { - output(Obligation.PROGRAM_ID) { outState.copy(quantity = 2000.DOLLARS.quantity) } - command(CHARLIE.owningKey) { Obligation.Commands.Move() } + output(Obligation.PROGRAM_ID, outState.copy(quantity = 2000.DOLLARS.quantity)) + command(CHARLIE.owningKey, Obligation.Commands.Move()) this `fails with` "the amounts balance" } tweak { - output(Obligation.PROGRAM_ID) { outState } - command(CHARLIE.owningKey) { DummyCommandData } + output(Obligation.PROGRAM_ID, outState) + command(CHARLIE.owningKey, DummyCommandData) // Invalid command this `fails with` "required net.corda.finance.contracts.asset.Obligation.Commands.Move command" } tweak { - output(Obligation.PROGRAM_ID) { outState } - command(BOB_PUBKEY) { Obligation.Commands.Move() } + output(Obligation.PROGRAM_ID, outState) + command(BOB_PUBKEY, Obligation.Commands.Move()) this `fails with` "the owning keys are a subset of the signing keys" } tweak { - output(Obligation.PROGRAM_ID) { outState } - output(Obligation.PROGRAM_ID) { outState `issued by` MINI_CORP } - command(CHARLIE.owningKey) { Obligation.Commands.Move() } + output(Obligation.PROGRAM_ID, outState) + output(Obligation.PROGRAM_ID, outState `issued by` MINI_CORP) + command(CHARLIE.owningKey, Obligation.Commands.Move()) this `fails with` "at least one obligation input" } // Simple reallocation works. tweak { - output(Obligation.PROGRAM_ID) { outState } - command(CHARLIE.owningKey) { Obligation.Commands.Move() } + output(Obligation.PROGRAM_ID, outState) + command(CHARLIE.owningKey, Obligation.Commands.Move()) this.verifies() } } @@ -109,10 +108,9 @@ class ObligationTests { // Check we can't "move" debt into existence. transaction { attachments(DummyContract.PROGRAM_ID, Obligation.PROGRAM_ID) - input(DummyContract.PROGRAM_ID) { DummyState() } - output(Obligation.PROGRAM_ID) { outState } - command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } - + input(DummyContract.PROGRAM_ID, DummyState()) + output(Obligation.PROGRAM_ID, outState) + command(MINI_CORP_PUBKEY, Obligation.Commands.Move()) this `fails with` "at least one obligation input" } @@ -120,21 +118,19 @@ class ObligationTests { // institution is allowed to issue as much cash as they want. transaction { attachments(Obligation.PROGRAM_ID) - output(Obligation.PROGRAM_ID) { outState } - command(CHARLIE.owningKey) { Obligation.Commands.Issue() } + output(Obligation.PROGRAM_ID, outState) + command(CHARLIE.owningKey, Obligation.Commands.Issue()) this `fails with` "output states are issued by a command signer" } transaction { attachments(Obligation.PROGRAM_ID) - output(Obligation.PROGRAM_ID) { + output(Obligation.PROGRAM_ID, Obligation.State( obligor = MINI_CORP, quantity = 1000.DOLLARS.quantity, beneficiary = CHARLIE, - template = megaCorpDollarSettlement - ) - } - command(MINI_CORP_PUBKEY) { Obligation.Commands.Issue() } + template = megaCorpDollarSettlement)) + command(MINI_CORP_PUBKEY, Obligation.Commands.Issue()) this.verifies() } run { @@ -157,18 +153,17 @@ class ObligationTests { // We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer. transaction { attachments(Obligation.PROGRAM_ID) - input(Obligation.PROGRAM_ID) { inState } - output(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity * 2) } - + input(Obligation.PROGRAM_ID, inState) + output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.amount.quantity * 2)) // Move fails: not allowed to summon money. tweak { - command(CHARLIE.owningKey) { Obligation.Commands.Move() } + command(CHARLIE.owningKey, Obligation.Commands.Move()) this `fails with` "the amounts balance" } // Issue works. tweak { - command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } + command(MEGA_CORP_PUBKEY, Obligation.Commands.Issue()) this.verifies() } } @@ -176,29 +171,29 @@ class ObligationTests { // Can't use an issue command to lower the amount. transaction { attachments(Obligation.PROGRAM_ID) - input(Obligation.PROGRAM_ID) { inState } - output(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity / 2) } - command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } + input(Obligation.PROGRAM_ID, inState) + output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.amount.quantity / 2)) + command(MEGA_CORP_PUBKEY, Obligation.Commands.Issue()) this `fails with` "output values sum to more than the inputs" } // Can't have an issue command that doesn't actually issue money. transaction { attachments(Obligation.PROGRAM_ID) - input(Obligation.PROGRAM_ID) { inState } - output(Obligation.PROGRAM_ID) { inState } - command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } + input(Obligation.PROGRAM_ID, inState) + output(Obligation.PROGRAM_ID, inState) + command(MEGA_CORP_PUBKEY, Obligation.Commands.Issue()) this `fails with` "" } // Can't have any other commands if we have an issue command (because the issue command overrules them). transaction { attachments(Obligation.PROGRAM_ID) - input(Obligation.PROGRAM_ID) { inState } - output(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity * 2) } - command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } + input(Obligation.PROGRAM_ID, inState) + output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.amount.quantity * 2)) + command(MEGA_CORP_PUBKEY, Obligation.Commands.Issue()) tweak { - command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } + command(MEGA_CORP_PUBKEY, Obligation.Commands.Issue()) this `fails with` "there is only a single issue command" } this.verifies() @@ -352,7 +347,7 @@ class ObligationTests { input("Alice's $1,000,000 obligation to Bob") input("Bob's $1,000,000 obligation to Alice") // Note we can sign with either key here - command(ALICE_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } + command(ALICE_PUBKEY, Obligation.Commands.Net(NetType.CLOSE_OUT)) timeWindow(TEST_TX_TIME) this.verifies() } @@ -368,8 +363,8 @@ class ObligationTests { input("Alice's $1,000,000 obligation to Bob") input("Bob's $1,000,000 obligation to Alice") input("MegaCorp's $1,000,000 obligation to Bob") - output(Obligation.PROGRAM_ID, "change") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB) } - command(BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } + output(Obligation.PROGRAM_ID, "change", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB)) + command(listOf(BOB_PUBKEY, MEGA_CORP_PUBKEY), Obligation.Commands.Net(NetType.CLOSE_OUT)) timeWindow(TEST_TX_TIME) this.verifies() } @@ -383,8 +378,8 @@ class ObligationTests { attachments(Obligation.PROGRAM_ID) input("Alice's $1,000,000 obligation to Bob") input("Bob's $1,000,000 obligation to Alice") - output(Obligation.PROGRAM_ID, "change") { (oneMillionDollars.splitEvenly(2).first()).OBLIGATION between Pair(ALICE, BOB) } - command(BOB_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } + output(Obligation.PROGRAM_ID, "change", oneMillionDollars.splitEvenly(2).first().OBLIGATION between Pair(ALICE, BOB)) + command(BOB_PUBKEY, Obligation.Commands.Net(NetType.CLOSE_OUT)) timeWindow(TEST_TX_TIME) this `fails with` "amounts owed on input and output must match" } @@ -397,7 +392,7 @@ class ObligationTests { attachments(Obligation.PROGRAM_ID) input("Alice's $1,000,000 obligation to Bob") input("Bob's $1,000,000 obligation to Alice") - command(MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } + command(MEGA_CORP_PUBKEY, Obligation.Commands.Net(NetType.CLOSE_OUT)) timeWindow(TEST_TX_TIME) this `fails with` "any involved party has signed" } @@ -413,7 +408,7 @@ class ObligationTests { attachments(Obligation.PROGRAM_ID) input("Alice's $1,000,000 obligation to Bob") input("Bob's $1,000,000 obligation to Alice") - command(ALICE_PUBKEY, BOB_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) } + command(listOf(ALICE_PUBKEY, BOB_PUBKEY), Obligation.Commands.Net(NetType.PAYMENT)) timeWindow(TEST_TX_TIME) this.verifies() } @@ -428,7 +423,7 @@ class ObligationTests { attachments(Obligation.PROGRAM_ID) input("Alice's $1,000,000 obligation to Bob") input("Bob's $1,000,000 obligation to Alice") - command(BOB_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) } + command(BOB_PUBKEY, Obligation.Commands.Net(NetType.PAYMENT)) timeWindow(TEST_TX_TIME) this `fails with` "all involved parties have signed" } @@ -441,8 +436,8 @@ class ObligationTests { attachments(Obligation.PROGRAM_ID) input("Bob's $1,000,000 obligation to Alice") input("MegaCorp's $1,000,000 obligation to Bob") - output(Obligation.PROGRAM_ID, "MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE) } - command(ALICE_PUBKEY, BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) } + output(Obligation.PROGRAM_ID, "MegaCorp's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE)) + command(listOf(ALICE_PUBKEY, BOB_PUBKEY, MEGA_CORP_PUBKEY), Obligation.Commands.Net(NetType.PAYMENT)) timeWindow(TEST_TX_TIME) this.verifies() } @@ -456,8 +451,8 @@ class ObligationTests { attachments(Obligation.PROGRAM_ID) input("Bob's $1,000,000 obligation to Alice") input("MegaCorp's $1,000,000 obligation to Bob") - output(Obligation.PROGRAM_ID, "MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE) } - command(ALICE_PUBKEY, BOB_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) } + output(Obligation.PROGRAM_ID, "MegaCorp's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE)) + command(listOf(ALICE_PUBKEY, BOB_PUBKEY), Obligation.Commands.Net(NetType.PAYMENT)) timeWindow(TEST_TX_TIME) this `fails with` "all involved parties have signed" } @@ -473,9 +468,9 @@ class ObligationTests { attachments(Obligation.PROGRAM_ID) input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000") - output(Obligation.PROGRAM_ID, "Bob's $1,000,000") { 1000000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB } - command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) } - command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } + output(Obligation.PROGRAM_ID, "Bob's $1,000,000", 1000000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB) + command(ALICE_PUBKEY, Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token))) + command(ALICE_PUBKEY, Cash.Commands.Move(Obligation::class.java)) attachment(attachment(cashContractBytes.inputStream())) this.verifies() } @@ -488,10 +483,10 @@ class ObligationTests { attachments(Obligation.PROGRAM_ID, Cash.PROGRAM_ID) input(Obligation.PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) input(Cash.PROGRAM_ID, 500000.DOLLARS.CASH issuedBy defaultIssuer ownedBy ALICE) - output(Obligation.PROGRAM_ID, "Alice's $500,000 obligation to Bob") { halfAMillionDollars.OBLIGATION between Pair(ALICE, BOB) } - output(Obligation.PROGRAM_ID, "Bob's $500,000") { 500000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB } - command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) } - command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } + output(Obligation.PROGRAM_ID, "Alice's $500,000 obligation to Bob", halfAMillionDollars.OBLIGATION between Pair(ALICE, BOB)) + output(Obligation.PROGRAM_ID, "Bob's $500,000", 500000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB) + command(ALICE_PUBKEY, Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token))) + command(ALICE_PUBKEY, Cash.Commands.Move(Obligation::class.java)) attachment(attachment(cashContractBytes.inputStream())) this.verifies() } @@ -504,9 +499,9 @@ class ObligationTests { attachments(Obligation.PROGRAM_ID, Cash.PROGRAM_ID) input(Obligation.PROGRAM_ID, defaultedObligation) // Alice's defaulted $1,000,000 obligation to Bob input(Cash.PROGRAM_ID, 1000000.DOLLARS.CASH issuedBy defaultIssuer ownedBy ALICE) - output(Obligation.PROGRAM_ID, "Bob's $1,000,000") { 1000000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB } - command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) } - command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } + output(Obligation.PROGRAM_ID, "Bob's $1,000,000", 1000000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB) + command(ALICE_PUBKEY, Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token))) + command(ALICE_PUBKEY, Cash.Commands.Move(Obligation::class.java)) this `fails with` "all inputs are in the normal state" } } @@ -518,9 +513,9 @@ class ObligationTests { attachments(Obligation.PROGRAM_ID) input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000") - output(Obligation.PROGRAM_ID, "Bob's $1,000,000") { 1000000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB } - command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) } - command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } + output(Obligation.PROGRAM_ID, "Bob's $1,000,000", 1000000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB) + command(ALICE_PUBKEY, Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token))) + command(ALICE_PUBKEY, Cash.Commands.Move(Obligation::class.java)) attachment(attachment(cashContractBytes.inputStream())) this `fails with` "amount in settle command" } @@ -546,9 +541,9 @@ class ObligationTests { attachments(Obligation.PROGRAM_ID) input("Alice's 1 FCOJ obligation to Bob") input("Alice's 1 FCOJ") - output(Obligation.PROGRAM_ID, "Bob's 1 FCOJ") { CommodityContract.State(oneUnitFcoj, BOB) } - command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneUnitFcoj.quantity, oneUnitFcojObligation.amount.token)) } - command(ALICE_PUBKEY) { CommodityContract.Commands.Move(Obligation::class.java) } + output(Obligation.PROGRAM_ID, "Bob's 1 FCOJ", CommodityContract.State(oneUnitFcoj, BOB)) + command(ALICE_PUBKEY, Obligation.Commands.Settle(Amount(oneUnitFcoj.quantity, oneUnitFcojObligation.amount.token))) + command(ALICE_PUBKEY, CommodityContract.Commands.Move(Obligation::class.java)) attachment(attachment(commodityContractBytes.inputStream())) verifies() } @@ -563,8 +558,8 @@ class ObligationTests { transaction("Settlement") { attachments(Obligation.PROGRAM_ID) input("Alice's $1,000,000 obligation to Bob") - output(Obligation.PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED) } - command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) } + output(Obligation.PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob", (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED)) + command(BOB_PUBKEY, Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED)) this `fails with` "there is a time-window from the authority" } } @@ -575,8 +570,8 @@ class ObligationTests { transaction { attachments(Obligation.PROGRAM_ID) input(Obligation.PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime) - output(Obligation.PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED) } - command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) } + output(Obligation.PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob", (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED)) + command(BOB_PUBKEY, Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED)) timeWindow(TEST_TX_TIME) this `fails with` "the due date has passed" } @@ -586,8 +581,8 @@ class ObligationTests { transaction { attachments(Obligation.PROGRAM_ID) input(Obligation.PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime) - output(Obligation.PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED) } - command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) } + output(Obligation.PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob", (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED)) + command(BOB_PUBKEY, Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED)) timeWindow(TEST_TX_TIME) this.verifies() } @@ -600,24 +595,24 @@ class ObligationTests { // Splitting value works. transaction { attachments(Obligation.PROGRAM_ID) - command(CHARLIE.owningKey) { Obligation.Commands.Move() } + command(CHARLIE.owningKey, Obligation.Commands.Move()) tweak { - input(Obligation.PROGRAM_ID) { inState } - repeat(4) { output(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.quantity / 4) } } + input(Obligation.PROGRAM_ID, inState) + repeat(4) { output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.quantity / 4)) } this.verifies() } // Merging 4 inputs into 2 outputs works. tweak { - repeat(4) { input(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.quantity / 4) } } - output(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) } - output(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) } + repeat(4) { input(Obligation.PROGRAM_ID, inState.copy(quantity = inState.quantity / 4)) } + output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.quantity / 2)) + output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.quantity / 2)) this.verifies() } // Merging 2 inputs into 1 works. tweak { - input(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) } - input(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) } - output(Obligation.PROGRAM_ID) { inState } + input(Obligation.PROGRAM_ID, inState.copy(quantity = inState.quantity / 2)) + input(Obligation.PROGRAM_ID, inState.copy(quantity = inState.quantity / 2)) + output(Obligation.PROGRAM_ID, inState) this.verifies() } } @@ -627,18 +622,16 @@ class ObligationTests { fun zeroSizedValues() { transaction { attachments(Obligation.PROGRAM_ID) - command(CHARLIE.owningKey) { Obligation.Commands.Move() } + command(CHARLIE.owningKey, Obligation.Commands.Move()) tweak { - input(Obligation.PROGRAM_ID) { inState } - input(Obligation.PROGRAM_ID) { inState.copy(quantity = 0L) } - + input(Obligation.PROGRAM_ID, inState) + input(Obligation.PROGRAM_ID, inState.copy(quantity = 0L)) this `fails with` "zero sized inputs" } tweak { - input(Obligation.PROGRAM_ID) { inState } - output(Obligation.PROGRAM_ID) { inState } - output(Obligation.PROGRAM_ID) { inState.copy(quantity = 0L) } - + input(Obligation.PROGRAM_ID, inState) + output(Obligation.PROGRAM_ID, inState) + output(Obligation.PROGRAM_ID, inState.copy(quantity = 0L)) this `fails with` "zero sized outputs" } } @@ -649,41 +642,39 @@ class ObligationTests { // Can't change issuer. transaction { attachments(Obligation.PROGRAM_ID) - input(Obligation.PROGRAM_ID) { inState } - output(Obligation.PROGRAM_ID) { outState `issued by` MINI_CORP } - command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } + input(Obligation.PROGRAM_ID, inState) + output(Obligation.PROGRAM_ID, outState `issued by` MINI_CORP) + command(MINI_CORP_PUBKEY, Obligation.Commands.Move()) this `fails with` "the amounts balance" } // Can't mix currencies. transaction { attachments(Obligation.PROGRAM_ID) - input(Obligation.PROGRAM_ID) { inState } - output(Obligation.PROGRAM_ID) { outState.copy(quantity = 80000, template = megaCorpDollarSettlement) } - output(Obligation.PROGRAM_ID) { outState.copy(quantity = 20000, template = megaCorpPoundSettlement) } - command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } + input(Obligation.PROGRAM_ID, inState) + output(Obligation.PROGRAM_ID, outState.copy(quantity = 80000, template = megaCorpDollarSettlement)) + output(Obligation.PROGRAM_ID, outState.copy(quantity = 20000, template = megaCorpPoundSettlement)) + command(MINI_CORP_PUBKEY, Obligation.Commands.Move()) this `fails with` "the amounts balance" } transaction { attachments(Obligation.PROGRAM_ID) - input(Obligation.PROGRAM_ID) { inState } - input(Obligation.PROGRAM_ID) { + input(Obligation.PROGRAM_ID, inState) + input(Obligation.PROGRAM_ID, inState.copy( quantity = 15000, template = megaCorpPoundSettlement, - beneficiary = AnonymousParty(BOB_PUBKEY) - ) - } - output(Obligation.PROGRAM_ID) { outState.copy(quantity = 115000) } - command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } + beneficiary = AnonymousParty(BOB_PUBKEY))) + output(Obligation.PROGRAM_ID, outState.copy(quantity = 115000)) + command(MINI_CORP_PUBKEY, Obligation.Commands.Move()) this `fails with` "the amounts balance" } // Can't have superfluous input states from different issuers. transaction { attachments(Obligation.PROGRAM_ID) - input(Obligation.PROGRAM_ID) { inState } - input(Obligation.PROGRAM_ID) { inState `issued by` MINI_CORP } - output(Obligation.PROGRAM_ID) { outState } - command(CHARLIE.owningKey) { Obligation.Commands.Move() } + input(Obligation.PROGRAM_ID, inState) + input(Obligation.PROGRAM_ID, inState `issued by` MINI_CORP) + output(Obligation.PROGRAM_ID, outState) + command(CHARLIE.owningKey, Obligation.Commands.Move()) this `fails with` "the amounts balance" } } @@ -693,21 +684,20 @@ class ObligationTests { // Single input/output straightforward case. transaction { attachments(Obligation.PROGRAM_ID) - input(Obligation.PROGRAM_ID) { inState } - output(Obligation.PROGRAM_ID) { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) } - + input(Obligation.PROGRAM_ID, inState) + output(Obligation.PROGRAM_ID, outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity)) tweak { - command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token)) } - command(CHARLIE.owningKey) { Obligation.Commands.Move() } + command(CHARLIE.owningKey, Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token))) + command(CHARLIE.owningKey, Obligation.Commands.Move()) this `fails with` "the amounts balance" } tweak { - command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token)) } + command(CHARLIE.owningKey, Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token))) this `fails with` "required net.corda.finance.contracts.asset.Obligation.Commands.Move command" tweak { - command(CHARLIE.owningKey) { Obligation.Commands.Move() } + command(CHARLIE.owningKey, Obligation.Commands.Move()) this.verifies() } } @@ -720,21 +710,15 @@ class ObligationTests { // Multi-product case. transaction { attachments(Obligation.PROGRAM_ID) - - input(Obligation.PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds)) } - input(Obligation.PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars)) } - - output(Obligation.PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds), quantity = inState.quantity - 200.POUNDS.quantity) } - output(Obligation.PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars), quantity = inState.quantity - 200.DOLLARS.quantity) } - - command(CHARLIE.owningKey) { Obligation.Commands.Move() } - + input(Obligation.PROGRAM_ID, inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds))) + input(Obligation.PROGRAM_ID, inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars))) + output(Obligation.PROGRAM_ID, inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds), quantity = inState.quantity - 200.POUNDS.quantity)) + output(Obligation.PROGRAM_ID, inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars), quantity = inState.quantity - 200.DOLLARS.quantity)) + command(CHARLIE.owningKey, Obligation.Commands.Move()) this `fails with` "the amounts balance" - - command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token.copy(product = megaCorpDollarSettlement))) } + command(CHARLIE.owningKey, Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token.copy(product = megaCorpDollarSettlement)))) this `fails with` "the amounts balance" - - command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(200.POUNDS.quantity, inState.amount.token.copy(product = megaCorpPoundSettlement))) } + command(CHARLIE.owningKey, Obligation.Commands.Exit(Amount(200.POUNDS.quantity, inState.amount.token.copy(product = megaCorpPoundSettlement)))) this.verifies() } } @@ -745,27 +729,26 @@ class ObligationTests { attachments(Obligation.PROGRAM_ID) // Gather 2000 dollars from two different issuers. - input(Obligation.PROGRAM_ID) { inState } - input(Obligation.PROGRAM_ID) { inState `issued by` MINI_CORP } - + input(Obligation.PROGRAM_ID, inState) + input(Obligation.PROGRAM_ID, inState `issued by` MINI_CORP) // Can't merge them together. tweak { - output(Obligation.PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY), quantity = 200000L) } - command(CHARLIE.owningKey) { Obligation.Commands.Move() } + output(Obligation.PROGRAM_ID, inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY), quantity = 200000L)) + command(CHARLIE.owningKey, Obligation.Commands.Move()) this `fails with` "the amounts balance" } // Missing MiniCorp deposit tweak { - output(Obligation.PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } - output(Obligation.PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } - command(CHARLIE.owningKey) { Obligation.Commands.Move() } + output(Obligation.PROGRAM_ID, inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY))) + output(Obligation.PROGRAM_ID, inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY))) + command(CHARLIE.owningKey, Obligation.Commands.Move()) this `fails with` "the amounts balance" } // This works. - output(Obligation.PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } - output(Obligation.PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP } - command(CHARLIE.owningKey) { Obligation.Commands.Move() } + output(Obligation.PROGRAM_ID, inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY))) + output(Obligation.PROGRAM_ID, inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP) + command(CHARLIE.owningKey, Obligation.Commands.Move()) this.verifies() } } @@ -776,12 +759,11 @@ class ObligationTests { transaction { attachments(Obligation.PROGRAM_ID) val pounds = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpPoundSettlement, 658.POUNDS.quantity, AnonymousParty(BOB_PUBKEY)) - input(Obligation.PROGRAM_ID) { inState `owned by` CHARLIE } - input(Obligation.PROGRAM_ID) { pounds } - output(Obligation.PROGRAM_ID) { inState `owned by` AnonymousParty(BOB_PUBKEY) } - output(Obligation.PROGRAM_ID) { pounds `owned by` CHARLIE } - command(CHARLIE.owningKey, BOB_PUBKEY) { Obligation.Commands.Move() } - + input(Obligation.PROGRAM_ID, inState `owned by` CHARLIE) + input(Obligation.PROGRAM_ID, pounds) + output(Obligation.PROGRAM_ID, inState `owned by` AnonymousParty(BOB_PUBKEY)) + output(Obligation.PROGRAM_ID, pounds `owned by` CHARLIE) + command(listOf(CHARLIE.owningKey, BOB_PUBKEY), Obligation.Commands.Move()) this.verifies() } } 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 9196c1d293..145a7b933b 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -653,13 +653,13 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { // wants to sell to Bob. val eb1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { // Issued money to itself. - output(Cash.PROGRAM_ID, "elbonian money 1", notary = notary) { 800.DOLLARS.CASH issuedBy issuer ownedBy interimOwner } - output(Cash.PROGRAM_ID, "elbonian money 2", notary = notary) { 1000.DOLLARS.CASH issuedBy issuer ownedBy interimOwner } + output(Cash.PROGRAM_ID, "elbonian money 1", notary = notary, contractState = 800.DOLLARS.CASH issuedBy issuer ownedBy interimOwner) + output(Cash.PROGRAM_ID, "elbonian money 2", notary = notary, contractState = 1000.DOLLARS.CASH issuedBy issuer ownedBy interimOwner) if (!withError) { - command(issuer.party.owningKey) { Cash.Commands.Issue() } + command(issuer.party.owningKey, Cash.Commands.Issue()) } else { // Put a broken command on so at least a signature is created - command(issuer.party.owningKey) { Cash.Commands.Move() } + command(issuer.party.owningKey, Cash.Commands.Move()) } timeWindow(TEST_TX_TIME) if (withError) { @@ -672,16 +672,16 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { // Bob gets some cash onto the ledger from BoE val bc1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { input("elbonian money 1") - output(Cash.PROGRAM_ID, "bob cash 1", notary = notary) { 800.DOLLARS.CASH issuedBy issuer ownedBy owner } - command(interimOwner.owningKey) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, "bob cash 1", notary = notary, contractState = 800.DOLLARS.CASH issuedBy issuer ownedBy owner) + command(interimOwner.owningKey, Cash.Commands.Move()) this.verifies() } val bc2 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { input("elbonian money 2") - output(Cash.PROGRAM_ID, "bob cash 2", notary = notary) { 300.DOLLARS.CASH issuedBy issuer ownedBy owner } - output(Cash.PROGRAM_ID, notary = notary) { 700.DOLLARS.CASH issuedBy issuer ownedBy interimOwner } // Change output. - command(interimOwner.owningKey) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, "bob cash 2", notary = notary, contractState = 300.DOLLARS.CASH issuedBy issuer ownedBy owner) + output(Cash.PROGRAM_ID, notary = notary, contractState = 700.DOLLARS.CASH issuedBy issuer ownedBy interimOwner) // Change output. + command(interimOwner.owningKey, Cash.Commands.Move()) this.verifies() } @@ -697,10 +697,9 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { attachmentID: SecureHash?, notary: Party): Pair, List> { val ap = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { - output(CommercialPaper.CP_PROGRAM_ID, "alice's paper", notary = notary) { - CommercialPaper.State(issuer, owner, amount, TEST_TX_TIME + 7.days) - } - command(issuer.party.owningKey) { CommercialPaper.Commands.Issue() } + output(CommercialPaper.CP_PROGRAM_ID, "alice's paper", notary = notary, + contractState = CommercialPaper.State(issuer, owner, amount, TEST_TX_TIME + 7.days)) + command(issuer.party.owningKey, CommercialPaper.Commands.Issue()) if (!withError) timeWindow(time = TEST_TX_TIME) if (attachmentID != null) 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 a0e75da7ac..c200bd7c88 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 @@ -385,8 +385,8 @@ class IRSTests { return ledger { transaction("Agreement") { attachments(IRS_PROGRAM_ID) - output(IRS_PROGRAM_ID, "irs post agreement") { singleIRS() } - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + output(IRS_PROGRAM_ID, "irs post agreement", singleIRS()) + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this.verifies() } @@ -395,17 +395,14 @@ class IRSTests { attachments(IRS_PROGRAM_ID) input("irs post agreement") val postAgreement = "irs post agreement".output() - output(IRS_PROGRAM_ID, "irs post first fixing") { + output(IRS_PROGRAM_ID, "irs post first fixing", postAgreement.copy( postAgreement.fixedLeg, postAgreement.floatingLeg, postAgreement.calculation.applyFixing(ld, FixedRate(RatioUnit(bd))), - postAgreement.common - ) - } - command(ORACLE_PUBKEY) { - InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) - } + postAgreement.common)) + command(ORACLE_PUBKEY, + InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd))) timeWindow(TEST_TX_TIME) this.verifies() } @@ -419,7 +416,7 @@ class IRSTests { attachments(IRS_PROGRAM_ID) input(IRS_PROGRAM_ID, irs) output(IRS_PROGRAM_ID, "irs post agreement", irs) - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this `fails with` "There are no in states for an agreement" } @@ -432,7 +429,7 @@ class IRSTests { transaction { attachments(IRS_PROGRAM_ID) output(IRS_PROGRAM_ID, irs.copy(calculation = irs.calculation.copy(fixedLegPaymentSchedule = emptySchedule))) - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this `fails with` "There are events in the fix schedule" } @@ -445,7 +442,7 @@ class IRSTests { transaction { attachments(IRS_PROGRAM_ID) output(IRS_PROGRAM_ID, irs.copy(calculation = irs.calculation.copy(floatingLegPaymentSchedule = emptySchedule))) - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this `fails with` "There are events in the float schedule" } @@ -457,7 +454,7 @@ class IRSTests { transaction { attachments(IRS_PROGRAM_ID) output(IRS_PROGRAM_ID, irs.copy(irs.fixedLeg.copy(notional = irs.fixedLeg.notional.copy(quantity = 0)))) - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this `fails with` "All notionals must be non zero" } @@ -465,7 +462,7 @@ class IRSTests { transaction { attachments(IRS_PROGRAM_ID) output(IRS_PROGRAM_ID, irs.copy(irs.fixedLeg.copy(notional = irs.floatingLeg.notional.copy(quantity = 0)))) - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this `fails with` "All notionals must be non zero" } @@ -478,7 +475,7 @@ class IRSTests { transaction { attachments(IRS_PROGRAM_ID) output(IRS_PROGRAM_ID, modifiedIRS) - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this `fails with` "The fixed leg rate must be positive" } @@ -494,7 +491,7 @@ class IRSTests { transaction { attachments(IRS_PROGRAM_ID) output(IRS_PROGRAM_ID, modifiedIRS) - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this `fails with` "The currency of the notionals must be the same" } @@ -507,7 +504,7 @@ class IRSTests { transaction { attachments(IRS_PROGRAM_ID) output(IRS_PROGRAM_ID, modifiedIRS) - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this `fails with` "All leg notionals must be the same" } @@ -520,7 +517,7 @@ class IRSTests { transaction { attachments(IRS_PROGRAM_ID) output(IRS_PROGRAM_ID, modifiedIRS1) - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this `fails with` "The effective date is before the termination date for the fixed leg" } @@ -529,7 +526,7 @@ class IRSTests { transaction { attachments(IRS_PROGRAM_ID) output(IRS_PROGRAM_ID, modifiedIRS2) - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this `fails with` "The effective date is before the termination date for the floating leg" } @@ -543,7 +540,7 @@ class IRSTests { transaction { attachments(IRS_PROGRAM_ID) output(IRS_PROGRAM_ID, modifiedIRS3) - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this `fails with` "The termination dates are aligned" } @@ -553,7 +550,7 @@ class IRSTests { transaction { attachments(IRS_PROGRAM_ID) output(IRS_PROGRAM_ID, modifiedIRS4) - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this `fails with` "The effective dates are aligned" } @@ -567,8 +564,8 @@ class IRSTests { transaction { attachments(IRS_PROGRAM_ID) - output(IRS_PROGRAM_ID, "irs post agreement") { singleIRS() } - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + output(IRS_PROGRAM_ID, "irs post agreement", singleIRS()) + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this.verifies() } @@ -585,9 +582,8 @@ class IRSTests { // Templated tweak for reference. A corrent fixing applied should be ok tweak { - command(ORACLE_PUBKEY) { - InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) - } + command(ORACLE_PUBKEY, + InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd))) timeWindow(TEST_TX_TIME) output(IRS_PROGRAM_ID, newIRS) this.verifies() @@ -595,7 +591,7 @@ class IRSTests { // This test makes sure that verify confirms the fixing was applied and there is a difference in the old and new tweak { - command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } + command(ORACLE_PUBKEY, InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd))) timeWindow(TEST_TX_TIME) output(IRS_PROGRAM_ID, oldIRS) this `fails with` "There is at least one difference in the IRS floating leg payment schedules" @@ -603,42 +599,36 @@ class IRSTests { // This tests tries to sneak in a change to another fixing (which may or may not be the latest one) tweak { - command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } + command(ORACLE_PUBKEY, InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd))) timeWindow(TEST_TX_TIME) val firstResetKey = newIRS.calculation.floatingLegPaymentSchedule.keys.toList()[1] val firstResetValue = newIRS.calculation.floatingLegPaymentSchedule[firstResetKey] val modifiedFirstResetValue = firstResetValue!!.copy(notional = Amount(firstResetValue.notional.quantity, Currency.getInstance("JPY"))) - - output(IRS_PROGRAM_ID) { + output(IRS_PROGRAM_ID, newIRS.copy( newIRS.fixedLeg, newIRS.floatingLeg, newIRS.calculation.copy(floatingLegPaymentSchedule = newIRS.calculation.floatingLegPaymentSchedule.plus( Pair(firstResetKey, modifiedFirstResetValue))), - newIRS.common - ) - } + newIRS.common)) this `fails with` "There is only one change in the IRS floating leg payment schedule" } // This tests modifies the payment currency for the fixing tweak { - command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } + command(ORACLE_PUBKEY, InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd))) timeWindow(TEST_TX_TIME) val latestReset = newIRS.calculation.floatingLegPaymentSchedule.filter { it.value.rate is FixedRate }.maxBy { it.key } val modifiedLatestResetValue = latestReset!!.value.copy(notional = Amount(latestReset.value.notional.quantity, Currency.getInstance("JPY"))) - - output(IRS_PROGRAM_ID) { + output(IRS_PROGRAM_ID, newIRS.copy( newIRS.fixedLeg, newIRS.floatingLeg, newIRS.calculation.copy(floatingLegPaymentSchedule = newIRS.calculation.floatingLegPaymentSchedule.plus( Pair(latestReset.key, modifiedLatestResetValue))), - newIRS.common - ) - } + newIRS.common)) this `fails with` "The fix payment has the same currency as the notional" } } @@ -660,31 +650,27 @@ class IRSTests { return ledger { transaction("Agreement") { attachments(IRS_PROGRAM_ID) - output(IRS_PROGRAM_ID, "irs post agreement1") { + output(IRS_PROGRAM_ID, "irs post agreement1", irs.copy( irs.fixedLeg, irs.floatingLeg, irs.calculation, - irs.common.copy(tradeID = "t1") - ) - } - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + irs.common.copy(tradeID = "t1"))) + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this.verifies() } transaction("Agreement") { attachments(IRS_PROGRAM_ID) - output(IRS_PROGRAM_ID, "irs post agreement2") { + output(IRS_PROGRAM_ID, "irs post agreement2", irs.copy( linearId = UniqueIdentifier("t2"), fixedLeg = irs.fixedLeg, floatingLeg = irs.floatingLeg, calculation = irs.calculation, - common = irs.common.copy(tradeID = "t2") - ) - } - command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } + common = irs.common.copy(tradeID = "t2"))) + command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree()) timeWindow(TEST_TX_TIME) this.verifies() } @@ -694,27 +680,21 @@ class IRSTests { input("irs post agreement1") input("irs post agreement2") val postAgreement1 = "irs post agreement1".output() - output(IRS_PROGRAM_ID, "irs post first fixing1") { + output(IRS_PROGRAM_ID, "irs post first fixing1", postAgreement1.copy( postAgreement1.fixedLeg, postAgreement1.floatingLeg, postAgreement1.calculation.applyFixing(ld1, FixedRate(RatioUnit(bd1))), - postAgreement1.common.copy(tradeID = "t1") - ) - } + postAgreement1.common.copy(tradeID = "t1"))) val postAgreement2 = "irs post agreement2".output() - output(IRS_PROGRAM_ID, "irs post first fixing2") { + output(IRS_PROGRAM_ID, "irs post first fixing2", postAgreement2.copy( postAgreement2.fixedLeg, postAgreement2.floatingLeg, postAgreement2.calculation.applyFixing(ld1, FixedRate(RatioUnit(bd1))), - postAgreement2.common.copy(tradeID = "t2") - ) - } - - command(ORACLE_PUBKEY) { - InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld1, Tenor("3M")), bd1)) - } + postAgreement2.common.copy(tradeID = "t2"))) + command(ORACLE_PUBKEY, + InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld1, Tenor("3M")), bd1))) timeWindow(TEST_TX_TIME) this.verifies() } 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 48658ea021..3167eb7a38 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 @@ -93,7 +93,7 @@ data class TestTransactionDSLInterpreter private constructor( transactionBuilder.addInputState(StateAndRef(state, stateRef)) } - override fun _output(contractClassName: ContractClassName, + override fun output(contractClassName: ContractClassName, label: String?, notary: Party, encumbrance: Int?, @@ -115,7 +115,7 @@ data class TestTransactionDSLInterpreter private constructor( transactionBuilder.addAttachment(attachmentId) } - override fun _command(signers: List, commandData: CommandData) { + override fun command(signers: List, commandData: CommandData) { val command = Command(commandData, signers) transactionBuilder.addCommand(command) } 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 af6520f4dc..356323f613 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 @@ -35,12 +35,12 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup { * @param contractState The state itself. * @param contractClassName The class name of the contract that verifies this state. */ - fun _output(contractClassName: ContractClassName, - label: String?, - notary: Party, - encumbrance: Int?, - attachmentConstraint: AttachmentConstraint, - contractState: ContractState) + fun output(contractClassName: ContractClassName, + label: String?, + notary: Party, + encumbrance: Int?, + attachmentConstraint: AttachmentConstraint, + contractState: ContractState) /** * Adds an [Attachment] reference to the transaction. @@ -53,7 +53,7 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup { * @param signers The signer public keys. * @param commandData The contents of the command. */ - fun _command(signers: List, commandData: CommandData) + fun command(signers: List, commandData: CommandData) /** * Sets the time-window of the transaction. @@ -74,10 +74,10 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup { fun _attachment(contractClassName: ContractClassName) } -class TransactionDSL(val interpreter: T) : TransactionDSLInterpreter by interpreter { +class TransactionDSL(interpreter: T) : TransactionDSLInterpreter by interpreter { /** * Looks up the output label and adds the found state as an input. - * @param stateLabel The label of the output state specified when calling [TransactionDSLInterpreter._output] and friends. + * @param stateLabel The label of the output state specified when calling [TransactionDSLInterpreter.output] and friends. */ fun input(stateLabel: String) = input(retrieveOutputStateAndRef(ContractState::class.java, stateLabel).ref) @@ -88,49 +88,51 @@ class TransactionDSL(val interpreter: T) : Tr */ fun input(contractClassName: ContractClassName, state: ContractState) { val transaction = ledgerInterpreter._unverifiedTransaction(null, TransactionBuilder(notary = DUMMY_NOTARY)) { - output(contractClassName, attachmentConstraint = AlwaysAcceptAttachmentConstraint) { state } + output(contractClassName, null, DUMMY_NOTARY, null, AlwaysAcceptAttachmentConstraint, state) } input(transaction.outRef(0).ref) } - fun input(contractClassName: ContractClassName, stateClosure: () -> ContractState) = input(contractClassName, stateClosure()) - /** - * Adds an output to the transaction. + * Adds a labelled output to the transaction. */ - @JvmOverloads - fun output(contractClassName: ContractClassName, - label: String? = null, - notary: Party = DUMMY_NOTARY, - encumbrance: Int? = null, - attachmentConstraint: AttachmentConstraint = AutomaticHashConstraint, - contractStateClosure: () -> ContractState) = - _output(contractClassName, label, notary, encumbrance, attachmentConstraint, contractStateClosure()) + fun output(contractClassName: ContractClassName, label: String, notary: Party, contractState: ContractState) = + output(contractClassName, label, notary, null, AutomaticHashConstraint, contractState) /** * Adds a labelled output to the transaction. */ - @JvmOverloads - fun output(contractClassName: ContractClassName, label: String, contractState: ContractState, attachmentConstraint: AttachmentConstraint = AutomaticHashConstraint) = - _output(contractClassName, label, DUMMY_NOTARY, null, attachmentConstraint, contractState) + fun output(contractClassName: ContractClassName, label: String, encumbrance: Int, contractState: ContractState) = + output(contractClassName, label, DUMMY_NOTARY, encumbrance, AutomaticHashConstraint, contractState) + + /** + * Adds a labelled output to the transaction. + */ + fun output(contractClassName: ContractClassName, label: String, contractState: ContractState) = + output(contractClassName, label, DUMMY_NOTARY, null, AutomaticHashConstraint, contractState) /** * Adds an output to the transaction. */ - @JvmOverloads - fun output(contractClassName: ContractClassName, contractState: ContractState, attachmentConstraint: AttachmentConstraint = AutomaticHashConstraint) = - _output(contractClassName, null, DUMMY_NOTARY, null, attachmentConstraint, contractState) + fun output(contractClassName: ContractClassName, notary: Party, contractState: ContractState) = + output(contractClassName, null, notary, null, AutomaticHashConstraint, contractState) + + /** + * Adds an output to the transaction. + */ + fun output(contractClassName: ContractClassName, encumbrance: Int, contractState: ContractState) = + output(contractClassName, null, DUMMY_NOTARY, encumbrance, AutomaticHashConstraint, contractState) + + /** + * Adds an output to the transaction. + */ + fun output(contractClassName: ContractClassName, contractState: ContractState) = + output(contractClassName, null, DUMMY_NOTARY, null, AutomaticHashConstraint, contractState) /** * Adds a command to the transaction. */ - fun command(vararg signers: PublicKey, commandDataClosure: () -> CommandData) = - _command(listOf(*signers), commandDataClosure()) - - /** - * Adds a command to the transaction. - */ - fun command(signer: PublicKey, commandData: CommandData) = _command(listOf(signer), commandData) + fun command(signer: PublicKey, commandData: CommandData) = command(listOf(signer), commandData) /** * Sets the [TimeWindow] of the transaction. @@ -142,7 +144,7 @@ class TransactionDSL(val interpreter: T) : Tr timeWindow(TimeWindow.withTolerance(time, tolerance)) /** - * @see TransactionDSLInterpreter._contractAttachment + * @see TransactionDSLInterpreter._attachment */ fun attachment(contractClassName: ContractClassName) = _attachment(contractClassName) From 10e686bc82e2542c0965d1bb90c18c4bb19004fd Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Thu, 30 Nov 2017 16:28:58 +0000 Subject: [PATCH 25/27] Inline DriverConstants. (#2156) --- .../statemachine/LargeTransactionsTest.kt | 23 ++++---- .../net/corda/testing/DriverConstants.kt | 52 ------------------- 2 files changed, 12 insertions(+), 63 deletions(-) delete mode 100644 testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt 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 9f0212d2cb..980a29bb2e 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 @@ -4,15 +4,15 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.crypto.SecureHash import net.corda.core.flows.* import net.corda.core.internal.InputStreamAndHash +import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.startFlow import net.corda.core.transactions.TransactionBuilder -import net.corda.testing.BOB -import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.aliceAndBob +import net.corda.core.utilities.getOrThrow +import net.corda.nodeapi.User +import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState import net.corda.testing.driver.driver -import net.corda.testing.dummyCommand import org.junit.Test import kotlin.test.assertEquals @@ -65,15 +65,16 @@ class LargeTransactionsTest { val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2) val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3) driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) { - val (alice, _) = aliceAndBob() - alice.useRPC { - val hash1 = it.uploadAttachment(bigFile1.inputStream) - val hash2 = it.uploadAttachment(bigFile2.inputStream) - val hash3 = it.uploadAttachment(bigFile3.inputStream) - val hash4 = it.uploadAttachment(bigFile4.inputStream) + val rpcUser = User("admin", "admin", setOf("ALL")) + val (alice, _) = listOf(ALICE_NAME, BOB_NAME).map { startNode(providedName = it, rpcUsers = listOf(rpcUser)) }.transpose().getOrThrow() + alice.rpcClientToNode().use(rpcUser.username, rpcUser.password) { + val hash1 = it.proxy.uploadAttachment(bigFile1.inputStream) + val hash2 = it.proxy.uploadAttachment(bigFile2.inputStream) + val hash3 = it.proxy.uploadAttachment(bigFile3.inputStream) + val hash4 = it.proxy.uploadAttachment(bigFile4.inputStream) assertEquals(hash1, bigFile1.sha256) // Should not throw any exceptions. - it.startFlow(::SendLargeTransactionFlow, hash1, hash2, hash3, hash4).returnValue.get() + it.proxy.startFlow(::SendLargeTransactionFlow, hash1, hash2, hash3, hash4).returnValue.getOrThrow() } } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt deleted file mode 100644 index 6c050f24b8..0000000000 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt +++ /dev/null @@ -1,52 +0,0 @@ -@file:JvmName("DriverConstants") - -package net.corda.testing - -import net.corda.core.identity.Party -import net.corda.core.internal.concurrent.transpose -import net.corda.core.messaging.CordaRPCOps -import net.corda.nodeapi.User -import net.corda.testing.driver.DriverDSLExposedInterface - -// -// Extensions to the Driver DSL to auto-manufacture nodes by name. -// - -/** - * A simple wrapper for objects provided by the integration test driver DSL. The fields are lazy so - * node construction won't start until you access the members. You can get one of these from the - * [alice], [bob] and [aliceAndBob] functions. - */ -class PredefinedTestNode internal constructor(party: Party, driver: DriverDSLExposedInterface) { - val rpcUsers = listOf(User("admin", "admin", setOf("ALL"))) // TODO: Randomize? - val nodeFuture by lazy { driver.startNode(providedName = party.name, rpcUsers = rpcUsers) } - val node by lazy { nodeFuture.get()!! } - val rpc by lazy { node.rpcClientToNode() } - - fun useRPC(block: (CordaRPCOps) -> R) = rpc.use(rpcUsers[0].username, rpcUsers[0].password) { block(it.proxy) } -} - -// TODO: Probably we should inject the above keys through the driver to make the nodes use it, rather than have the warnings below. - -/** - * Returns a plain, entirely stock node pre-configured with the [ALICE] identity. Note that a random key will be generated - * for it: you won't have [ALICE_KEY]. - */ -fun DriverDSLExposedInterface.alice(): PredefinedTestNode = PredefinedTestNode(ALICE, this) - -/** - * Returns a plain, entirely stock node pre-configured with the [BOB] identity. Note that a random key will be generated - * for it: you won't have [BOB_KEY]. - */ -fun DriverDSLExposedInterface.bob(): PredefinedTestNode = PredefinedTestNode(BOB, this) - -/** - * Returns plain, entirely stock nodes pre-configured with the [ALICE] and [BOB] X.500 names in that order. They have been - * started up in parallel and are now ready to use. - */ -fun DriverDSLExposedInterface.aliceAndBob(): List { - val alice = alice() - val bob = bob() - listOf(alice.nodeFuture, bob.nodeFuture).transpose().get() - return listOf(alice, bob) -} From 07e46ddb3c0c0dabf292b39437647d015cde4ee3 Mon Sep 17 00:00:00 2001 From: szymonsztuka Date: Fri, 1 Dec 2017 00:02:11 +0000 Subject: [PATCH 26/27] Merge OS to Enterprise - fixes after merge: update SQL Server cash query, test in pertestcordapp module, platform version 1 --- constants.properties | 2 +- .../selection/CashSelectionSQLServerImpl.kt | 31 +-- .../internal/persistence/CordaPersistence.kt | 3 +- .../contracts/CommercialPaperTests.kt | 38 ++-- .../contracts/asset/CashTests.kt | 214 +++++++++--------- .../flows/TwoPartyTradeFlowTest.kt | 29 ++- .../corda/bank/api/BankOfCordaClientApi.kt | 1 - .../corda/notarydemo/RaftNotaryCordform.kt | 1 - .../net/corda/testing/node/MockServices.kt | 1 - 9 files changed, 160 insertions(+), 160 deletions(-) diff --git a/constants.properties b/constants.properties index 7ee707fd23..cb32304d74 100644 --- a/constants.properties +++ b/constants.properties @@ -1,6 +1,6 @@ gradlePluginsVersion=3.0.0 kotlinVersion=1.1.60 -platformVersion=2 +platformVersion=1 guavaVersion=21.0 bouncycastleVersion=1.57 typesafeConfigVersion=1.3.1 diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionSQLServerImpl.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionSQLServerImpl.kt index c0f6591e2f..4f89016fca 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionSQLServerImpl.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionSQLServerImpl.kt @@ -28,7 +28,7 @@ class CashSelectionSQLServerImpl : AbstractCashSelection() { override fun toString() = "${this::class.java} for $JDBC_DRIVER_NAME" override fun executeQuery(connection: Connection, amount: Amount, lockId: UUID, notary: Party?, - onlyFromIssuerParties: Set, withIssuerRefs: Set) : ResultSet { + onlyFromIssuerParties: Set, withIssuerRefs: Set, withResultSet: (ResultSet) -> Boolean): Boolean { val selectJoin = """ WITH row(transaction_id, output_index, pennies, total, lock_id) AS @@ -51,19 +51,22 @@ class CashSelectionSQLServerImpl : AbstractCashSelection() { FROM row where row.total <= ? + row.pennies""" // Use prepared statement for protection against SQL Injection - val psSelectJoin = connection.prepareStatement(selectJoin) - var pIndex = 0 - psSelectJoin.setString(++pIndex, amount.token.currencyCode) - psSelectJoin.setString(++pIndex, lockId.toString()) - if (notary != null) - psSelectJoin.setString(++pIndex, notary.name.toString()) - if (onlyFromIssuerParties.isNotEmpty()) - psSelectJoin.setObject(++pIndex, onlyFromIssuerParties.map { it.owningKey.toBase58String() as Any}.toTypedArray() ) - if (withIssuerRefs.isNotEmpty()) - psSelectJoin.setObject(++pIndex, withIssuerRefs.map { it.bytes as Any }.toTypedArray()) - psSelectJoin.setLong(++pIndex, amount.quantity) - log.debug(selectJoin) + connection.prepareStatement(selectJoin).use { statement -> + var pIndex = 0 + statement.setString(++pIndex, amount.token.currencyCode) + statement.setString(++pIndex, lockId.toString()) + if (notary != null) + statement.setString(++pIndex, notary.name.toString()) + if (onlyFromIssuerParties.isNotEmpty()) + statement.setObject(++pIndex, onlyFromIssuerParties.map { it.owningKey.toBase58String() as Any }.toTypedArray()) + if (withIssuerRefs.isNotEmpty()) + statement.setObject(++pIndex, withIssuerRefs.map { it.bytes as Any }.toTypedArray()) + statement.setLong(++pIndex, amount.quantity) + log.debug(selectJoin) - return psSelectJoin.executeQuery() + statement.executeQuery().use { rs -> + return withResultSet(rs) + } + } } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt index 30c2817fad..cce28ec508 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt @@ -20,7 +20,8 @@ const val NODE_DATABASE_PREFIX = "node_" data class DatabaseConfig( val initialiseSchema: Boolean = true, val serverNameTablePrefix: String = "", - val transactionIsolationLevel: TransactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ + val transactionIsolationLevel: TransactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ, + val schema: String? = null ) // This class forms part of the node config and so any changes to it must be handled with care diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/CommercialPaperTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/CommercialPaperTests.kt index e337b1c56b..e94ec7e72f 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/CommercialPaperTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/CommercialPaperTests.kt @@ -89,8 +89,8 @@ class CommercialPaperTestsGeneric { // Some CP is issued onto the ledger by MegaCorp. transaction("Issuance") { attachments(CP_PROGRAM_ID, CommercialPaper.CP_PROGRAM_ID) - output(thisTest.getContract(), "paper") { thisTest.getPaper() } - command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } + output(thisTest.getContract(), "paper", thisTest.getPaper()) + command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY)) timeWindow(TEST_TX_TIME) this.verifies() } @@ -101,10 +101,10 @@ class CommercialPaperTestsGeneric { attachments(Cash.PROGRAM_ID, CommercialPaper.CP_PROGRAM_ID) input("paper") input("alice's $900") - output(Cash.PROGRAM_ID, "borrowed $900") { 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP } - output(thisTest.getContract(), "alice's paper") { "paper".output().withOwner(ALICE) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } - command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() } + output(Cash.PROGRAM_ID, "borrowed $900", 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP) + output(thisTest.getContract(), "alice's paper", "paper".output().withOwner(ALICE)) + command(ALICE_PUBKEY, Cash.Commands.Move()) + command(MEGA_CORP_PUBKEY, thisTest.getMoveCommand()) this.verifies() } @@ -116,12 +116,12 @@ class CommercialPaperTestsGeneric { input("some profits") fun TransactionDSL.outputs(aliceGetsBack: Amount>) { - output(Cash.PROGRAM_ID, "Alice's profit") { aliceGetsBack.STATE ownedBy ALICE } - output(Cash.PROGRAM_ID, "Change") { (someProfits - aliceGetsBack).STATE ownedBy MEGA_CORP } + output(Cash.PROGRAM_ID, "Alice's profit", aliceGetsBack.STATE ownedBy ALICE) + output(Cash.PROGRAM_ID, "Change", (someProfits - aliceGetsBack).STATE ownedBy MEGA_CORP) } - command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } - command(ALICE_PUBKEY) { thisTest.getRedeemCommand(DUMMY_NOTARY) } + command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) + command(ALICE_PUBKEY, thisTest.getRedeemCommand(DUMMY_NOTARY)) tweak { outputs(700.DOLLARS `issued by` issuer) @@ -138,7 +138,7 @@ class CommercialPaperTestsGeneric { timeWindow(TEST_TX_TIME + 8.days) tweak { - output(thisTest.getContract()) { "paper".output() } + output(thisTest.getContract(), "paper".output()) this `fails with` "must be destroyed" } @@ -152,8 +152,8 @@ class CommercialPaperTestsGeneric { transaction { attachment(CP_PROGRAM_ID) attachment(CP_PROGRAM_ID) - output(thisTest.getContract()) { thisTest.getPaper() } - command(MINI_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } + output(thisTest.getContract(), thisTest.getPaper()) + command(MINI_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY)) timeWindow(TEST_TX_TIME) this `fails with` "output states are issued by a command signer" } @@ -164,8 +164,8 @@ class CommercialPaperTestsGeneric { transaction { attachment(CP_PROGRAM_ID) attachment(CP_PROGRAM_ID) - output(thisTest.getContract()) { thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer) } - command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } + output(thisTest.getContract(), thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer)) + command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY)) timeWindow(TEST_TX_TIME) this `fails with` "output values sum to more than the inputs" } @@ -176,8 +176,8 @@ class CommercialPaperTestsGeneric { transaction { attachment(CP_PROGRAM_ID) attachment(CP_PROGRAM_ID) - output(thisTest.getContract()) { thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days) } - command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } + output(thisTest.getContract(), thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days)) + command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY)) timeWindow(TEST_TX_TIME) this `fails with` "maturity date is not in the past" } @@ -189,8 +189,8 @@ class CommercialPaperTestsGeneric { attachment(CP_PROGRAM_ID) attachment(CP_PROGRAM_ID) input(thisTest.getContract(), thisTest.getPaper()) - output(thisTest.getContract()) { thisTest.getPaper() } - command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } + output(thisTest.getContract(), thisTest.getPaper()) + command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY)) timeWindow(TEST_TX_TIME) this `fails with` "output values sum to more than the inputs" } diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashTests.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashTests.kt index 241560ae4d..bdef97ea64 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashTests.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/contracts/asset/CashTests.kt @@ -22,10 +22,10 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.OpaqueBytes import net.corda.node.services.vault.NodeVaultService -import net.corda.node.utilities.CordaPersistence +import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.* import net.corda.testing.contracts.DummyState -import net.corda.testing.contracts.calculateRandomlySizedAmounts +import net.corda.testing.contracts.VaultFiller.Companion.calculateRandomlySizedAmounts import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices import org.junit.After @@ -133,34 +133,34 @@ class CashTests { fun trivial() = withTestSerialization { transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } + input(Cash.PROGRAM_ID, inState) tweak { - output(Cash.PROGRAM_ID) { outState.copy(amount = 2000.DOLLARS `issued by` defaultIssuer) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, outState.copy(amount = 2000.DOLLARS `issued by` defaultIssuer)) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } tweak { - output(Cash.PROGRAM_ID) { outState } - command(ALICE_PUBKEY) { DummyCommandData } + output(Cash.PROGRAM_ID, outState) + command(ALICE_PUBKEY, DummyCommandData) // Invalid command this `fails with` "required com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash.Commands.Move command" } tweak { - output(Cash.PROGRAM_ID) { outState } - command(BOB_PUBKEY) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, outState) + command(BOB_PUBKEY, Cash.Commands.Move()) this `fails with` "the owning keys are a subset of the signing keys" } tweak { - output(Cash.PROGRAM_ID) { outState } - output(Cash.PROGRAM_ID) { outState issuedBy MINI_CORP } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, outState) + output(Cash.PROGRAM_ID, outState issuedBy MINI_CORP) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "at least one cash input" } // Simple reallocation works. tweak { - output(Cash.PROGRAM_ID) { outState } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, outState) + command(ALICE_PUBKEY, Cash.Commands.Move()) this.verifies() } } @@ -172,9 +172,9 @@ class CashTests { // Check we can't "move" money into existence. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { DummyState() } - output(Cash.PROGRAM_ID) { outState } - command(MINI_CORP_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, DummyState()) + output(Cash.PROGRAM_ID, outState) + command(MINI_CORP_PUBKEY, Cash.Commands.Move()) this `fails with` "there is at least one cash input for this group" } @@ -187,19 +187,19 @@ class CashTests { // institution is allowed to issue as much cash as they want. transaction { attachment(Cash.PROGRAM_ID) - output(Cash.PROGRAM_ID) { outState } - command(ALICE_PUBKEY) { Cash.Commands.Issue() } + output(Cash.PROGRAM_ID, outState) + command(ALICE_PUBKEY, Cash.Commands.Issue()) this `fails with` "output states are issued by a command signer" } transaction { attachment(Cash.PROGRAM_ID) - output(Cash.PROGRAM_ID) { + output(Cash.PROGRAM_ID, Cash.State( amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = AnonymousParty(ALICE_PUBKEY) ) - } - command(MINI_CORP_PUBKEY) { Cash.Commands.Issue() } + ) + command(MINI_CORP_PUBKEY, Cash.Commands.Issue()) this.verifies() } Unit @@ -236,18 +236,18 @@ class CashTests { // We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { issuerInState } - output(Cash.PROGRAM_ID) { inState.copy(amount = inState.amount * 2) } + input(Cash.PROGRAM_ID, issuerInState) + output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount * 2)) // Move fails: not allowed to summon money. tweak { - command(ALICE_PUBKEY) { Cash.Commands.Move() } + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } // Issue works. tweak { - command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } + command(MEGA_CORP_PUBKEY, Cash.Commands.Issue()) this.verifies() } } @@ -255,29 +255,29 @@ class CashTests { // Can't use an issue command to lower the amount. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { inState.copy(amount = inState.amount.splitEvenly(2).first()) } - command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } + input(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount.splitEvenly(2).first())) + command(MEGA_CORP_PUBKEY, Cash.Commands.Issue()) this `fails with` "output values sum to more than the inputs" } // Can't have an issue command that doesn't actually issue money. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { inState } - command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } + input(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, inState) + command(MEGA_CORP_PUBKEY, Cash.Commands.Issue()) this `fails with` "output values sum to more than the inputs" } // Can't have any other commands if we have an issue command (because the issue command overrules them) transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { inState.copy(amount = inState.amount * 2) } - command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } + input(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount * 2)) + command(MEGA_CORP_PUBKEY, Cash.Commands.Issue()) tweak { - command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } + command(MEGA_CORP_PUBKEY, Cash.Commands.Issue()) this `fails with` "there is only a single issue command" } this.verifies() @@ -309,26 +309,26 @@ class CashTests { // Splitting value works. transaction { attachment(Cash.PROGRAM_ID) - command(ALICE_PUBKEY) { Cash.Commands.Move() } + command(ALICE_PUBKEY, Cash.Commands.Move()) tweak { - input(Cash.PROGRAM_ID) { inState } + input(Cash.PROGRAM_ID, inState) val splits4 = inState.amount.splitEvenly(4) - for (i in 0..3) output(Cash.PROGRAM_ID) { inState.copy(amount = splits4[i]) } + for (i in 0..3) output(Cash.PROGRAM_ID, inState.copy(amount = splits4[i])) this.verifies() } // Merging 4 inputs into 2 outputs works. tweak { val splits2 = inState.amount.splitEvenly(2) val splits4 = inState.amount.splitEvenly(4) - for (i in 0..3) input(Cash.PROGRAM_ID) { inState.copy(amount = splits4[i]) } - for (i in 0..1) output(Cash.PROGRAM_ID) { inState.copy(amount = splits2[i]) } + for (i in 0..3) input(Cash.PROGRAM_ID, inState.copy(amount = splits4[i])) + for (i in 0..1) output(Cash.PROGRAM_ID, inState.copy(amount = splits2[i])) this.verifies() } // Merging 2 inputs into 1 works. tweak { val splits2 = inState.amount.splitEvenly(2) - for (i in 0..1) input(Cash.PROGRAM_ID) { inState.copy(amount = splits2[i]) } - output(Cash.PROGRAM_ID) { inState } + for (i in 0..1) input(Cash.PROGRAM_ID, inState.copy(amount = splits2[i])) + output(Cash.PROGRAM_ID, inState) this.verifies() } } @@ -339,17 +339,17 @@ class CashTests { fun zeroSizedValues() = withTestSerialization { transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - input(Cash.PROGRAM_ID) { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + input(Cash.PROGRAM_ID, inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer)) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "zero sized inputs" } transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer)) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "zero sized outputs" } Unit @@ -360,58 +360,58 @@ class CashTests { // Can't change issuer. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { outState issuedBy MINI_CORP } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, outState issuedBy MINI_CORP) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } // Can't change deposit reference when splitting. transaction { attachment(Cash.PROGRAM_ID) val splits2 = inState.amount.splitEvenly(2) - input(Cash.PROGRAM_ID) { inState } - for (i in 0..1) output(Cash.PROGRAM_ID) { outState.copy(amount = splits2[i]).editDepositRef(i.toByte()) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + for (i in 0..1) output(Cash.PROGRAM_ID, outState.copy(amount = splits2[i]).editDepositRef(i.toByte())) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } // Can't mix currencies. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer) } - output(Cash.PROGRAM_ID) { outState.copy(amount = 200.POUNDS `issued by` defaultIssuer) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer)) + output(Cash.PROGRAM_ID, outState.copy(amount = 200.POUNDS `issued by` defaultIssuer)) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - input(Cash.PROGRAM_ID) { + input(Cash.PROGRAM_ID, inState) + input(Cash.PROGRAM_ID, inState.copy( amount = 150.POUNDS `issued by` defaultIssuer, owner = AnonymousParty(BOB_PUBKEY) ) - } - output(Cash.PROGRAM_ID) { outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + ) + output(Cash.PROGRAM_ID, outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer)) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } // Can't have superfluous input states from different issuers. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - input(Cash.PROGRAM_ID) { inState issuedBy MINI_CORP } - output(Cash.PROGRAM_ID) { outState } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + input(Cash.PROGRAM_ID, inState issuedBy MINI_CORP) + output(Cash.PROGRAM_ID, outState) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } // Can't combine two different deposits at the same issuer. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - input(Cash.PROGRAM_ID) { inState.editDepositRef(3) } - output(Cash.PROGRAM_ID) { outState.copy(amount = inState.amount * 2).editDepositRef(3) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + input(Cash.PROGRAM_ID, inState.editDepositRef(3)) + output(Cash.PROGRAM_ID, outState.copy(amount = inState.amount * 2).editDepositRef(3)) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "for reference [01]" } Unit @@ -422,21 +422,21 @@ class CashTests { // Single input/output straightforward case. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { issuerInState } - output(Cash.PROGRAM_ID) { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } + input(Cash.PROGRAM_ID, issuerInState) + output(Cash.PROGRAM_ID, issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer))) tweak { - command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(100.DOLLARS `issued by` defaultIssuer) } - command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } + command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(100.DOLLARS `issued by` defaultIssuer)) + command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } tweak { - command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer) } + command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer)) this `fails with` "required com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash.Commands.Move command" tweak { - command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } + command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) this.verifies() } } @@ -449,20 +449,20 @@ class CashTests { // Multi-issuer case. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { issuerInState } - input(Cash.PROGRAM_ID) { issuerInState.copy(owner = MINI_CORP) issuedBy MINI_CORP } + input(Cash.PROGRAM_ID, issuerInState) + input(Cash.PROGRAM_ID, issuerInState.copy(owner = MINI_CORP) issuedBy MINI_CORP) - output(Cash.PROGRAM_ID) { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) issuedBy MINI_CORP } - output(Cash.PROGRAM_ID) { issuerInState.copy(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } + output(Cash.PROGRAM_ID, issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) issuedBy MINI_CORP) + output(Cash.PROGRAM_ID, issuerInState.copy(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer))) - command(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY) { Cash.Commands.Move() } + command(listOf(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY), Cash.Commands.Move()) this `fails with` "the amounts balance" - command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer) } + command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer)) this `fails with` "the amounts balance" - command(MINI_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` MINI_CORP.ref(defaultRef)) } + command(MINI_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` MINI_CORP.ref(defaultRef))) this.verifies() } Unit @@ -473,10 +473,10 @@ class CashTests { // Single input/output straightforward case. transaction { attachment(Cash.PROGRAM_ID) - input(Cash.PROGRAM_ID) { inState } - output(Cash.PROGRAM_ID) { outState.copy(amount = inState.amount - (200.DOLLARS `issued by` defaultIssuer)) } - command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer) } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + output(Cash.PROGRAM_ID, outState.copy(amount = inState.amount - (200.DOLLARS `issued by` defaultIssuer))) + command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer)) + command(ALICE_PUBKEY, Cash.Commands.Move()) this `fails with` "the amounts balance" } Unit @@ -487,25 +487,25 @@ class CashTests { transaction { attachment(Cash.PROGRAM_ID) // Gather 2000 dollars from two different issuers. - input(Cash.PROGRAM_ID) { inState } - input(Cash.PROGRAM_ID) { inState issuedBy MINI_CORP } - command(ALICE_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState) + input(Cash.PROGRAM_ID, inState issuedBy MINI_CORP) + command(ALICE_PUBKEY, Cash.Commands.Move()) // Can't merge them together. tweak { - output(Cash.PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY), amount = 2000.DOLLARS `issued by` defaultIssuer) } + output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY), amount = 2000.DOLLARS `issued by` defaultIssuer)) this `fails with` "the amounts balance" } // Missing MiniCorp deposit tweak { - output(Cash.PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } - output(Cash.PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } + output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY))) + output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY))) this `fails with` "the amounts balance" } // This works. - output(Cash.PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } - output(Cash.PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) issuedBy MINI_CORP } + output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY))) + output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY)) issuedBy MINI_CORP) this.verifies() } Unit @@ -517,11 +517,11 @@ class CashTests { transaction { attachment(Cash.PROGRAM_ID) val pounds = Cash.State(658.POUNDS `issued by` MINI_CORP.ref(3, 4, 5), AnonymousParty(BOB_PUBKEY)) - input(Cash.PROGRAM_ID) { inState ownedBy AnonymousParty(ALICE_PUBKEY) } - input(Cash.PROGRAM_ID) { pounds } - output(Cash.PROGRAM_ID) { inState ownedBy AnonymousParty(BOB_PUBKEY) } - output(Cash.PROGRAM_ID) { pounds ownedBy AnonymousParty(ALICE_PUBKEY) } - command(ALICE_PUBKEY, BOB_PUBKEY) { Cash.Commands.Move() } + input(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(ALICE_PUBKEY)) + input(Cash.PROGRAM_ID, pounds) + output(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(BOB_PUBKEY)) + output(Cash.PROGRAM_ID, pounds ownedBy AnonymousParty(ALICE_PUBKEY)) + command(listOf(ALICE_PUBKEY, BOB_PUBKEY), Cash.Commands.Move()) this.verifies() } @@ -844,19 +844,19 @@ class CashTests { ledger(mockService) { unverifiedTransaction { attachment(Cash.PROGRAM_ID) - output(Cash.PROGRAM_ID, "MEGA_CORP cash") { + output(Cash.PROGRAM_ID, "MEGA_CORP cash", Cash.State( amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), owner = MEGA_CORP ) - } + ) } transaction { attachment(Cash.PROGRAM_ID) input("MEGA_CORP cash") output(Cash.PROGRAM_ID, "MEGA_CORP cash 2", "MEGA_CORP cash".output().copy(owner = AnonymousParty(ALICE_PUBKEY))) - command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } + command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) this.verifies() } @@ -866,7 +866,7 @@ class CashTests { input("MEGA_CORP cash") // We send it to another pubkey so that the transaction is not identical to the previous one output(Cash.PROGRAM_ID, "MEGA_CORP cash 3", "MEGA_CORP cash".output().copy(owner = ALICE)) - command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } + command(MEGA_CORP_PUBKEY, Cash.Commands.Move()) this.verifies() } this.fails() diff --git a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt index a144bd2f03..bc8b247fd2 100644 --- a/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt +++ b/perftestcordapp/src/test/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/TwoPartyTradeFlowTest.kt @@ -41,7 +41,7 @@ import net.corda.node.services.api.Checkpoint import net.corda.node.services.api.CheckpointStorage import net.corda.node.services.api.WritableTransactionStorage import net.corda.node.services.persistence.DBTransactionStorage -import net.corda.node.utilities.CordaPersistence +import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.* import net.corda.testing.node.* import org.assertj.core.api.Assertions.assertThat @@ -536,7 +536,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { sellerNode: StartedNode, buyerNode: StartedNode, assetToSell: StateAndRef): RunResult { - val buyerFlows: Observable> = buyerNode.internals.registerInitiatedFlow(BuyerAcceptor::class.java) + val buyerFlows: Observable> = buyerNode.registerInitiatedFlow(BuyerAcceptor::class.java) val firstBuyerFiber = buyerFlows.toFuture().map { it.stateMachine } val seller = SellerInitiator(buyer, notary, assetToSell, 1000.DOLLARS, anonymous) val sellerResult = sellerNode.services.startFlow(seller).resultFuture @@ -673,13 +673,13 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { // wants to sell to Bob. val eb1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { // Issued money to itself. - output(Cash.PROGRAM_ID, "elbonian money 1", notary = notary) { 800.DOLLARS.CASH issuedBy issuer ownedBy interimOwner } - output(Cash.PROGRAM_ID, "elbonian money 2", notary = notary) { 1000.DOLLARS.CASH issuedBy issuer ownedBy interimOwner } + output(Cash.PROGRAM_ID, "elbonian money 1", notary = notary, contractState = 800.DOLLARS.CASH issuedBy issuer ownedBy interimOwner) + output(Cash.PROGRAM_ID, "elbonian money 2", notary = notary, contractState = 1000.DOLLARS.CASH issuedBy issuer ownedBy interimOwner) if (!withError) { - command(issuer.party.owningKey) { Cash.Commands.Issue() } + command(issuer.party.owningKey, Cash.Commands.Issue()) } else { // Put a broken command on so at least a signature is created - command(issuer.party.owningKey) { Cash.Commands.Move() } + command(issuer.party.owningKey, Cash.Commands.Move()) } timeWindow(TEST_TX_TIME) if (withError) { @@ -692,16 +692,16 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { // Bob gets some cash onto the ledger from BoE val bc1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { input("elbonian money 1") - output(Cash.PROGRAM_ID, "bob cash 1", notary = notary) { 800.DOLLARS.CASH issuedBy issuer ownedBy owner } - command(interimOwner.owningKey) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, "bob cash 1", notary = notary, contractState = 800.DOLLARS.CASH issuedBy issuer ownedBy owner) + command(interimOwner.owningKey, Cash.Commands.Move()) this.verifies() } val bc2 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { input("elbonian money 2") - output(Cash.PROGRAM_ID, "bob cash 2", notary = notary) { 300.DOLLARS.CASH issuedBy issuer ownedBy owner } - output(Cash.PROGRAM_ID, notary = notary) { 700.DOLLARS.CASH issuedBy issuer ownedBy interimOwner } // Change output. - command(interimOwner.owningKey) { Cash.Commands.Move() } + output(Cash.PROGRAM_ID, "bob cash 2", notary = notary, contractState = 300.DOLLARS.CASH issuedBy issuer ownedBy owner) + output(Cash.PROGRAM_ID, notary = notary, contractState = 700.DOLLARS.CASH issuedBy issuer ownedBy interimOwner) // Change output. + command(interimOwner.owningKey, Cash.Commands.Move()) this.verifies() } @@ -717,10 +717,9 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { attachmentID: SecureHash?, notary: Party): Pair, List> { val ap = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { - output(CommercialPaper.CP_PROGRAM_ID, "alice's paper", notary = notary) { - CommercialPaper.State(issuer, owner, amount, TEST_TX_TIME + 7.days) - } - command(issuer.party.owningKey) { CommercialPaper.Commands.Issue() } + output(CommercialPaper.CP_PROGRAM_ID, "alice's paper", notary = notary, + contractState = CommercialPaper.State(issuer, owner, amount, TEST_TX_TIME + 7.days)) + command(issuer.party.owningKey, CommercialPaper.Commands.Issue()) if (!withError) timeWindow(time = TEST_TX_TIME) if (attachmentID != null) diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt index ac0a136f7c..85fe095d19 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt @@ -10,7 +10,6 @@ import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.testing.http.HttpApi -import java.util.* /** * Interface for communicating with Bank of Corda node 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 e6c6f54595..7ba39a8dea 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,7 +4,6 @@ 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.utilities.NetworkHostAndPort import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.RaftConfig 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 56a07cf6c5..13ccd7432c 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 @@ -26,7 +26,6 @@ import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage import net.corda.node.services.api.VaultServiceInternal import net.corda.node.services.api.WritableTransactionStorage import net.corda.node.services.config.configOf -import net.corda.node.services.config.DatabaseConfig import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.services.keys.freshCertificate import net.corda.node.services.keys.getSigner From 6f288b583361e0c5e744872c3f84105fd40d65fc Mon Sep 17 00:00:00 2001 From: szymonsztuka Date: Fri, 1 Dec 2017 09:36:14 +0000 Subject: [PATCH 27/27] Merge OS to Enterprise - added note about Enterprise specific Gradle wrapper version --- gradle/wrapper/gradle-wrapper.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index dd7948e732..7904303d93 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +# Gradle >= 4.3.1 required for Enterprise only experimental:intellij-plugin distributionUrl=https\://services.gradle.org/distributions/gradle-4.3.1-all.zip