From decdcbefa6c974782d7e07f278360b8c0fe882d6 Mon Sep 17 00:00:00 2001 From: Christian Sailer Date: Fri, 25 Sep 2020 15:54:31 +0100 Subject: [PATCH] Add serialization env to OwnerMigration (#212) * Make redeem flow handlers open (#199) * Make redeem flow handlers open to allow adding custom processing logic for redeemed tokens on the issuer`s side. Make ObserverAwareFinalityFlowHandler return a SignedTransaction if one was received. * Undo removing a flow Co-authored-by: Stefano Franz * Put Workflows Jar on a diet - exclude all corda runtime files (#206) * keep * put workflows jar on a bit of a diet * Add serialization env to OwnerMigration * fix migration for 4.6 Co-authored-by: Andrius Dagys Co-authored-by: Stefano Franz Co-authored-by: Stefano Franz --- build.gradle | 6 +- contracts/build.gradle | 5 +- .../lib/tokens/contracts/CommonTokens.kt | 17 ++ .../tokens/contracts/FungibleTokenTests.kt | 40 ++--- .../tokens/contracts/NonFungibleTokenTests.kt | 9 +- .../lib/tokens/contracts/TokenPointerTests.kt | 5 +- freighter-tests/build.gradle | 14 +- .../testing/TokenSDKDBCompatibility.kt | 18 +- .../testing/TokenSDKUpgrade46Compatibility.kt | 162 ++++++++++++++++++ .../testing/TokenSDKUpgradeDBCompatibility.kt | 13 +- modules/contracts-for-testing/build.gradle | 1 - modules/money/build.gradle | 57 ------ settings.gradle | 2 - workflows/build.gradle | 17 +- .../tokens/integrationTest/TokenDriverTest.kt | 4 - .../corda/lib/tokens/money/DigitalCurrency.kt | 0 .../r3/corda/lib/tokens/money/FiatCurrency.kt | 0 .../r3/corda/lib/tokens/money/Utilities.kt | 0 .../lib/tokens/workflows/OwnerMigration.kt | 98 ++++++++--- .../CreateEvolvableTokensFlowHandler.kt | 2 +- .../UpdateEvolvableTokenFlowHandler.kt | 2 +- .../flows/redeem/RedeemTokensFlowHandler.kt | 8 +- .../workflows/flows/rpc/RedeemTokens.kt | 20 ++- .../ObserverAwareFinalityFlowHandler.kt | 9 +- .../money/CurrencyAccessFromJavaTest.java | 0 25 files changed, 335 insertions(+), 174 deletions(-) create mode 100644 contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/CommonTokens.kt create mode 100644 freighter-tests/src/freighterTest/kotlin/freighter/testing/TokenSDKUpgrade46Compatibility.kt delete mode 100644 modules/money/build.gradle rename {modules/money => workflows}/src/main/kotlin/com/r3/corda/lib/tokens/money/DigitalCurrency.kt (100%) rename {modules/money => workflows}/src/main/kotlin/com/r3/corda/lib/tokens/money/FiatCurrency.kt (100%) rename {modules/money => workflows}/src/main/kotlin/com/r3/corda/lib/tokens/money/Utilities.kt (100%) rename {modules/money => workflows}/src/test/java/com/r3/corda/lib/tokens/money/CurrencyAccessFromJavaTest.java (100%) diff --git a/build.gradle b/build.gradle index db2127c7..3cd3f3e2 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ buildscript { corda_release_group = 'net.corda' corda_release_version = '4.3' tokens_release_group = "com.r3.corda.lib.tokens" - tokens_release_version = "1.2" - corda_gradle_plugins_version = '5.0.8' + tokens_release_version = "1.2.1-SNAPSHOT" + corda_gradle_plugins_version = '5.0.12' kotlin_version = '1.2.71' junit_version = '4.12' dokka_version = '0.9.17' @@ -25,6 +25,7 @@ buildscript { mavenCentral() mavenLocal() maven { url "http://ci-artifactory.corda.r3cev.com/artifactory/corda-releases" } + maven { url "http://ci-artifactory.corda.r3cev.com/artifactory/corda-dependencies" } maven { url "https://repo.gradle.org/gradle/libs-releases-local/" } } @@ -72,6 +73,7 @@ subprojects { maven { url "http://ci-artifactory.corda.r3cev.com/artifactory/corda-lib-dev" } maven { url "http://ci-artifactory.corda.r3cev.com/artifactory/corda-lib" } maven { url "https://repo.gradle.org/gradle/libs-releases-local/" } + maven { url "http://ci-artifactory.corda.r3cev.com/artifactory/corda-dependencies" } } apply plugin: 'kotlin' diff --git a/contracts/build.gradle b/contracts/build.gradle index 6ea16d86..21e20c66 100644 --- a/contracts/build.gradle +++ b/contracts/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'kotlin-jpa' apply plugin: 'net.corda.plugins.cordapp' if (!(corda_release_version in ['4.1'])) { -apply from: "${rootProject.projectDir}/deterministic.gradle" + apply from: "${rootProject.projectDir}/deterministic.gradle" } sourceSets { @@ -23,7 +23,7 @@ dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" // Corda dependencies. - cordaCompile ("$corda_release_group:corda-core:$corda_release_version"){ + cordaCompile("$corda_release_group:corda-core:$corda_release_version") { changing = true } @@ -34,7 +34,6 @@ dependencies { testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" testCompile "junit:junit:$junit_version" testCompile "$corda_release_group:corda-node-driver:$corda_release_version" - testCompile project(":modules:money") testCompile project(":modules:contracts-for-testing") } diff --git a/contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/CommonTokens.kt b/contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/CommonTokens.kt new file mode 100644 index 00000000..048bd14c --- /dev/null +++ b/contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/CommonTokens.kt @@ -0,0 +1,17 @@ +package com.r3.corda.lib.tokens.contracts + +import com.r3.corda.lib.tokens.contracts.types.TokenType +import net.corda.core.contracts.Amount + +class CommonTokens { + + companion object { + val USD = TokenType("USD", 2) + val GBP = TokenType("GBP", 2) + } + +} + +fun Number.ofType(tt: TokenType): Amount { + return Amount(this.toLong(), tt) +} \ No newline at end of file diff --git a/contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/FungibleTokenTests.kt b/contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/FungibleTokenTests.kt index c91d9963..6c6a7dbf 100644 --- a/contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/FungibleTokenTests.kt +++ b/contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/FungibleTokenTests.kt @@ -8,12 +8,12 @@ import com.r3.corda.lib.tokens.contracts.utilities.getAttachmentIdForGenericPara import com.r3.corda.lib.tokens.contracts.utilities.heldBy import com.r3.corda.lib.tokens.contracts.utilities.issuedBy import com.r3.corda.lib.tokens.contracts.utilities.of -import com.r3.corda.lib.tokens.money.GBP -import com.r3.corda.lib.tokens.money.USD import com.r3.corda.lib.tokens.testing.states.DodgeToken import com.r3.corda.lib.tokens.testing.states.DodgeTokenContract import com.r3.corda.lib.tokens.testing.states.RUB import com.r3.corda.lib.tokens.testing.states.RubleToken +import com.r3.corda.lib.tokens.contracts.CommonTokens.Companion.USD +import com.r3.corda.lib.tokens.contracts.CommonTokens.Companion.GBP import org.junit.Test // TODO: Some of these tests are testing AbstractToken and should be moved into the super-class. @@ -58,7 +58,7 @@ class FungibleTokenTests : ContractTestCommon() { } // Includes a group with no assigned command. tweak { - output(FungibleTokenContract.contractId, 10.USD issuedBy ISSUER.party heldBy ALICE.party) + output(FungibleTokenContract.contractId, 10.ofType(USD) issuedBy ISSUER.party heldBy ALICE.party) command(ISSUER.publicKey, IssueTokenCommand(issuedToken, listOf(0))) this `fails with` "There is a token group with no assigned command!" } @@ -101,7 +101,7 @@ class FungibleTokenTests : ContractTestCommon() { // Includes the same token issued by a different issuer. // You wouldn't usually do this but it is possible. tweak { - output(FungibleTokenContract.contractId, 1.GBP issuedBy BOB.party heldBy ALICE.party) + output(FungibleTokenContract.contractId, 1.ofType(GBP) issuedBy BOB.party heldBy ALICE.party) command(ISSUER.publicKey, IssueTokenCommand(issuedToken, listOf(0))) command(BOB.publicKey, IssueTokenCommand(GBP issuedBy BOB.party, listOf(1))) verifies() @@ -131,7 +131,7 @@ class FungibleTokenTests : ContractTestCommon() { // Move coupled with an issue. tweak { - output(FungibleTokenContract.contractId, 10.USD issuedBy BOB.party heldBy ALICE.party) + output(FungibleTokenContract.contractId, 10.ofType(USD) issuedBy BOB.party heldBy ALICE.party) //the issue token is added after the move tokens, so it will have index(1) command(BOB.publicKey, IssueTokenCommand(USD issuedBy BOB.party, outputs = listOf(1))) @@ -140,7 +140,7 @@ class FungibleTokenTests : ContractTestCommon() { // Input missing. tweak { - output(FungibleTokenContract.contractId, 10.USD issuedBy BOB.party heldBy BOB.party) + output(FungibleTokenContract.contractId, 10.ofType(USD) issuedBy BOB.party heldBy BOB.party) command(ALICE.publicKey, MoveTokenCommand(USD issuedBy BOB.party, outputs = listOf(1))) this `fails with` "When moving tokens, there must be input states present." @@ -148,7 +148,7 @@ class FungibleTokenTests : ContractTestCommon() { // Output missing. tweak { - input(FungibleTokenContract.contractId, 10.USD issuedBy BOB.party heldBy ALICE.party) + input(FungibleTokenContract.contractId, 10.ofType(USD) issuedBy BOB.party heldBy ALICE.party) command(ALICE.publicKey, MoveTokenCommand(USD issuedBy BOB.party, inputs = listOf(1))) this `fails with` "When moving tokens, there must be output states present." @@ -156,9 +156,9 @@ class FungibleTokenTests : ContractTestCommon() { // Inputs sum to zero. tweak { - input(FungibleTokenContract.contractId, 0.USD issuedBy BOB.party heldBy ALICE.party) - input(FungibleTokenContract.contractId, 0.USD issuedBy BOB.party heldBy ALICE.party) - output(FungibleTokenContract.contractId, 10.USD issuedBy BOB.party heldBy BOB.party) + input(FungibleTokenContract.contractId, 0.ofType(USD) issuedBy BOB.party heldBy ALICE.party) + input(FungibleTokenContract.contractId, 0.ofType(USD) issuedBy BOB.party heldBy ALICE.party) + output(FungibleTokenContract.contractId, 10.ofType(USD) issuedBy BOB.party heldBy BOB.party) command(ALICE.publicKey, MoveTokenCommand(USD issuedBy BOB.party, inputs = listOf(1, 2), outputs = listOf(1))) // Command for the move. this `fails with` "In move groups there must be an amount of input tokens > ZERO." @@ -166,9 +166,9 @@ class FungibleTokenTests : ContractTestCommon() { // Outputs sum to zero. tweak { - input(FungibleTokenContract.contractId, 10.USD issuedBy BOB.party heldBy ALICE.party) - output(FungibleTokenContract.contractId, 0.USD issuedBy BOB.party heldBy BOB.party) - output(FungibleTokenContract.contractId, 0.USD issuedBy BOB.party heldBy BOB.party) + input(FungibleTokenContract.contractId, 10.ofType(USD) issuedBy BOB.party heldBy ALICE.party) + output(FungibleTokenContract.contractId, 0.ofType(USD) issuedBy BOB.party heldBy BOB.party) + output(FungibleTokenContract.contractId, 0.ofType(USD) issuedBy BOB.party heldBy BOB.party) command(ALICE.publicKey, MoveTokenCommand(USD issuedBy BOB.party, inputs = listOf(1), outputs = listOf(1, 2))) // Command for the move. this `fails with` "In move groups there must be an amount of output tokens > ZERO." @@ -176,8 +176,8 @@ class FungibleTokenTests : ContractTestCommon() { // Unbalanced move. tweak { - input(FungibleTokenContract.contractId, 10.USD issuedBy BOB.party heldBy ALICE.party) - output(FungibleTokenContract.contractId, 11.USD issuedBy BOB.party heldBy BOB.party) + input(FungibleTokenContract.contractId, 10.ofType(USD) issuedBy BOB.party heldBy ALICE.party) + output(FungibleTokenContract.contractId, 11.ofType(USD) issuedBy BOB.party heldBy BOB.party) command(ALICE.publicKey, MoveTokenCommand(USD issuedBy BOB.party, inputs = listOf(1), outputs = listOf(1))) // Command for the move. this `fails with` "In move groups the amount of input tokens MUST EQUAL the amount of output tokens. " + @@ -185,9 +185,9 @@ class FungibleTokenTests : ContractTestCommon() { } tweak { - input(FungibleTokenContract.contractId, 10.USD issuedBy BOB.party heldBy ALICE.party) - output(FungibleTokenContract.contractId, 10.USD issuedBy BOB.party heldBy BOB.party) - output(FungibleTokenContract.contractId, 0.USD issuedBy BOB.party heldBy BOB.party) + input(FungibleTokenContract.contractId, 10.ofType(USD) issuedBy BOB.party heldBy ALICE.party) + output(FungibleTokenContract.contractId, 10.ofType(USD) issuedBy BOB.party heldBy BOB.party) + output(FungibleTokenContract.contractId, 0.ofType(USD) issuedBy BOB.party heldBy BOB.party) command(ALICE.publicKey, MoveTokenCommand(USD issuedBy BOB.party, inputs = listOf(1), outputs = listOf(1, 2))) // Command for the move. this `fails with` "You cannot create output token amounts with a ZERO amount." @@ -195,8 +195,8 @@ class FungibleTokenTests : ContractTestCommon() { // Two moves (two different groups). tweak { - input(FungibleTokenContract.contractId, 10.USD issuedBy BOB.party heldBy ALICE.party) - output(FungibleTokenContract.contractId, 10.USD issuedBy BOB.party heldBy BOB.party) + input(FungibleTokenContract.contractId, 10.ofType(USD) issuedBy BOB.party heldBy ALICE.party) + output(FungibleTokenContract.contractId, 10.ofType(USD) issuedBy BOB.party heldBy BOB.party) command(ALICE.publicKey, MoveTokenCommand(USD issuedBy BOB.party, inputs = listOf(1), outputs = listOf(1))) // Command for the move. verifies() diff --git a/contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/NonFungibleTokenTests.kt b/contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/NonFungibleTokenTests.kt index e6805037..0bbb5563 100644 --- a/contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/NonFungibleTokenTests.kt +++ b/contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/NonFungibleTokenTests.kt @@ -7,7 +7,6 @@ import com.r3.corda.lib.tokens.contracts.utilities.heldBy import com.r3.corda.lib.tokens.contracts.utilities.issuedBy import com.r3.corda.lib.tokens.contracts.utilities.of import com.r3.corda.lib.tokens.contracts.utilities.withNewHolder -import com.r3.corda.lib.tokens.money.USD import com.r3.corda.lib.tokens.testing.states.PTK import com.r3.corda.lib.tokens.testing.states.RUB import org.junit.Test @@ -58,7 +57,7 @@ class NonFungibleTokenTests : ContractTestCommon() { } // Includes a group with no assigned command. tweak { - output(FungibleTokenContract.contractId, 10.USD issuedBy ISSUER.party heldBy ALICE.party) + output(FungibleTokenContract.contractId, 10.ofType(CommonTokens.USD) issuedBy ISSUER.party heldBy ALICE.party) command(ISSUER.publicKey, IssueTokenCommand(issuedToken, outputs = listOf(0))) this `fails with` "There is a token group with no assigned command!" } @@ -71,7 +70,7 @@ class NonFungibleTokenTests : ContractTestCommon() { // Includes another token type and a matching command. tweak { - val otherToken = USD issuedBy ISSUER.party + val otherToken = CommonTokens.USD issuedBy ISSUER.party output(FungibleTokenContract.contractId, 10 of otherToken heldBy ALICE.party) command(ISSUER.publicKey, IssueTokenCommand(issuedToken, outputs = listOf(0))) command(ISSUER.publicKey, IssueTokenCommand(otherToken, outputs = listOf(1))) @@ -118,8 +117,8 @@ class NonFungibleTokenTests : ContractTestCommon() { // Move coupled with an issue. tweak { - output(FungibleTokenContract.contractId, 10.USD issuedBy BOB.party heldBy ALICE.party) - command(BOB.publicKey, IssueTokenCommand(USD issuedBy BOB.party, outputs = listOf(1))) + output(FungibleTokenContract.contractId, 10.ofType(CommonTokens.USD) issuedBy BOB.party heldBy ALICE.party) + command(BOB.publicKey, IssueTokenCommand(CommonTokens.USD issuedBy BOB.party, outputs = listOf(1))) // Command for the move. command(ALICE.publicKey, MoveTokenCommand(issuedToken, inputs = listOf(0), outputs = listOf(0))) verifies() diff --git a/contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/TokenPointerTests.kt b/contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/TokenPointerTests.kt index 5a56bfac..5dce26f6 100644 --- a/contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/TokenPointerTests.kt +++ b/contracts/src/test/kotlin/com/r3/corda/lib/tokens/contracts/TokenPointerTests.kt @@ -6,7 +6,6 @@ import com.r3.corda.lib.tokens.contracts.states.FungibleToken import com.r3.corda.lib.tokens.contracts.states.NonFungibleToken import com.r3.corda.lib.tokens.contracts.types.TokenPointer import com.r3.corda.lib.tokens.contracts.utilities.* -import com.r3.corda.lib.tokens.money.GBP import net.corda.core.crypto.Crypto import net.corda.core.crypto.SignableData import net.corda.core.crypto.SignatureMetadata @@ -57,8 +56,8 @@ class TokenPointerTests : ContractTestCommon() { fun `tokenTypeJarHash must be not null if tokenType is not a pointer`() { val pointer: TokenPointer = TestEvolvableTokenType(listOf(ALICE.party)).toPointer() val pointerToken: NonFungibleToken = pointer issuedBy ISSUER.party heldBy ALICE.party - val staticToken: NonFungibleToken = GBP issuedBy ISSUER.party heldBy ALICE.party + val staticToken: NonFungibleToken = CommonTokens.GBP issuedBy ISSUER.party heldBy ALICE.party assertEquals(pointerToken.tokenTypeJarHash, null) - assertEquals(staticToken.tokenTypeJarHash, GBP.getAttachmentIdForGenericParam()) + assertEquals(staticToken.tokenTypeJarHash, CommonTokens.GBP.getAttachmentIdForGenericParam()) } } \ No newline at end of file diff --git a/freighter-tests/build.gradle b/freighter-tests/build.gradle index 5639453e..135f778e 100644 --- a/freighter-tests/build.gradle +++ b/freighter-tests/build.gradle @@ -20,21 +20,13 @@ sourceSets { evaluationDependsOn(":workflows") task freighterTest(type: Test, dependsOn: [project(":workflows").jar]) { - maxParallelForks 2 + maxParallelForks 1 systemProperty "java.util.concurrent.ForkJoinPool.common.parallelism", "128" testClassesDirs = sourceSets.freighterTest.output.classesDirs classpath = sourceSets.freighterTest.runtimeClasspath useJUnitPlatform { includeTags "DOCKER" - excludeTags "AZURE", "ORACLE" - if (Os.isFamily(Os.FAMILY_WINDOWS) || Os.isFamily(Os.FAMILY_MAC)) { - //windows + mac cannot run comcast tests and cannot run oracle tests due to credential store issues - excludeTags "FULL_LINUX_KERNEL", "ORACLE" - } - - if (Os.isFamily(Os.FAMILY_UNIX) && "uname -a".execute().getText().contains("microsoft")) { - excludeTags "FULL_LINUX_KERNEL" - } + excludeTags "AZURE", "FULL_LINUX_KERNEL", "ORACLE" } } @@ -45,7 +37,7 @@ configurations { dependencies { freighterTestCompile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - freighterTestCompile "freighter:freighter-testing-core-junit5:0.4.5-SNAPSHOT" + freighterTestCompile "freighter:freighter-testing-core-junit5:0.7.1-SNAPSHOT" freighterTestCompile project(":contracts") freighterTestCompile project(":workflows") diff --git a/freighter-tests/src/freighterTest/kotlin/freighter/testing/TokenSDKDBCompatibility.kt b/freighter-tests/src/freighterTest/kotlin/freighter/testing/TokenSDKDBCompatibility.kt index 84ebe18d..283f5346 100644 --- a/freighter-tests/src/freighterTest/kotlin/freighter/testing/TokenSDKDBCompatibility.kt +++ b/freighter-tests/src/freighterTest/kotlin/freighter/testing/TokenSDKDBCompatibility.kt @@ -3,33 +3,19 @@ package freighter.testing import com.r3.corda.lib.tokens.contracts.states.FungibleToken import com.r3.corda.lib.tokens.contracts.types.IssuedTokenType import com.r3.corda.lib.tokens.contracts.types.TokenType -import com.r3.corda.lib.tokens.workflows.OwnerMigration import com.r3.corda.lib.tokens.workflows.flows.rpc.IssueTokens import freighter.deployments.DeploymentContext import freighter.deployments.NodeBuilder import freighter.deployments.SingleNodeDeployment +import freighter.deployments.UnitOfDeployment.Companion.CORDA_4_3 import freighter.machine.DeploymentMachineProvider import freighter.machine.generateRandomString -import liquibase.database.DatabaseConnection -import liquibase.database.core.PostgresDatabase import net.corda.core.contracts.Amount import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow -import net.corda.nodeapi.internal.persistence.DatabaseConfig -import org.hamcrest.MatcherAssert -import org.hamcrest.core.Is -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import utility.LoggingUtils -import utility.Retry -import utility.getArtifactAndDependencies import utility.getOrThrow -import java.net.URLClassLoader -import java.sql.Connection -import java.sql.Driver import java.time.Duration -import java.util.* -import java.util.concurrent.CompletableFuture class TokenSDKDBCompatibility : DockerRemoteMachineBasedTest() { @@ -90,7 +76,7 @@ class TokenSDKDBCompatibility : DockerRemoteMachineBasedTest() { .withCordapp(tokenSelection) .withCordapp(modernCiV1) .withDatabase(machineProvider.requestDatabase(db)) - ).withVersion("4.3") + ).withVersion(CORDA_4_3) .deploy(deploymentContext) val nodeMachine = deploymentResult.getOrThrow().nodeMachines.single() diff --git a/freighter-tests/src/freighterTest/kotlin/freighter/testing/TokenSDKUpgrade46Compatibility.kt b/freighter-tests/src/freighterTest/kotlin/freighter/testing/TokenSDKUpgrade46Compatibility.kt new file mode 100644 index 00000000..164a749e --- /dev/null +++ b/freighter-tests/src/freighterTest/kotlin/freighter/testing/TokenSDKUpgrade46Compatibility.kt @@ -0,0 +1,162 @@ +package freighter.testing + +import com.r3.corda.lib.tokens.contracts.states.FungibleToken +import com.r3.corda.lib.tokens.contracts.types.IssuedTokenType +import com.r3.corda.lib.tokens.contracts.types.TokenType +import com.r3.corda.lib.tokens.workflows.flows.rpc.IssueTokens +import com.r3.corda.lib.tokens.workflows.flows.rpc.MoveFungibleTokens +import com.r3.corda.lib.tokens.workflows.utilities.heldTokenAmountCriteria +import com.stress.flows.CreateNewCIFlow +import freighter.deployments.DeploymentContext +import freighter.deployments.NodeBuilder +import freighter.deployments.SingleNodeDeployment +import freighter.deployments.UnitOfDeployment +import freighter.machine.DeploymentMachineProvider +import freighter.machine.generateRandomString +import net.corda.core.contracts.Amount +import net.corda.core.internal.sumByLong +import net.corda.core.messaging.startFlow +import net.corda.core.utilities.getOrThrow +import org.hamcrest.MatcherAssert +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.containsInAnyOrder +import org.junit.jupiter.api.Test +import utility.getOrThrow +import java.time.Duration + +class TokenSDKUpgrade46Compatibility : DockerRemoteMachineBasedTest() { + + //remove the prod key and sign with freighter key + val tokensV1Contracts = NodeBuilder.DeployedCordapp.fromGradleArtifact( + group = "com.r3.corda.lib.tokens", + artifact = "tokens-contracts", + version = "1.1" + ).signedWithFreighterKey() + + val tokensV1Workflows = NodeBuilder.DeployedCordapp.fromGradleArtifact( + group = "com.r3.corda.lib.tokens", + artifact = "tokens-workflows", + version = "1.1" + ) + + val tokensV1Selection = NodeBuilder.DeployedCordapp.fromGradleArtifact( + group = "com.r3.corda.lib.tokens", + artifact = "tokens-selection", + version = "1.1" + ) + + //we must sign with the same freighter key to allow contract upgrade from 1.1 + val tokenCurrentContracts = + NodeBuilder.DeployedCordapp.fromClassPath("tokens-contracts").signedWithFreighterKey() + + val tokenCurrentWorkflows = + NodeBuilder.DeployedCordapp.fromClassPath("tokens-workflows") + + val modernCiV1 = NodeBuilder.DeployedCordapp.fromGradleArtifact( + group = "com.r3.corda.lib.ci", + artifact = "ci-workflows", + version = "1.0" + ) + + val freighterHelperCordapp = NodeBuilder.DeployedCordapp.fromClassPath("freighter-cordapp-flows") + + @Test + fun `tokens can be upgraded on a node running postgres 9_6`() { + runTokensOnNodeRunningDatabase(DeploymentMachineProvider.DatabaseType.PG_9_6) + } + + @Test + fun `tokens can be upgraded on a node running postgres H2`() { + runTokensOnNodeRunningDatabase(DeploymentMachineProvider.DatabaseType.H2) + } + + @Test + fun `tokens can be upgraded on a node running postgres 10_10`() { + runTokensOnNodeRunningDatabase(DeploymentMachineProvider.DatabaseType.PG_10_10) + } + + @Test + fun `tokens can be upgraded on a node running postgres 11_5`() { + runTokensOnNodeRunningDatabase(DeploymentMachineProvider.DatabaseType.PG_11_5) + } + + @Test + fun `tokens can be upgraded on a node running ms_sql`() { + runTokensOnNodeRunningDatabase(DeploymentMachineProvider.DatabaseType.MS_SQL) + } + + @Test + @OracleTest + fun `tokens can be upgraded on a node running oracle 12 r2`() { + runTokensOnNodeRunningDatabase(DeploymentMachineProvider.DatabaseType.ORACLE_12_R2) + } + + private fun runTokensOnNodeRunningDatabase(db: DeploymentMachineProvider.DatabaseType) { + val randomString = generateRandomString() + val deploymentContext = DeploymentContext(machineProvider, nms, artifactoryUsername, artifactoryPassword) + val deploymentResult = SingleNodeDeployment( + NodeBuilder().withX500("O=PartyB, C=GB, L=LONDON, CN=$randomString") + .withCordapp(tokensV1Contracts) + .withCordapp(tokensV1Workflows) + .withCordapp(tokensV1Selection) + .withCordapp(modernCiV1) + .withCordapp(freighterHelperCordapp) + .withDatabase(machineProvider.requestDatabase(db)) + ).withVersion(UnitOfDeployment.CORDA_4_6_SNAPSHOT) + .deploy(deploymentContext) + + val nodeMachine = deploymentResult.getOrThrow().nodeMachines.single() + val ourIdentity = nodeMachine.rpc { nodeInfo().legalIdentities.first() } + + + val tokenType = TokenType("StefCoin", 2) + val issuedTokenType = IssuedTokenType(ourIdentity, tokenType) + val amount = Amount(100_000_000, issuedTokenType) + val tokenToIssue1 = FungibleToken(amount, ourIdentity) + val tokenToIssue2 = FungibleToken(amount, ourIdentity) + + val issueTx = nodeMachine.rpc { + startFlow( + ::IssueTokens, + listOf(tokenToIssue1, tokenToIssue2), emptyList() + ).returnValue.getOrThrow(Duration.ofMinutes(1)) + } + + + nodeMachine.stopNode() + nodeMachine.upgradeCordapp(tokensV1Contracts, tokenCurrentContracts) + nodeMachine.upgradeCordapp(tokensV1Workflows, tokenCurrentWorkflows) + //selection should be fat-jarred into tokens-workflows + nodeMachine.deleteCordapp(tokensV1Selection) + nodeMachine.startNode() + + nodeMachine.rpc { + val keyCriteria = heldTokenAmountCriteria(tokenType, ourIdentity) + val actual = vaultQueryByCriteria(keyCriteria, FungibleToken::class.java).states.map { it.state.data } + MatcherAssert.assertThat(actual, containsInAnyOrder(tokenToIssue1, tokenToIssue2)) + } + + val createdCi = nodeMachine.rpc { + startFlow(::CreateNewCIFlow).returnValue.getOrThrow().also { + println("Successfully created CI: $it") + } + } + + val moveToCi = nodeMachine.rpc { + startFlow( + ::MoveFungibleTokens, + Amount(50_000_000, tokenType), + createdCi + ).returnValue.getOrThrow(Duration.ofMinutes(1)) + } + + nodeMachine.rpc { + val keyCriteria = heldTokenAmountCriteria(tokenType, createdCi) + val actual = vaultQueryByCriteria(keyCriteria, FungibleToken::class.java).states.map { it.state.data } + MatcherAssert.assertThat(actual.map { it.amount }.sumByLong { it.quantity }, `is`(50_000_000L)) + } + + } + + +} \ No newline at end of file diff --git a/freighter-tests/src/freighterTest/kotlin/freighter/testing/TokenSDKUpgradeDBCompatibility.kt b/freighter-tests/src/freighterTest/kotlin/freighter/testing/TokenSDKUpgradeDBCompatibility.kt index 86662d74..69546a2a 100644 --- a/freighter-tests/src/freighterTest/kotlin/freighter/testing/TokenSDKUpgradeDBCompatibility.kt +++ b/freighter-tests/src/freighterTest/kotlin/freighter/testing/TokenSDKUpgradeDBCompatibility.kt @@ -10,6 +10,7 @@ import com.stress.flows.CreateNewCIFlow import freighter.deployments.DeploymentContext import freighter.deployments.NodeBuilder import freighter.deployments.SingleNodeDeployment +import freighter.deployments.UnitOfDeployment import freighter.machine.DeploymentMachineProvider import freighter.machine.generateRandomString import net.corda.core.contracts.Amount @@ -17,12 +18,11 @@ import net.corda.core.internal.sumByLong import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow import org.hamcrest.MatcherAssert -import org.hamcrest.Matchers.* -import org.junit.jupiter.api.BeforeEach +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.containsInAnyOrder import org.junit.jupiter.api.Test import utility.getOrThrow import java.time.Duration -import java.util.concurrent.CompletableFuture class TokenSDKUpgradeDBCompatibility : DockerRemoteMachineBasedTest() { @@ -65,6 +65,11 @@ class TokenSDKUpgradeDBCompatibility : DockerRemoteMachineBasedTest() { runTokensOnNodeRunningDatabase(DeploymentMachineProvider.DatabaseType.PG_9_6) } + @Test + fun `tokens can be upgraded on a node running postgres H2`() { + runTokensOnNodeRunningDatabase(DeploymentMachineProvider.DatabaseType.H2) + } + @Test fun `tokens can be upgraded on a node running postgres 10_10`() { runTokensOnNodeRunningDatabase(DeploymentMachineProvider.DatabaseType.PG_10_10) @@ -100,7 +105,7 @@ class TokenSDKUpgradeDBCompatibility : DockerRemoteMachineBasedTest() { .withCordapp(modernCiV1) .withCordapp(freighterHelperCordapp) .withDatabase(machineProvider.requestDatabase(db)) - ).withVersion("4.3") + ).withVersion(UnitOfDeployment.CORDA_4_3) .deploy(deploymentContext) val nodeMachine = deploymentResult.getOrThrow().nodeMachines.single() diff --git a/modules/contracts-for-testing/build.gradle b/modules/contracts-for-testing/build.gradle index 1082ff37..9602787f 100644 --- a/modules/contracts-for-testing/build.gradle +++ b/modules/contracts-for-testing/build.gradle @@ -46,5 +46,4 @@ dependencies { // CorDapp dependencies. cordapp project(":contracts") - cordapp project(":modules:money") } \ No newline at end of file diff --git a/modules/money/build.gradle b/modules/money/build.gradle deleted file mode 100644 index ed0f8a51..00000000 --- a/modules/money/build.gradle +++ /dev/null @@ -1,57 +0,0 @@ -apply plugin: 'net.corda.plugins.cordapp' - -if (!(corda_release_version in ['4.1'])) { -apply from: "${rootProject.projectDir}/deterministic.gradle" -} - -cordapp { - targetPlatformVersion 4 - minimumPlatformVersion 4 - contract { - name "Token SDK money definitions" - vendor "R3" - licence "Apache 2" - versionId 1 - } - signing { - enabled false - } -} - -sourceSets { - main { - resources { - srcDir rootProject.file("config/dev") - } - } - test { - resources { - srcDir rootProject.file("config/test") - } - } -} - -dependencies { - // Kotlin. - compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - - // Corda dependencies. - cordaCompile ("$corda_release_group:corda-core:$corda_release_version"){ - changing = true - } - - // Logging. - testCompile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}" - - // Testing. - testCompile "$corda_release_group:corda-node-driver:$corda_release_version" - testCompile "junit:junit:$junit_version" - testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" - - // CorDapp dependencies. - cordapp project(":contracts") -} - -jar { - baseName "tokens-money" -} diff --git a/settings.gradle b/settings.gradle index 31861d30..70ec1b99 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,8 +1,6 @@ include 'contracts' include 'workflows' -include 'modules:money' include 'modules:contracts-for-testing' include 'modules:selection' -findProject(':modules:money')?.name = 'money' include 'freighter-tests' diff --git a/workflows/build.gradle b/workflows/build.gradle index 3e523d6f..04a5c4ac 100644 --- a/workflows/build.gradle +++ b/workflows/build.gradle @@ -2,6 +2,11 @@ apply plugin: 'kotlin-jpa' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.cordapp' + +evaluationDependsOn(":modules:selection") + +def selectionProject = project(":modules:selection") + cordapp { targetPlatformVersion 5 minimumPlatformVersion 5 @@ -41,6 +46,10 @@ configurations { integrationTestRuntime.extendsFrom testRuntime } +compileKotlin{ + dependsOn (selectionProject.jar) +} + dependencies { // Kotlin. compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" @@ -50,6 +59,10 @@ dependencies { changing = true } + cordaCompile("$corda_release_group:corda-node-api:$corda_release_version") { + changing = true + } + // Logging. testCompile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}" @@ -60,8 +73,8 @@ dependencies { // CorDapp dependencies. cordapp project(":contracts") - compile project(":modules:money") - compile project(":modules:selection") + + compile(files(selectionProject.jar.archivePath)) //CI for confidential tokens cordapp "$confidential_id_release_group:ci-workflows:$confidential_id_release_version" diff --git a/workflows/src/integrationTest/kotlin/com/r3/corda/lib/tokens/integrationTest/TokenDriverTest.kt b/workflows/src/integrationTest/kotlin/com/r3/corda/lib/tokens/integrationTest/TokenDriverTest.kt index 17490288..fed5b17a 100644 --- a/workflows/src/integrationTest/kotlin/com/r3/corda/lib/tokens/integrationTest/TokenDriverTest.kt +++ b/workflows/src/integrationTest/kotlin/com/r3/corda/lib/tokens/integrationTest/TokenDriverTest.kt @@ -130,7 +130,6 @@ class TokenDriverTest { portAllocation = incrementalPortAllocation(), startNodesInProcess = false, cordappsForAllNodes = listOf( - TestCordapp.findCordapp("com.r3.corda.lib.tokens.money"), TestCordapp.findCordapp("com.r3.corda.lib.tokens.selection"), TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"), TestCordapp.findCordapp("com.r3.corda.lib.tokens.workflows"), @@ -156,7 +155,6 @@ class TokenDriverTest { portAllocation = incrementalPortAllocation(), startNodesInProcess = false, cordappsForAllNodes = listOf( - TestCordapp.findCordapp("com.r3.corda.lib.tokens.money"), TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"), TestCordapp.findCordapp("com.r3.corda.lib.tokens.workflows"), TestCordapp.findCordapp("com.r3.corda.lib.tokens.testing"), @@ -265,7 +263,6 @@ class TokenDriverTest { inMemoryDB = false, startNodesInProcess = false, cordappsForAllNodes = listOf( - TestCordapp.findCordapp("com.r3.corda.lib.tokens.money"), TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"), TestCordapp.findCordapp("com.r3.corda.lib.tokens.workflows"), TestCordapp.findCordapp("com.r3.corda.lib.tokens.testing"), @@ -323,7 +320,6 @@ class TokenDriverTest { inMemoryDB = false, startNodesInProcess = false, cordappsForAllNodes = listOf( - TestCordapp.findCordapp("com.r3.corda.lib.tokens.money"), TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"), TestCordapp.findCordapp("com.r3.corda.lib.tokens.workflows"), TestCordapp.findCordapp("com.r3.corda.lib.tokens.testing"), diff --git a/modules/money/src/main/kotlin/com/r3/corda/lib/tokens/money/DigitalCurrency.kt b/workflows/src/main/kotlin/com/r3/corda/lib/tokens/money/DigitalCurrency.kt similarity index 100% rename from modules/money/src/main/kotlin/com/r3/corda/lib/tokens/money/DigitalCurrency.kt rename to workflows/src/main/kotlin/com/r3/corda/lib/tokens/money/DigitalCurrency.kt diff --git a/modules/money/src/main/kotlin/com/r3/corda/lib/tokens/money/FiatCurrency.kt b/workflows/src/main/kotlin/com/r3/corda/lib/tokens/money/FiatCurrency.kt similarity index 100% rename from modules/money/src/main/kotlin/com/r3/corda/lib/tokens/money/FiatCurrency.kt rename to workflows/src/main/kotlin/com/r3/corda/lib/tokens/money/FiatCurrency.kt diff --git a/modules/money/src/main/kotlin/com/r3/corda/lib/tokens/money/Utilities.kt b/workflows/src/main/kotlin/com/r3/corda/lib/tokens/money/Utilities.kt similarity index 100% rename from modules/money/src/main/kotlin/com/r3/corda/lib/tokens/money/Utilities.kt rename to workflows/src/main/kotlin/com/r3/corda/lib/tokens/money/Utilities.kt diff --git a/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/OwnerMigration.kt b/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/OwnerMigration.kt index 55a2e688..cf580ff3 100644 --- a/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/OwnerMigration.kt +++ b/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/OwnerMigration.kt @@ -26,6 +26,7 @@ import net.corda.serialization.internal.AMQP_STORAGE_CONTEXT import net.corda.serialization.internal.CordaSerializationMagic import net.corda.serialization.internal.SerializationFactoryImpl import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme +import net.corda.serialization.internal.amqp.amqpMagic import java.sql.ResultSet @@ -47,7 +48,7 @@ class OwnerMigration : CustomSqlChange { val serializationFactory = SerializationFactoryImpl().apply { registerScheme(AMQPInspectorSerializationScheme) } - val context = AMQP_STORAGE_CONTEXT.withLenientCarpenter() + val context = AMQP_STORAGE_CONTEXT override fun validate(database: Database?): ValidationErrors? { @@ -66,43 +67,44 @@ class OwnerMigration : CustomSqlChange { override fun generateStatements(database: Database?): Array { + return withSerializationEnv { - if (database == null) { - throw IllegalStateException("Cannot migrate tokens without owner as Liquibase failed to provide a suitable connection") - } - val selectStatement = """ + if (database == null) { + throw IllegalStateException("Cannot migrate tokens without owner as Liquibase failed to provide a suitable connection") + } + val selectStatement = """ SELECT output_index, transaction_id, transaction_value FROM fungible_token, node_transactions WHERE holding_key IS NULL and node_transactions.tx_id = fungible_token.transaction_id """.trimIndent() - val connection = (database.connection as JdbcConnection).wrappedConnection - val preparedStatement = connection.prepareStatement(selectStatement) - val resultSet = preparedStatement.executeQuery() - val listOfUpdates = mutableListOf>() - resultSet.use { rs -> - while (rs.next()) { - val outputIdx = rs.getInt(1) - val txId = rs.getString(2) - val txBytes = getBytes(database, resultSet) - val signedTx: SignedTransaction = txBytes.deserialize(serializationFactory, context) - val fungibleTokensOutRefs = signedTx.coreTransaction.outRefsOfType(FungibleToken::class.java) - val tokenMatchingRef = fungibleTokensOutRefs.singleOrNull { it.ref.index == outputIdx } - tokenMatchingRef?.state?.data?.holder?.owningKey?.toStringShort()?.let { ownerHash -> - listOfUpdates.add(StateRef(SecureHash.parse(txId), outputIdx) to ownerHash) + val connection = (database.connection as JdbcConnection).wrappedConnection + val preparedStatement = connection.prepareStatement(selectStatement) + val resultSet = preparedStatement.executeQuery() + val listOfUpdates = mutableListOf>() + resultSet.use { rs -> + while (rs.next()) { + val outputIdx = rs.getInt(1) + val txId = rs.getString(2) + val txBytes = getBytes(database, resultSet) + val signedTx: SignedTransaction = txBytes.deserialize(serializationFactory, AMQP_STORAGE_CONTEXT.withClassLoader(this.javaClass.classLoader).withoutCarpenter()) + val fungibleTokensOutRefs = signedTx.coreTransaction.outRefsOfType(FungibleToken::class.java) + val tokenMatchingRef = fungibleTokensOutRefs.singleOrNull { it.ref.index == outputIdx } + tokenMatchingRef?.state?.data?.holder?.owningKey?.toStringShort()?.let { ownerHash -> + listOfUpdates.add(StateRef(SecureHash.parse(txId), outputIdx) to ownerHash) + } } } - } - return listOfUpdates.map { (stateRef, holdingKeyHash) -> - UpdateStatement(connection.catalog, connection.schema, "fungible_token") - .setWhereClause("output_index=? AND transaction_id=?") - .addNewColumnValue("holding_key", holdingKeyHash) - .addWhereParameters(stateRef.index, stateRef.txhash.toString()) - }.toTypedArray() + listOfUpdates.map { (stateRef, holdingKeyHash) -> + UpdateStatement(connection.catalog, connection.schema, "fungible_token") + .setWhereClause("output_index=? AND transaction_id=?") + .addNewColumnValue("holding_key", holdingKeyHash) + .addWhereParameters(stateRef.index, stateRef.txhash.toString()) + }.toTypedArray() + } } - } fun getBytes(database: Database, resultSet: ResultSet): ByteArray { @@ -121,4 +123,46 @@ fun getBytes(database: Database, resultSet: ResultSet): ByteArray { } } +private object AMQPInspectorSerializationScheme : AbstractAMQPSerializationScheme(emptyList()) { + override fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean { + return magic == amqpMagic + } + + override fun rpcClientSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException() + override fun rpcServerSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException() +} + +fun withSerializationEnv(block: () -> T): T { + val newEnv = if (_allEnabledSerializationEnvs.isEmpty()) { + initialiseSerialization() + true + } else { + false + } + var result: T? = null + effectiveSerializationEnv.serializationFactory.withCurrentContext(effectiveSerializationEnv.storageContext.withClassLoader(FungibleToken::class.java.classLoader)) { + result = block() + } + + if (newEnv) { + disableSerialization() + } + return result!! +} + +private fun initialiseSerialization() { + // Deserialise with the lenient carpenter as we only care for the AMQP field getters + _inheritableContextSerializationEnv.set( + SerializationEnvironment.with( + SerializationFactoryImpl().apply { + registerScheme(AMQPInspectorSerializationScheme) + }, + p2pContext = AMQP_P2P_CONTEXT.withoutCarpenter(), + storageContext = AMQP_STORAGE_CONTEXT.withoutCarpenter() + )) +} + +private fun disableSerialization() { + _inheritableContextSerializationEnv.set(null) +} diff --git a/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/evolvable/CreateEvolvableTokensFlowHandler.kt b/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/evolvable/CreateEvolvableTokensFlowHandler.kt index d50b1c0e..a0b7db71 100644 --- a/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/evolvable/CreateEvolvableTokensFlowHandler.kt +++ b/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/evolvable/CreateEvolvableTokensFlowHandler.kt @@ -27,6 +27,6 @@ class CreateEvolvableTokensFlowHandler(val otherSession: FlowSession) : FlowLogi } // Resolve the creation transaction. - return subFlow(ObserverAwareFinalityFlowHandler(otherSession)) + subFlow(ObserverAwareFinalityFlowHandler(otherSession)) } } \ No newline at end of file diff --git a/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/evolvable/UpdateEvolvableTokenFlowHandler.kt b/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/evolvable/UpdateEvolvableTokenFlowHandler.kt index a83dba4f..281f75fd 100644 --- a/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/evolvable/UpdateEvolvableTokenFlowHandler.kt +++ b/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/evolvable/UpdateEvolvableTokenFlowHandler.kt @@ -34,6 +34,6 @@ class UpdateEvolvableTokenFlowHandler(val otherSession: FlowSession) : FlowLogic } // Resolve the update transaction. - return subFlow(ObserverAwareFinalityFlowHandler(otherSession)) + subFlow(ObserverAwareFinalityFlowHandler(otherSession)) } } \ No newline at end of file diff --git a/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/redeem/RedeemTokensFlowHandler.kt b/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/redeem/RedeemTokensFlowHandler.kt index 999dc0cb..f86f83ac 100644 --- a/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/redeem/RedeemTokensFlowHandler.kt +++ b/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/redeem/RedeemTokensFlowHandler.kt @@ -19,9 +19,9 @@ import net.corda.core.utilities.unwrap * [RedeemNonFungibleTokensFlow], [RedeemTokensFlow]. */ // Called on Issuer side. -class RedeemTokensFlowHandler(val otherSession: FlowSession) : FlowLogic() { +class RedeemTokensFlowHandler(val otherSession: FlowSession) : FlowLogic() { @Suspendable - override fun call() { + override fun call(): SignedTransaction? { val role = otherSession.receive().unwrap { it } if (role == TransactionRole.PARTICIPANT) { // Synchronise all confidential identities, issuer isn't involved in move transactions, so states holders may @@ -42,9 +42,9 @@ class RedeemTokensFlowHandler(val otherSession: FlowSession) : FlowLogic() }) } } - if (!serviceHub.myInfo.isLegalIdentity(otherSession.counterparty)) { + return if (!serviceHub.myInfo.isLegalIdentity(otherSession.counterparty)) { // Call observer aware finality flow handler. subFlow(ObserverAwareFinalityFlowHandler(otherSession)) - } + } else null } } diff --git a/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/rpc/RedeemTokens.kt b/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/rpc/RedeemTokens.kt index 1e150326..b00e8e88 100644 --- a/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/rpc/RedeemTokens.kt +++ b/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/flows/rpc/RedeemTokens.kt @@ -33,9 +33,11 @@ constructor( } @InitiatedBy(RedeemFungibleTokens::class) -class RedeemFungibleTokensHandler(val otherSession: FlowSession) : FlowLogic() { +open class RedeemFungibleTokensHandler(val otherSession: FlowSession) : FlowLogic() { @Suspendable - override fun call() = subFlow(RedeemTokensFlowHandler(otherSession)) + override fun call() { + subFlow(RedeemTokensFlowHandler(otherSession)) + } } @StartableByService @@ -57,9 +59,11 @@ constructor( } @InitiatedBy(RedeemNonFungibleTokens::class) -class RedeemNonFungibleTokensHandler(val otherSession: FlowSession) : FlowLogic() { +open class RedeemNonFungibleTokensHandler(val otherSession: FlowSession) : FlowLogic() { @Suspendable - override fun call() = subFlow(RedeemTokensFlowHandler(otherSession)) + override fun call() { + subFlow(RedeemTokensFlowHandler(otherSession)) + } } /* Confidential flows. */ @@ -91,7 +95,9 @@ constructor( } @InitiatedBy(ConfidentialRedeemFungibleTokens::class) -class ConfidentialRedeemFungibleTokensHandler(val otherSession: FlowSession) : FlowLogic() { +open class ConfidentialRedeemFungibleTokensHandler(val otherSession: FlowSession) : FlowLogic() { @Suspendable - override fun call() = subFlow(ConfidentialRedeemFungibleTokensFlowHandler(otherSession)) -} + override fun call() { + subFlow(ConfidentialRedeemFungibleTokensFlowHandler(otherSession)) + } +} \ No newline at end of file diff --git a/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/internal/flows/finality/ObserverAwareFinalityFlowHandler.kt b/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/internal/flows/finality/ObserverAwareFinalityFlowHandler.kt index 0ddf37de..2c85c6db 100644 --- a/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/internal/flows/finality/ObserverAwareFinalityFlowHandler.kt +++ b/workflows/src/main/kotlin/com/r3/corda/lib/tokens/workflows/internal/flows/finality/ObserverAwareFinalityFlowHandler.kt @@ -5,20 +5,21 @@ import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowSession import net.corda.core.flows.ReceiveFinalityFlow import net.corda.core.node.StatesToRecord +import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.unwrap -class ObserverAwareFinalityFlowHandler(val otherSession: FlowSession) : FlowLogic() { +class ObserverAwareFinalityFlowHandler(val otherSession: FlowSession) : FlowLogic() { @Suspendable - override fun call() { + override fun call(): SignedTransaction? { val role = otherSession.receive().unwrap { it } val statesToRecord = when (role) { TransactionRole.PARTICIPANT -> StatesToRecord.ONLY_RELEVANT TransactionRole.OBSERVER -> StatesToRecord.ALL_VISIBLE } // If states are issued to self, then ReceiveFinalityFlow does not need to be invoked. - if (!serviceHub.myInfo.isLegalIdentity(otherSession.counterparty)) { + return if (!serviceHub.myInfo.isLegalIdentity(otherSession.counterparty)) { subFlow(ReceiveFinalityFlow(otherSideSession = otherSession, statesToRecord = statesToRecord)) - } + } else null } } diff --git a/modules/money/src/test/java/com/r3/corda/lib/tokens/money/CurrencyAccessFromJavaTest.java b/workflows/src/test/java/com/r3/corda/lib/tokens/money/CurrencyAccessFromJavaTest.java similarity index 100% rename from modules/money/src/test/java/com/r3/corda/lib/tokens/money/CurrencyAccessFromJavaTest.java rename to workflows/src/test/java/com/r3/corda/lib/tokens/money/CurrencyAccessFromJavaTest.java