diff --git a/.ci/e2e/JenkinsfileE2E b/.ci/e2e/JenkinsfileE2E index a129f701..48387bea 100644 --- a/.ci/e2e/JenkinsfileE2E +++ b/.ci/e2e/JenkinsfileE2E @@ -5,5 +5,6 @@ endToEndPipeline( gradleTestTargetsToExecute: ['e2eTest'], usePackagedCordaHelmChart: true, helmVersion: '^5.0.0-beta', - dynamicCordaApiVersion: false + dynamicCordaApiVersion: false, + gradleAdditionalArgs: '-PcordaNotaryPluginsVersion=5.0.0.0-beta+' ) diff --git a/Jenkinsfile b/Jenkinsfile index 88e5ac92..305df418 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -8,5 +8,6 @@ cordaPipelineKubernetesAgent( gitHubComments: false, e2eTestName: 'corda-utxo-ledger-extensions-e2e-tests', runE2eTests: true, - publishToMavenS3Repository: true + publishToMavenS3Repository: true, + gradleAdditionalArgs: '-PcordaNotaryPluginsVersion=5.0.0.0-beta+' ) diff --git a/README.md b/README.md index af58ac23..db938076 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,621 @@ -# Corda 5 UTXO Ledger Extensions +# Corda 5 Advanced UTXO Ledger Extensions -A framework for building advanced contracts and states on Corda 5. +The Corda 5 Advanced UTXO Ledger Extensions library provides several powerful features to Corda 5's UTXO ledger. These features have been selected and designed to solve common problems that CorDapp developers face when building states and contracts on Corda. -## Chainable States +## Feature Overview -Chainable states represent strictly linear state chains, where each state points to the previous state in the chain. +The following definitions provide an overview of each major feature or component that has been implemented in the Corda 5 Advanced UTXO Ledger Extensions library. These features can be used together; for example, a state could be designed to be fungible, issuable and ownable. -## Fungible States +### Chainable -Fungible states represent states that have quantity, and can be split or merged. +Chainable states represent strictly linear state chains, where every state in the chain points to the previous state in the chain. This could be thought of as a similar concept to a blockchain, where each new block points to the previous block. -## Identifiable States +### Fungible -Identifiable states represent states that have a unique identifier that is guaranteed to be unique across the network. +Fungible states represent states that have a scalar numeric quantity, and can be split, merged and mutually exchanged with other fungible states of the same class. Fungible states represent the building blocks for states like tokens. -## Issuable States +### Identifiable -Issuable states have an issuer. +Identifiable states represent states that have a unique identifier that is guaranteed unique at the network level. Identifiable states are designed to evolve over time, where unique identifiers can be used to resolve the history of the identifiable state. -## Ownable States +### Issuable -Ownable states have an owner. +Issuable states represent states that have an issuer. Typically an issuer is responsible for signing transactions where issuable states are issued or redeemed. +### Ownable + +Ownable states represent states that have an owner. Typically an owner is responsible for signing transactions where ownable states are transferred from one owner to another. + +## Design Motivations + +Over the years as Corda has grown and matured, we've identified several common patterns and practices for Corda state and contract design; for example tokens (Corda 4 addressed this with the Token SDK). + +What we noticed was that many of these patterns and practices either end up being implemented repeatedly in different business application scenarios, and abstractions of these patterns and practices ended up being too rigid and didn't fit certain business application scenarios. + +The Corda 5 Advanced UTXO Ledger Extensions library aims to provide abstractions at several levels, allowing CorDapp developers to opt-in to API features as required. + +## Contract Design + +In order to fully understand the design motivations, first we must understand some of the challenges commonly faced by CorDapp developers. + +### Basic Contract Design + +We'll start by taking a look at a trivial contract implementation, below. The following contract defines three commands; `Create`, `Update` and `Delete`. The `verify` function delegates these command types to `verifyCreate`, `verifyUpdate` and `verifyDelete` functions respectively; for example: + +```java +public final class ExampleContract implements Contract { + + private interface ExampleContractCommand extends Command { } + + public static class Create implements ExampleContractCommand { } + public static class Update implements ExampleContractCommand { } + public static class Delete implements ExampleContractCommand { } + + @Override + public void verify(UtxoLedgerTransaction transaction) { + + List commands = transaction + .getCommands(ExampleContractCommand.class); + + for (ExampleContractCommand command : commands) { + if (command instanceof Create) verifyCreate(transaction); + else if (command instanceof Update) verifyUpdate(transaction); + else if (command instanceof Delete) verifyDelete(transaction); + else throw new IllegalStateException("Unrecognised command type."); + } + } + + private void verifyCreate(UtxoLedgerTransaction transaction) { + // Verify Create constraints + } + + private void verifyUpdate(UtxoLedgerTransaction transaction) { + // Verify Update constraints + } + + private void verifyDelete(UtxoLedgerTransaction transaction) { + // Verify Delete constraints + } +} +``` + +Designing a contract like this will suffice in many cases. Assuming that the constraints have been implemented correctly then the contract functionality and design is perfectly acceptable. + +However, there are cases where this design approach no longer fits the design goals of the system being implemented; specifically, in regard to contract extensibility, it's currently not possible to extend this contract to support additional constraints. + +### Derivable Contract Design + +The following contract refactors the above to support the ability to derive contracts, and provide additional constraints in a secure and controlled way. + +The contract still provides the same three commands; `Create`, `Update` and `Delete`. The `verify` function delegates these command types to `verifyCreate`, `verifyUpdate` and `verifyDelete` functions respectively, which in turn call `onVerifyCreate`, `onVerifyUpdate` and `onVerifyDelete` respectively. + +Note that the `verify` function has been marked `final`. This change is necessary as it prevents derived contract implementations from circumventing the base contract rules; for example; + +```java +public class ExampleContract implements Contract { + + private interface ExampleContractCommand extends Command { } + + public static class Create implements ExampleContractCommand { } + public static class Update implements ExampleContractCommand { } + public static class Delete implements ExampleContractCommand { } + + @Override + public final void verify(UtxoLedgerTransaction transaction) { + + List commands = transaction + .getCommands(ExampleContractCommand.class); + + for (ExampleContractCommand command : commands) { + if (command instanceof Create) verifyCreate(transaction); + else if (command instanceof Update) verifyUpdate(transaction); + else if (command instanceof Delete) verifyDelete(transaction); + else throw new IllegalStateException("Unrecognised command type."); + } + } + + protected void onVerifyCreate(UtxoLedgerTransaction transaction) { } + protected void onVerifyUpdate(UtxoLedgerTransaction transaction) { } + protected void onVerifyDelete(UtxoLedgerTransaction transaction) { } + + private void verifyCreate(UtxoLedgerTransaction transaction) { + // Verify base Create constraints + // Then verify additional Create constraints implemented by derived contracts + onVerifyCreate(transaction); + } + + private void verifyUpdate(UtxoLedgerTransaction transaction) { + // Verify base Update constraints + // Then verify additional Update constraints implemented by derived contracts + onVerifyUpdate(transaction); + } + + private void verifyDelete(UtxoLedgerTransaction transaction) { + // Verify base Delete constraints + // Then verify additional Delete constraints implemented by derived contracts + onVerifyDelete(transaction); + } +} +``` + +As demonstrated, refactoring a contract like this allows CorDapp implementors to derive from the contract, allowing additional constraints which will be verified in additional to the constraints specified by the base contract. + +However, there are still some outstanding issues with this design, where this design approach no longer fits the design goals of the system being implemented. + +The problem really lies in the verify function; for example: + +```java +public final void verify(UtxoLedgerTransaction transaction) { + + List commands = transaction + .getCommands(ExampleContractCommand.class); + + for (ExampleContractCommand command : commands) { + if (command instanceof Create) verifyCreate(transaction); + else if (command instanceof Update) verifyUpdate(transaction); + else if (command instanceof Delete) verifyDelete(transaction); + else throw new IllegalStateException("Unrecognised command type."); + } +} +``` + +The `verify` function is marked `final` for security reasons, and therefore additional commands cannot be added to the contract; for example, the contract may wish to describe multiple ways to _Update_ a state, or set of states. Since the contract only defines a single `Update` command, there can only be one mechanism to perform updates. + +The second problem lies in the commands themselves, and their names. `Create`, `Update` and `Delete` are very ambiguous names, which almost certainly won't make sense depending on the context of the contract being implemented. + +### Delegated Command Design + +In the contracts above, the commands are nothing more than marker classes; effectively they are cases in a switch statement, which allow the contract's `verify` function to delegate responsibility of specific contract constraints to other functions, such as `verifyCreate`, `verifyUpdate` and `verifyDelete`. + +Instead, we could implement the `verify` function on the command itself. Instead of being an empty marker class, this gives the command responsibility, as it becomes responsible for implementing its associated contract verification constraints. + +In this case, we define a `VerifiableCommand` interface with a `verify` function; for example: + +```java +public interface VerifiableCommand extends Command { + void verify(UtxoLedgerTransaction transaction); +} +``` + +Now that we have a command which itself can implement contract verification constraints, we can use this as the basis for the `ExampleContractCommand` class. Notice that this now needs to be a class rather than an interface, because we need to be in complete control of its implementations for security. + +We achieve this by making the default constructor package-private, so that only commands within the same package can extend it; for example: + +```java +public class ExampleContractCommand implements VerifiableCommand { + ExampleContractCommand() { } +} +``` + +Next, we can implement this interface as `Create`, `Update` and `Delete` commands; for example: + +```java +public class Create extends ExampleContractCommand { + + @Override + public final void verify(UtxoLedgerTransaction transaction) { + // Verify base Create constraints + // Then verify additional Create constraints implemented in derived commands + onVerify(transaction); + } + + protected void onVerify(UtxoLedgerTransaction transaction) { } +} + +public class Update extends ExampleContractCommand { + + @Override + public final void verify(UtxoLedgerTransaction transaction) { + // Verify base Update constraints + // Then verify additional Update constraints implemented in derived commands + onVerify(transaction); + } + + protected void onVerify(UtxoLedgerTransaction transaction) { } +} + +public class Delete extends ExampleContractCommand { + + @Override + public final void verify(UtxoLedgerTransaction transaction) { + // Verify base Delete constraints + // Then verify additional Delete constraints implemented in derived commands + onVerify(transaction); + } + + protected void onVerify(UtxoLedgerTransaction transaction) { } +} +``` + +Note that the `Create`, `Update` and `Delete` commands are not marked `final`, therefore we can extend the contract verification constraints from these points, but we can't extend from `ExampleContractCommand`. + +### Delegated Contract Design + +As we have now delegated contract verification constraint logic to the commands themselves, we must also refctor the contract to support this delegation. The contract implementation in this case becomes incredibly simple, since it's no longer responsible for defining contract verification constraints; for example: + +```java +public final class ExampleContract implements Contract { + + @Override + public void verify(UtxoLedgerTransaction transaction) { + + List commands = transaction + .getCommands(ExampleContractCommand.class); + + for (ExampleContractCommand command : commands) { + command.verify(transaction); + } + } +} +``` + +This design addresses the outstanding issues in regard to being able to extend a contract with multiple commands, and being able to give command's names that make sense in the context that they're used; for example: + +```java +class Mint extends Create { ... } +class Issue extends Update { ... } +class Transfer extends Update { ... } +class Exchange extends Update { ... } +class Redeem extends Update { ... } +class Burn extends Delete { ... } +``` + +Note that the contract now supports five different command types, each of which implements different constraints and derives from `Create`, `Update`, or `Delete`. + +## Advanced Contract Design + +Ultimately, all the contract design issues that have been highlighted above are implemented by the Corda 5 Advanced UTXO Extensions library, and into all the specific implementations; for example chainable, fungible and identifiable contracts. + +## Base API + +**Module:** base + +**Package:** com.r3.corda.ledger.utxo.base + +The base API provides the underlying component model for designing extensible contracts with delegated contract verification constraint logic, as well as some other components which allow CorDapp developers to be more expressive and better express intent throughout their applications. + +## Chainable API + +**Module:** chainable + +**Package:** com.r3.corda.ledger.utxo.chainable + +The chainable API provides the component model for designing chainable states and contracts. Chainable states represent strictly linear state chains, where every state in the chain points to the previous state in the chain. This could be thought of as a similar concept to a blockchain, where each new block points to the previous block. + +### Designing a Chainable State + +A chainable state can be implemented by implementing the `ChainableState` interface; for example: + +```java +@BelongsToContract(ExampleChainableContract.class) +public final class ExampleChainableState extends ChainableState { + + @Nullable + private final StaticPointer pointer; + + public ExampleChainableState(@NotNull final StaticPointer pointer) { + this.pointer = pointer; + } + + @Nullable + public StaticPointer getPreviousStatePointer() { + return pointer; + } + + @NotNull + public List getParticipants() { + return List.of(...); + } +} +``` + +### Designing Chainable Commands + +Chainable commands support creating, updating and deleting chainable states. + +The `ChainableContractCreateCommand` supports creating new chainable states and will verify the following constraints: + +- On chainable state(s) creating, at least one chainable state must be created. +- On chainable state(s) creating, the previous state pointer of every created chainable state must be null. + +```java +public final class Create extends ChainableContractCreateCommand { + + @NotNull + public Class getContractStateType() { + return ExampleChainableState.class; + } + + @Override + protected void onVerify(@NotNull final UtxoLedgerTransaction transaction) { + // Verify additional Create constraints + } +} +``` + +The `ChainableContractUpdateCommand` supports updating existing chainable states and will verify the following constraints: + +- On chainable state(s) updating, at least one chainable state must be consumed. +- On chainable state(s) updating, at least one chainable state must be created. +- On chainable state(s) updating, the previous state pointer of every created chainable state must not be null. +- On chainable state(s) updating, the previous state pointer of every created chainable state must be pointing to exactly one consumed chainable state, exclusively. + +```java +public final class Update extends ChainableContractUpdateCommand { + + @NotNull + public Class getContractStateType() { + return ExampleChainableState.class; + } + + @Override + protected void onVerify(@NotNull final UtxoLedgerTransaction transaction) { + // Verify additional Update constraints + } +} +``` + +The `ChainableContractDeleteCommand` supports deleting existing chainable states and will verify the following constraints: + +- On chainable state(s) deleting, at least one chainable state must be consumed. + +```java +public final class Delete extends ChainableContractDeleteCommand { + + @NotNull + public Class getContractStateType() { + return ExampleChainableState.class; + } + + @Override + protected void onVerify(@NotNull final UtxoLedgerTransaction transaction) { + // Verify additional Delete constraints + } +} +``` + +### Designing a Chainable Contract + +A chainable contract can be implemented by extending the `ChainableContract` class; for example: + +```java +public final class ExampleChainableContract extends ChainableContract { + + @Override + public List>> getPermittedCommandTypes() { + return List.of(Create.class, Update.class, Delete.class); + } +} +``` + +## Fungible API + +**Module:** fungible + +**Package:** com.r3.corda.ledger.utxo.fungible + +The fungible API provides the component model for designing fungible states and contracts. Fungible states represent states that have a scalar numeric quantity, and can be split, merged and mutually exchanged with other fungible states of the same class. Fungible states represent the building blocks for states like tokens. + +### Designing a Fungible State + +A fungible state can be implemented by implementing the `FungibleState` interface; for example: + +```java +public final class ExampleFungibleState extends FungibleState { + + @NotNull + private final NumericDecimal quantity; + + public ExampleFungibleState(@NotNull final NumericDecimal quantity) { + this.quantity = quantity; + } + + @NotNull + public NumericDecimal getQuantity() { + return quantity; + } + + @NotNull + public List getParticipants() { + return List.of(...); + } + + @Override + public boolean isFungibleWith(@NotNull final FungibleState other) { + return this == other || other instanceof ExampleFungibleState // && other fungibility rules. + } +} +``` + +### Designing Fungible Commands + +Fungible commands support creating, updating and deleting fungible states. + +The `FungibleContractCreateCommand` supports creating new fungible states and will verify the following constraints: + +- On fungible state(s) creating, at least one fungible state must be created. +- On fungible state(s) creating, the quantity of every created fungible state must be greater than zero. + +```java +public final class Create extends FungibleContractCreateCommand { + + @NotNull + public Class getContractStateType() { + return ExampleFungibleState.class; + } + + @Override + protected void onVerify(@NotNull final UtxoLedgerTransaction transaction) { + // Verify additional Create constraints + } +} +``` + +The `FungibleContractUpdateCommand` supports updating existing fungible states and will verify the following constraints: + +- On fungible state(s) updating, at least one fungible state must be consumed. +- On fungible state(s) updating, at least one fungible state must be created. +- On fungible state(s) updating, the quantity of every created fungible state must be greater than zero. +- On fungible state(s) updating, the sum of the unscaled values of the consumed states must be equal to the sum of the unscaled values of the created states. +- On fungible state(s) updating, the sum of the consumed states that are fungible with each other must be equal to the sum of the created states that are fungible with each other. + +```java +public final class Update extends FungibleContractUpdateCommand { + + @NotNull + public Class getContractStateType() { + return ExampleFungibleState.class; + } + + @Override + protected void onVerify(@NotNull final UtxoLedgerTransaction transaction) { + // Verify additional Update constraints + } +} +``` + +The `FungibleContractDeleteCommand` supports deleting existing fungible states and will verify the following constraints: + +- On fungible state(s) deleting, at least one fungible state input must be consumed. +- On fungible state(s) deleting, the sum of the unscaled values of the consumed states must be greater than the sum of the unscaled values of the created states. +- On fungible state(s) deleting, the sum of consumed states that are fungible with each other must be greater than the sum of the created states that are fungible with each other. + +```java +public final class Delete extends FungibleContractDeleteCommand { + + @NotNull + public Class getContractStateType() { + return ExampleFungibleState.class; + } + + @Override + protected void onVerify(@NotNull final UtxoLedgerTransaction transaction) { + // Verify additional Delete constraints + } +} +``` + +### Designing a Fungible Contract + +A fungible contract can be implemented by extending the `FungibleContract` class; for example: + +```java +public final class ExampleFungibleContract extends FungibleContract { + + @Override + public List>> getPermittedCommandTypes() { + return List.of(Create.class, Update.class, Delete.class); + } +} +``` + +## Identifiable API + +**Module:** identifiable + +**Package:** com.r3.corda.ledger.utxo.identifiable + +The identifiable API provides the component model for designing identifiable states and contracts. Identifiable states represent states that have a unique identifier that is guaranteed unique at the network level. Identifiable states are designed to evolve over time, where unique identifiers can be used to resolve the history of the identifiable state. + +### Designing an Identifiable State + +An identifiable state can be implemented by implementing the `IdentifiableState` interface; for example: + +```java +public final class ExampleIdentifiableState extends IdentifiableState { + + @Nullable + private final StateRef id; + + public ExampleIdentifiableState(@Nullable final StateRef id) { + this.id = id; + } + + @Nullable + public StateRef getId() { + return id; + } + + @NotNull + public List getParticipants() { + return List.of(...); + } +} +``` + +### Designing Identifiable Commands + +Identifiable commands support creating, updating and deleting identifiable states. + +The `IdentifiableContractCreateCommand` supports creating new identifiable states and will verify the following constraints: + +- On identifiable state(s) creating, at least one identifiable state must be created. + +```java +public final class Create extends IdentifiableContractCreateCommand { + + @NotNull + public Class getContractStateType() { + return ExampleIdentifiableState.class; + } + + @Override + protected void onVerify(@NotNull final UtxoLedgerTransaction transaction) { + // Verify additional Create constraints + } +} +``` + +The `IdentifiableContractUpdateCommand` supports updating existing identifiable states and will verify the following constraints: + +- On identifiable state(s) updating, at least one identifiable state must be consumed. +- On identifiable state(s) updating, at least one identifiable state must be created. +- On identifiable state(s) updating, each created identifiable state's identifier must match one consumed identifiable state's state ref or identifier, exclusively. + +```java +public final class Update extends IdentifiableContractUpdateCommand { + + @NotNull + public Class getContractStateType() { + return ExampleIdentifiableState.class; + } + + @Override + protected void onVerify(@NotNull final UtxoLedgerTransaction transaction) { + // Verify additional Update constraints + } +} +``` + +The `IdentifiableContractDeleteCommand` supports deleting existing identifiable states and will verify the following constraints: + +- On identifiable state(s) deleting, at least one identifiable state must be consumed. + +```java +public final class Delete extends IdentifiableContractDeleteCommand { + + @NotNull + public Class getContractStateType() { + return ExampleIdentifiableState.class; + } + + @Override + protected void onVerify(@NotNull final UtxoLedgerTransaction transaction) { + // Verify additional Delete constraints + } +} +``` + +### Designing an Identifiable Contract + +An identifiable contract can be implemented by extending the `IdentifiableContract` class; for example: + +```java +public final class ExampleIdentifiableContract extends IdentifiableContract { + + @Override + public List>> getPermittedCommandTypes() { + return List.of(Create.class, Update.class, Delete.class); + } +} +``` diff --git a/build.gradle b/build.gradle index 6e5c5262..3f21867a 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,7 @@ subprojects { } version = "$cordautxoVersion$versionSuffix" } + cordaNotaryPluginsVersion = project.findProperty("cordaNotaryPluginsVersion") ?: cordaRuntimeOsVersion apply plugin: 'maven-publish' diff --git a/e2e-tests/build.gradle b/e2e-tests/build.gradle index d3450210..3c147e27 100644 --- a/e2e-tests/build.gradle +++ b/e2e-tests/build.gradle @@ -61,11 +61,15 @@ dependencies { } } - cpis project(path: ':e2e-tests:cpbs:ledger-utxo-advanced-fungible-demo-app', configuration: 'cordaCPB') - cpis project(path: ':e2e-tests:cpbs:ledger-utxo-advanced-identifiable-demo-app', configuration: 'cordaCPB') - cpis project(path: ':e2e-tests:cpbs:ledger-utxo-advanced-issuable-demo-app', configuration: 'cordaCPB') - cpis project(path: ':e2e-tests:cpbs:ledger-utxo-advanced-chainable-demo-app', configuration: 'cordaCPB') - cpis project(path: ':e2e-tests:cpbs:ledger-utxo-advanced-ownable-demo-app', configuration: 'cordaCPB') + cpis project(path: ':e2e-tests:cpbs:ledger-utxo-advanced-fungible-test-app', configuration: 'cordaCPB') + cpis project(path: ':e2e-tests:cpbs:ledger-utxo-advanced-identifiable-test-app', configuration: 'cordaCPB') + cpis project(path: ':e2e-tests:cpbs:ledger-utxo-advanced-issuable-test-app', configuration: 'cordaCPB') + cpis project(path: ':e2e-tests:cpbs:ledger-utxo-advanced-chainable-test-app', configuration: 'cordaCPB') + cpis project(path: ':e2e-tests:cpbs:ledger-utxo-advanced-ownable-test-app', configuration: 'cordaCPB') + + cpis project(path: ':examples:ledger-utxo-advanced-fungible-demo-app', configuration: 'cordaCPB') + cpis project(path: ':examples:ledger-utxo-advanced-identifiable-demo-app', configuration: 'cordaCPB') + cpis project(path: ':examples:ledger-utxo-advanced-chainable-demo-app', configuration: 'cordaCPB') e2eTestImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" e2eTestImplementation libs.jackson.module diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/build.gradle b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/build.gradle similarity index 90% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/build.gradle rename to e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/build.gradle index c319f9a1..6d129c5d 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/build.gradle +++ b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/build.gradle @@ -7,7 +7,7 @@ cordapp { targetPlatformVersion platformVersion as Integer minimumPlatformVersion platformVersion as Integer workflow { - name "Advanced UTXO Ledger Demo Chainable Workflow" + name "Advanced UTXO Ledger Test Chainable Workflow" versionId 1 vendor "R3" } @@ -18,7 +18,7 @@ dependencies { cordaProvided 'org.jetbrains.kotlin:kotlin-osgi-bundle' cordaProvided 'net.corda:corda-ledger-utxo' - cordapp project(':e2e-tests:cpbs:ledger-utxo-advanced-chainable-demo-contract') + cordapp project(':e2e-tests:cpbs:ledger-utxo-advanced-chainable-test-contract') // Common and API packages pulled in as transitive dependencies through client cordapp "com.r3.corda.notary.plugin.nonvalidating:notary-plugin-non-validating-client:$cordaNotaryPluginsVersion" diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/src/main/kotlin/com/r3/corda/test/utxo/chainable/workflow/ChainableContractCreateTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/src/main/kotlin/com/r3/corda/test/utxo/chainable/workflow/ChainableContractCreateTestFlow.kt new file mode 100644 index 00000000..bbdecab6 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/src/main/kotlin/com/r3/corda/test/utxo/chainable/workflow/ChainableContractCreateTestFlow.kt @@ -0,0 +1,72 @@ +package com.r3.corda.test.utxo.chainable.workflow + +import com.r3.corda.ledger.utxo.base.StaticPointer +import com.r3.corda.test.utxo.chainable.contract.MyChainableContract +import com.r3.corda.test.utxo.chainable.contract.MyChainableState +import com.r3.corda.test.utxo.chainable.contract.MyContractState +import net.corda.v5.application.crypto.DigestService +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.SubFlow +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.crypto.DigestAlgorithmName +import net.corda.v5.ledger.common.NotaryLookup +import net.corda.v5.ledger.utxo.StateAndRef +import net.corda.v5.ledger.utxo.StateRef +import net.corda.v5.ledger.utxo.UtxoLedgerService +import java.time.Instant +import java.time.temporal.ChronoUnit +import java.util.UUID + +class ChainableContractCreateTestFlow(private val rule: String) : SubFlow>> { + + @CordaInject + private lateinit var digestService: DigestService + + @CordaInject + private lateinit var memberLookup: MemberLookup + + @CordaInject + private lateinit var notaryLookup: NotaryLookup + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(): List> { + val key = memberLookup.myInfo().firstLedgerKey + val outputs = when (rule) { + "CONTRACT_RULE_CREATE_OUTPUTS" -> listOf(MyContractState(UUID.randomUUID())) + "CONTRACT_RULE_CREATE_POINTERS" -> { + listOf( + MyChainableState( + UUID.randomUUID(), + key, + StaticPointer( + StateRef(digestService.hash(byteArrayOf(1, 2, 3, 4), DigestAlgorithmName.SHA2_256), 0), + MyChainableState::class.java + ) + ), + MyChainableState(UUID.randomUUID(), key, null) + ) + } + "VALID" -> { + listOf( + MyChainableState(UUID.randomUUID(), key, null), + MyChainableState(UUID.randomUUID(), key, null), + MyContractState(UUID.randomUUID()) + ) + } + else -> throw IllegalArgumentException("Invalid rule type passed in") + } + val transaction = utxoLedgerService.createTransactionBuilder() + .setNotary(notaryLookup.notaryServices.first().name) + .addOutputStates(outputs) + .addSignatories(key) + .addCommand(MyChainableContract.Create()) + .setTimeWindowUntil(Instant.now().plus(10, ChronoUnit.DAYS)) + .toSignedTransaction() + + return utxoLedgerService.finalize(transaction, emptyList()).transaction.outputStateAndRefs + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/src/main/kotlin/com/r3/corda/test/utxo/chainable/workflow/ChainableContractDeleteTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/src/main/kotlin/com/r3/corda/test/utxo/chainable/workflow/ChainableContractDeleteTestFlow.kt new file mode 100644 index 00000000..6fa6d1ef --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/src/main/kotlin/com/r3/corda/test/utxo/chainable/workflow/ChainableContractDeleteTestFlow.kt @@ -0,0 +1,53 @@ +package com.r3.corda.test.utxo.chainable.workflow + +import com.r3.corda.test.utxo.chainable.contract.MyChainableContract +import com.r3.corda.test.utxo.chainable.contract.MyChainableState +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.SubFlow +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.ledger.common.NotaryLookup +import net.corda.v5.ledger.utxo.StateAndRef +import net.corda.v5.ledger.utxo.UtxoLedgerService +import java.time.Instant +import java.time.temporal.ChronoUnit + +class ChainableContractDeleteTestFlow( + private val rule: String, + private val stateAndRefs: List> +) : SubFlow>> { + + @CordaInject + private lateinit var memberLookup: MemberLookup + + @CordaInject + private lateinit var notaryLookup: NotaryLookup + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(): List> { + val key = memberLookup.myInfo().firstLedgerKey + val inputs = when (rule) { + "CONTRACT_RULE_DELETE_INPUTS" -> { + stateAndRefs + .filter { it.state.contractState !is MyChainableState } + .map { it.ref } + } + "VALID" -> { + stateAndRefs.map { it.ref } + } + else -> throw IllegalArgumentException("Invalid rule type passed in") + } + val transaction = utxoLedgerService.createTransactionBuilder() + .setNotary(notaryLookup.notaryServices.first().name) + .addInputStates(inputs) + .addSignatories(key) + .addCommand(MyChainableContract.Delete()) + .setTimeWindowUntil(Instant.now().plus(10, ChronoUnit.DAYS)) + .toSignedTransaction() + + return utxoLedgerService.finalize(transaction, emptyList()).transaction.outputStateAndRefs + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/src/main/kotlin/com/r3/corda/test/utxo/chainable/workflow/ChainableContractTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/src/main/kotlin/com/r3/corda/test/utxo/chainable/workflow/ChainableContractTestFlow.kt new file mode 100644 index 00000000..a628d662 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/src/main/kotlin/com/r3/corda/test/utxo/chainable/workflow/ChainableContractTestFlow.kt @@ -0,0 +1,37 @@ +package com.r3.corda.test.utxo.chainable.workflow + +import net.corda.v5.application.flows.ClientRequestBody +import net.corda.v5.application.flows.ClientStartableFlow +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.FlowEngine +import net.corda.v5.application.marshalling.JsonMarshallingService +import net.corda.v5.base.annotations.Suspendable + +class ChainableContractTestFlow : ClientStartableFlow { + + @CordaInject + private lateinit var flowEngine: FlowEngine + + @CordaInject + private lateinit var jsonMarshallingService: JsonMarshallingService + + @Suspendable + override fun call(requestBody: ClientRequestBody): String { + val request = requestBody.getRequestBodyAs(jsonMarshallingService, Request::class.java) + when (request.command) { + "CREATE" -> flowEngine.subFlow(ChainableContractCreateTestFlow(request.rule)) + "UPDATE" -> { + val outputs = flowEngine.subFlow(ChainableContractCreateTestFlow("VALID")) + flowEngine.subFlow(ChainableContractUpdateTestFlow(request.rule, outputs)) + } + "DELETE" -> { + val outputs = flowEngine.subFlow(ChainableContractCreateTestFlow("VALID")) + flowEngine.subFlow(ChainableContractDeleteTestFlow(request.rule, outputs)) + } + else -> throw IllegalArgumentException("Invalid command type passed in") + } + return "success" + } +} + +private class Request(val command: String, val rule: String) diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/src/main/kotlin/com/r3/corda/test/utxo/chainable/workflow/ChainableContractUpdateTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/src/main/kotlin/com/r3/corda/test/utxo/chainable/workflow/ChainableContractUpdateTestFlow.kt new file mode 100644 index 00000000..5d2fafdf --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/src/main/kotlin/com/r3/corda/test/utxo/chainable/workflow/ChainableContractUpdateTestFlow.kt @@ -0,0 +1,86 @@ +package com.r3.corda.test.utxo.chainable.workflow + +import com.r3.corda.ledger.utxo.base.StaticPointer +import com.r3.corda.test.utxo.chainable.contract.MyChainableContract +import com.r3.corda.test.utxo.chainable.contract.MyChainableState +import com.r3.corda.test.utxo.chainable.contract.MyContractState +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.SubFlow +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.ledger.common.NotaryLookup +import net.corda.v5.ledger.utxo.StateAndRef +import net.corda.v5.ledger.utxo.StateRef +import net.corda.v5.ledger.utxo.UtxoLedgerService +import java.time.Instant +import java.time.temporal.ChronoUnit +import java.util.UUID + +class ChainableContractUpdateTestFlow( + private val rule: String, + private val stateAndRefs: List> +) : SubFlow>> { + + @CordaInject + private lateinit var memberLookup: MemberLookup + + @CordaInject + private lateinit var notaryLookup: NotaryLookup + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(): List> { + val key = memberLookup.myInfo().firstLedgerKey + val (inputs, outputs) = when (rule) { + "CONTRACT_RULE_UPDATE_INPUTS" -> { + emptyList() to listOf(MyContractState(UUID.randomUUID())) + } + "CONTRACT_RULE_UPDATE_OUTPUTS" -> { + stateAndRefs.map { it.ref } to emptyList() + } + "CONTRACT_RULE_UPDATE_POINTERS" -> { + stateAndRefs.map { it.ref } to stateAndRefs + .filter { it.state.contractState is MyChainableState } + .map { (it.state.contractState as MyChainableState).copy(previousStatePointer = null) } + } + "CONTRACT_RULE_UPDATE_EXCLUSIVE_POINTERS" -> { + val stateRef = stateAndRefs.first().ref + stateAndRefs.map { it.ref } to stateAndRefs + .filter { it.state.contractState is MyChainableState } + .map { + (it.state.contractState as MyChainableState).copy( + previousStatePointer = StaticPointer( + stateRef, + MyChainableState::class.java + ) + ) + } + } + "VALID" -> { + stateAndRefs.map { it.ref } to stateAndRefs + .filter { it.state.contractState is MyChainableState } + .map { + (it.state.contractState as MyChainableState).copy( + previousStatePointer = StaticPointer( + it.ref, + MyChainableState::class.java + ) + ) + } + } + else -> throw IllegalArgumentException("Invalid rule type passed in") + } + val transaction = utxoLedgerService.createTransactionBuilder() + .setNotary(notaryLookup.notaryServices.first().name) + .addInputStates(inputs) + .addOutputStates(outputs) + .addSignatories(key) + .addCommand(MyChainableContract.Update()) + .setTimeWindowUntil(Instant.now().plus(10, ChronoUnit.DAYS)) + .toSignedTransaction() + + return utxoLedgerService.finalize(transaction, emptyList()).transaction.outputStateAndRefs + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/src/main/kotlin/com/r3/corda/test/utxo/chainable/workflow/MemberInfoExtensions.kt b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/src/main/kotlin/com/r3/corda/test/utxo/chainable/workflow/MemberInfoExtensions.kt new file mode 100644 index 00000000..8f3e00d0 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-app/src/main/kotlin/com/r3/corda/test/utxo/chainable/workflow/MemberInfoExtensions.kt @@ -0,0 +1,7 @@ +package com.r3.corda.test.utxo.chainable.workflow + +import net.corda.v5.membership.MemberInfo +import java.security.PublicKey + +internal val MemberInfo.firstLedgerKey: PublicKey + get() = ledgerKeys.first() diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-contract/build.gradle b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-contract/build.gradle new file mode 100644 index 00000000..12ae8f99 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-contract/build.gradle @@ -0,0 +1,23 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' + id 'net.corda.plugins.cordapp-cpk2' +} + +cordapp { + targetPlatformVersion platformVersion.toInteger() + minimumPlatformVersion platformVersion.toInteger() + contract { + name "Advanced UTXO Ledger Test Chainable Contract" + versionId 1 + vendor "R3" + } +} + +dependencies { + cordaProvided platform("net.corda:corda-api:$cordaApiVersion") + cordaProvided 'org.jetbrains.kotlin:kotlin-osgi-bundle' + cordaProvided 'net.corda:corda-ledger-utxo' + + cordapp project(":chainable") + cordapp project(":ownable") +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/chainable/contract/MyChainableContract.kt b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/chainable/contract/MyChainableContract.kt new file mode 100644 index 00000000..973481e6 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/chainable/contract/MyChainableContract.kt @@ -0,0 +1,52 @@ +package com.r3.corda.test.utxo.chainable.contract + +import com.r3.corda.ledger.utxo.chainable.ChainableContract +import com.r3.corda.ledger.utxo.chainable.ChainableContractCommand +import com.r3.corda.ledger.utxo.chainable.ChainableContractCreateCommand +import com.r3.corda.ledger.utxo.chainable.ChainableContractDeleteCommand +import com.r3.corda.ledger.utxo.chainable.ChainableContractUpdateCommand +import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction + +class MyChainableContract : ChainableContract() { + + override fun getPermittedCommandTypes(): List>> { + return listOf(Create::class.java, Update::class.java, Delete::class.java) + } + + override fun onVerify(transaction: UtxoLedgerTransaction) { + + } + + class Create : ChainableContractCreateCommand() { + + override fun getContractStateType(): Class { + return MyChainableState::class.java + } + + override fun onVerify(transaction: UtxoLedgerTransaction) { + + } + } + + class Update : ChainableContractUpdateCommand() { + + override fun getContractStateType(): Class { + return MyChainableState::class.java + } + + override fun onVerify(transaction: UtxoLedgerTransaction) { + + } + } + + class Delete : ChainableContractDeleteCommand() { + + override fun getContractStateType(): Class { + return MyChainableState::class.java + } + + override fun onVerify(transaction: UtxoLedgerTransaction) { + + } + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/chainable/contract/MyChainableState.kt b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/chainable/contract/MyChainableState.kt new file mode 100644 index 00000000..5c837679 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/chainable/contract/MyChainableState.kt @@ -0,0 +1,28 @@ +package com.r3.corda.test.utxo.chainable.contract + +import com.r3.corda.ledger.utxo.base.StaticPointer +import com.r3.corda.ledger.utxo.chainable.ChainableState +import com.r3.corda.ledger.utxo.ownable.OwnableState +import net.corda.v5.ledger.utxo.BelongsToContract +import java.security.PublicKey +import java.util.UUID + +@BelongsToContract(MyChainableContract::class) +data class MyChainableState( + val id: UUID, + private val owner: PublicKey, + private val previousStatePointer: StaticPointer? +) : ChainableState, OwnableState { + + override fun getOwner(): PublicKey { + return owner + } + + override fun getParticipants(): List { + return listOf(owner) + } + + override fun getPreviousStatePointer(): StaticPointer? { + return previousStatePointer + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/chainable/contract/MyContractState.kt b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/chainable/contract/MyContractState.kt new file mode 100644 index 00000000..3b0f9640 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-chainable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/chainable/contract/MyContractState.kt @@ -0,0 +1,16 @@ +package com.r3.corda.test.utxo.chainable.contract + +import net.corda.v5.ledger.utxo.BelongsToContract +import net.corda.v5.ledger.utxo.ContractState +import java.security.PublicKey +import java.util.UUID + +@BelongsToContract(MyChainableContract::class) +data class MyContractState( + val id: UUID, +) : ContractState { + + override fun getParticipants(): List { + return emptyList() + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/build.gradle b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/build.gradle similarity index 90% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/build.gradle rename to e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/build.gradle index afb4518a..513dd342 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/build.gradle +++ b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/build.gradle @@ -7,7 +7,7 @@ cordapp { targetPlatformVersion platformVersion.toInteger() minimumPlatformVersion platformVersion.toInteger() workflow { - name "Advanced UTXO Ledger Demo Fungible Workflow" + name "Advanced UTXO Ledger Test Fungible Workflow" versionId 1 vendor "R3" } @@ -20,7 +20,7 @@ dependencies { cordapp project(":fungible") - cordapp project(':e2e-tests:cpbs:ledger-utxo-advanced-fungible-demo-contract') + cordapp project(':e2e-tests:cpbs:ledger-utxo-advanced-fungible-test-contract') // Common and API packages pulled in as transitive dependencies through client cordapp "com.r3.corda.notary.plugin.nonvalidating:notary-plugin-non-validating-client:$cordaNotaryPluginsVersion" diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/src/main/kotlin/com/r3/corda/test/utxo/fungible/workflow/FungibleContractCreateTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/src/main/kotlin/com/r3/corda/test/utxo/fungible/workflow/FungibleContractCreateTestFlow.kt new file mode 100644 index 00000000..67f08e3f --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/src/main/kotlin/com/r3/corda/test/utxo/fungible/workflow/FungibleContractCreateTestFlow.kt @@ -0,0 +1,72 @@ +package com.r3.corda.test.utxo.fungible.workflow + +import com.r3.corda.ledger.utxo.fungible.NumericInteger +import com.r3.corda.test.utxo.fungible.contract.MyContractState +import com.r3.corda.test.utxo.fungible.contract.MyFungibleContract +import com.r3.corda.test.utxo.fungible.contract.MyFungibleStateA +import com.r3.corda.test.utxo.fungible.contract.MyFungibleStateB +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.SubFlow +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.ledger.common.NotaryLookup +import net.corda.v5.ledger.utxo.StateAndRef +import net.corda.v5.ledger.utxo.UtxoLedgerService +import java.time.Instant +import java.time.temporal.ChronoUnit +import java.util.UUID + +class FungibleContractCreateTestFlow(private val rule: String) : SubFlow>> { + + @CordaInject + private lateinit var memberLookup: MemberLookup + + @CordaInject + private lateinit var notaryLookup: NotaryLookup + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(): List> { + val key = memberLookup.myInfo().firstLedgerKey + val outputs = when (rule) { + "CONTRACT_RULE_CREATE_OUTPUTS" -> listOf(MyContractState(UUID.randomUUID())) + "CONTRACT_RULE_CREATE_POSITIVE_QUANTITIES" -> { + listOf( + MyFungibleStateA( + quantity = NumericInteger.ZERO, + owner = key + ), + MyFungibleStateB( + quantity = NumericInteger.TEN, + owner = key + ), + ) + } + "VALID" -> { + listOf( + MyFungibleStateA( + quantity = NumericInteger.ONE, + owner = key + ), + MyFungibleStateB( + quantity = NumericInteger.TEN, + owner = key + ), + MyContractState(UUID.randomUUID()) + ) + } + else -> throw IllegalArgumentException("Invalid rule type passed in") + } + val transaction = utxoLedgerService.createTransactionBuilder() + .setNotary(notaryLookup.notaryServices.first().name) + .addOutputStates(outputs) + .addSignatories(key) + .addCommand(MyFungibleContract.Create()) + .setTimeWindowUntil(Instant.now().plus(10, ChronoUnit.DAYS)) + .toSignedTransaction() + + return utxoLedgerService.finalize(transaction, emptyList()).transaction.outputStateAndRefs + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/src/main/kotlin/com/r3/corda/test/utxo/fungible/workflow/FungibleContractDeleteTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/src/main/kotlin/com/r3/corda/test/utxo/fungible/workflow/FungibleContractDeleteTestFlow.kt new file mode 100644 index 00000000..7811e7ff --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/src/main/kotlin/com/r3/corda/test/utxo/fungible/workflow/FungibleContractDeleteTestFlow.kt @@ -0,0 +1,83 @@ +package com.r3.corda.test.utxo.fungible.workflow + +import com.r3.corda.ledger.utxo.fungible.FungibleState +import com.r3.corda.ledger.utxo.fungible.NumericInteger +import com.r3.corda.test.utxo.fungible.contract.MyFungibleContract +import com.r3.corda.test.utxo.fungible.contract.MyFungibleStateA +import com.r3.corda.test.utxo.fungible.contract.MyFungibleStateB +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.SubFlow +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.ledger.common.NotaryLookup +import net.corda.v5.ledger.utxo.StateAndRef +import net.corda.v5.ledger.utxo.UtxoLedgerService +import java.math.BigInteger +import java.time.Instant +import java.time.temporal.ChronoUnit + +class FungibleContractDeleteTestFlow( + private val rule: String, + private val stateAndRefs: List> +) : SubFlow>> { + + @CordaInject + private lateinit var memberLookup: MemberLookup + + @CordaInject + private lateinit var notaryLookup: NotaryLookup + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(): List> { + val key = memberLookup.myInfo().firstLedgerKey + val states = stateAndRefs.map { it.state.contractState } + val fungibleStateAStateAndRef = states.filterIsInstance().single() + val fungibleStateBStateAndRef = states.filterIsInstance().single() + val (inputs, outputs) = when (rule) { + "CONTRACT_RULE_DELETE_INPUTS" -> { + stateAndRefs + .filter { it.state.contractState !is FungibleState<*> } + .map { it.ref } to emptyList() + } + "CONTRACT_RULE_DELETE_POSITIVE_QUANTITIES" -> { + stateAndRefs.map { it.ref } to listOf( + fungibleStateAStateAndRef.copy(quantity = NumericInteger.ZERO), + fungibleStateBStateAndRef.copy() + ) + } + "CONTRACT_RULE_DELETE_SUM" -> { + stateAndRefs.map { it.ref } to listOf( + fungibleStateAStateAndRef.copy(), + fungibleStateBStateAndRef.copy() + ) + } + // Must be more inputs (deleted) states than outputs (created) per group. + // Fungible state A has increased compared to the input of state A so fails the check. + "CONTRACT_RULE_DELETE_GROUP_SUM" -> { + val total = (fungibleStateAStateAndRef.quantity + fungibleStateBStateAndRef.quantity).unscaledValue.toLong() + stateAndRefs.map { it.ref } to listOf( + fungibleStateAStateAndRef.copy(quantity = fungibleStateAStateAndRef.quantity + NumericInteger.ONE), + fungibleStateBStateAndRef.copy(quantity = NumericInteger(BigInteger.valueOf(total / 2 - 1))), + fungibleStateBStateAndRef.copy(quantity = NumericInteger(BigInteger.valueOf((total / 2) - 1))), + ) + } + "VALID" -> { + stateAndRefs.map { it.ref } to emptyList() + } + else -> throw IllegalArgumentException("Invalid rule type passed in") + } + val transaction = utxoLedgerService.createTransactionBuilder() + .setNotary(notaryLookup.notaryServices.first().name) + .addInputStates(inputs) + .addOutputStates(outputs) + .addSignatories(key) + .addCommand(MyFungibleContract.Delete()) + .setTimeWindowUntil(Instant.now().plus(10, ChronoUnit.DAYS)) + .toSignedTransaction() + + return utxoLedgerService.finalize(transaction, emptyList()).transaction.outputStateAndRefs + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/src/main/kotlin/com/r3/corda/test/utxo/fungible/workflow/FungibleContractTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/src/main/kotlin/com/r3/corda/test/utxo/fungible/workflow/FungibleContractTestFlow.kt new file mode 100644 index 00000000..84c6a30f --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/src/main/kotlin/com/r3/corda/test/utxo/fungible/workflow/FungibleContractTestFlow.kt @@ -0,0 +1,37 @@ +package com.r3.corda.test.utxo.fungible.workflow + +import net.corda.v5.application.flows.ClientRequestBody +import net.corda.v5.application.flows.ClientStartableFlow +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.FlowEngine +import net.corda.v5.application.marshalling.JsonMarshallingService +import net.corda.v5.base.annotations.Suspendable + +class FungibleContractTestFlow : ClientStartableFlow { + + @CordaInject + private lateinit var flowEngine: FlowEngine + + @CordaInject + private lateinit var jsonMarshallingService: JsonMarshallingService + + @Suspendable + override fun call(requestBody: ClientRequestBody): String { + val request = requestBody.getRequestBodyAs(jsonMarshallingService, Request::class.java) + when (request.command) { + "CREATE" -> flowEngine.subFlow(FungibleContractCreateTestFlow(request.rule)) + "UPDATE" -> { + val outputs = flowEngine.subFlow(FungibleContractCreateTestFlow("VALID")) + flowEngine.subFlow(FungibleContractUpdateTestFlow(request.rule, outputs)) + } + "DELETE" -> { + val outputs = flowEngine.subFlow(FungibleContractCreateTestFlow("VALID")) + flowEngine.subFlow(FungibleContractDeleteTestFlow(request.rule, outputs)) + } + else -> throw IllegalArgumentException("Invalid command type passed in") + } + return "success" + } +} + +private class Request(val command: String, val rule: String) diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/src/main/kotlin/com/r3/corda/test/utxo/fungible/workflow/FungibleContractUpdateTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/src/main/kotlin/com/r3/corda/test/utxo/fungible/workflow/FungibleContractUpdateTestFlow.kt new file mode 100644 index 00000000..03859d11 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/src/main/kotlin/com/r3/corda/test/utxo/fungible/workflow/FungibleContractUpdateTestFlow.kt @@ -0,0 +1,92 @@ +package com.r3.corda.test.utxo.fungible.workflow + +import com.r3.corda.ledger.utxo.fungible.NumericInteger +import com.r3.corda.test.utxo.fungible.contract.MyContractState +import com.r3.corda.test.utxo.fungible.contract.MyFungibleContract +import com.r3.corda.test.utxo.fungible.contract.MyFungibleState +import com.r3.corda.test.utxo.fungible.contract.MyFungibleStateA +import com.r3.corda.test.utxo.fungible.contract.MyFungibleStateB +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.SubFlow +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.ledger.common.NotaryLookup +import net.corda.v5.ledger.utxo.StateAndRef +import net.corda.v5.ledger.utxo.StateRef +import net.corda.v5.ledger.utxo.UtxoLedgerService +import java.math.BigInteger +import java.time.Instant +import java.time.temporal.ChronoUnit +import java.util.UUID + +class FungibleContractUpdateTestFlow( + private val rule: String, + private val stateAndRefs: List> +) : SubFlow>> { + + @CordaInject + private lateinit var memberLookup: MemberLookup + + @CordaInject + private lateinit var notaryLookup: NotaryLookup + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(): List> { + val key = memberLookup.myInfo().firstLedgerKey + val states = stateAndRefs.map { it.state.contractState } + val fungibleStateAStateAndRef = states.filterIsInstance().single() + val fungibleStateBStateAndRef = states.filterIsInstance().single() + val (inputs, outputs) = when (rule) { + "CONTRACT_RULE_UPDATE_INPUTS" -> { + emptyList() to listOf(MyContractState(UUID.randomUUID())) + } + "CONTRACT_RULE_UPDATE_OUTPUTS" -> { + stateAndRefs.map { it.ref } to emptyList() + } + "CONTRACT_RULE_UPDATE_POSITIVE_QUANTITIES" -> { + stateAndRefs.map { it.ref } to listOf( + fungibleStateAStateAndRef.copy(quantity = NumericInteger.ZERO), + fungibleStateBStateAndRef.copy() + ) + } + "CONTRACT_RULE_UPDATE_SUM" -> { + stateAndRefs.map { it.ref } to listOf( + fungibleStateAStateAndRef.copy(quantity = fungibleStateAStateAndRef.quantity + NumericInteger(BigInteger.valueOf(100))), + fungibleStateBStateAndRef.copy() + ) + } + // Groups fungible with each other must be equal. + // Fungible state A has increased but it is not fungible with B so the check fails. + "CONTRACT_RULE_UPDATE_GROUP_SUM" -> { + val total = (fungibleStateAStateAndRef.quantity + fungibleStateBStateAndRef.quantity).unscaledValue.toLong() + stateAndRefs.map { it.ref } to listOf( + fungibleStateAStateAndRef.copy(quantity = fungibleStateAStateAndRef.quantity + NumericInteger.ONE), + fungibleStateBStateAndRef.copy(quantity = NumericInteger(BigInteger.valueOf(total / 2))), + fungibleStateBStateAndRef.copy(quantity = NumericInteger(BigInteger.valueOf((total / 2) - 1))), + ) + } + "VALID" -> { + val total = (fungibleStateAStateAndRef.quantity + fungibleStateBStateAndRef.quantity).unscaledValue.toLong() + stateAndRefs.map { it.ref } to listOf( + fungibleStateAStateAndRef.copy(), + fungibleStateBStateAndRef.copy(quantity = NumericInteger(BigInteger.valueOf(total / 2))), + fungibleStateBStateAndRef.copy(quantity = NumericInteger(BigInteger.valueOf(total / 2))), + ) + } + else -> throw IllegalArgumentException("Invalid rule type passed in") + } + val transaction = utxoLedgerService.createTransactionBuilder() + .setNotary(notaryLookup.notaryServices.first().name) + .addInputStates(inputs) + .addOutputStates(outputs) + .addSignatories(key) + .addCommand(MyFungibleContract.Update()) + .setTimeWindowUntil(Instant.now().plus(10, ChronoUnit.DAYS)) + .toSignedTransaction() + + return utxoLedgerService.finalize(transaction, emptyList()).transaction.outputStateAndRefs + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/issuable/workflow/MemberInfoExtensions.kt b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/src/main/kotlin/com/r3/corda/test/utxo/fungible/workflow/MemberInfoExtensions.kt similarity index 76% rename from e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/issuable/workflow/MemberInfoExtensions.kt rename to e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/src/main/kotlin/com/r3/corda/test/utxo/fungible/workflow/MemberInfoExtensions.kt index a46090a0..306e9de4 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/issuable/workflow/MemberInfoExtensions.kt +++ b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-app/src/main/kotlin/com/r3/corda/test/utxo/fungible/workflow/MemberInfoExtensions.kt @@ -1,4 +1,4 @@ -package com.r3.corda.demo.utxo.issuable.workflow +package com.r3.corda.test.utxo.fungible.workflow import net.corda.v5.membership.MemberInfo import java.security.PublicKey diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/build.gradle b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/build.gradle new file mode 100644 index 00000000..a49396af --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/build.gradle @@ -0,0 +1,24 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' + id 'net.corda.plugins.cordapp-cpk2' +} + +cordapp { + targetPlatformVersion platformVersion.toInteger() + minimumPlatformVersion platformVersion.toInteger() + contract { + name "Advanced UTXO Ledger Test Fungible Contract" + versionId 1 + vendor "R3" + } +} + +dependencies { + cordaProvided platform("net.corda:corda-api:$cordaApiVersion") + cordaProvided 'org.jetbrains.kotlin:kotlin-osgi-bundle' + cordaProvided 'net.corda:corda-ledger-utxo' + + cordapp project(":fungible") + cordapp project(":issuable") + cordapp project(":ownable") +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/src/main/kotlin/com/r3/corda/test/utxo/fungible/contract/MyContractState.kt b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/src/main/kotlin/com/r3/corda/test/utxo/fungible/contract/MyContractState.kt new file mode 100644 index 00000000..8788f046 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/src/main/kotlin/com/r3/corda/test/utxo/fungible/contract/MyContractState.kt @@ -0,0 +1,16 @@ +package com.r3.corda.test.utxo.fungible.contract + +import net.corda.v5.ledger.utxo.BelongsToContract +import net.corda.v5.ledger.utxo.ContractState +import java.security.PublicKey +import java.util.UUID + +@BelongsToContract(MyFungibleContract::class) +data class MyContractState( + val id: UUID, +) : ContractState { + + override fun getParticipants(): List { + return emptyList() + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/src/main/kotlin/com/r3/corda/test/utxo/fungible/contract/MyFungibleContract.kt b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/src/main/kotlin/com/r3/corda/test/utxo/fungible/contract/MyFungibleContract.kt new file mode 100644 index 00000000..c2566515 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/src/main/kotlin/com/r3/corda/test/utxo/fungible/contract/MyFungibleContract.kt @@ -0,0 +1,52 @@ +package com.r3.corda.test.utxo.fungible.contract + +import com.r3.corda.ledger.utxo.fungible.FungibleContract +import com.r3.corda.ledger.utxo.fungible.FungibleContractCommand +import com.r3.corda.ledger.utxo.fungible.FungibleContractCreateCommand +import com.r3.corda.ledger.utxo.fungible.FungibleContractDeleteCommand +import com.r3.corda.ledger.utxo.fungible.FungibleContractUpdateCommand +import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction + +class MyFungibleContract : FungibleContract() { + + override fun getPermittedCommandTypes(): List>> { + return listOf(Create::class.java, Update::class.java, Delete::class.java) + } + + override fun onVerify(transaction: UtxoLedgerTransaction) { + + } + + class Create : FungibleContractCreateCommand() { + + override fun getContractStateType(): Class { + return MyFungibleState::class.java + } + + override fun onVerify(transaction: UtxoLedgerTransaction) { + + } + } + + class Update : FungibleContractUpdateCommand() { + + override fun getContractStateType(): Class { + return MyFungibleState::class.java + } + + override fun onVerify(transaction: UtxoLedgerTransaction) { + + } + } + + class Delete : FungibleContractDeleteCommand() { + + override fun getContractStateType(): Class { + return MyFungibleState::class.java + } + + override fun onVerify(transaction: UtxoLedgerTransaction) { + + } + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/src/main/kotlin/com/r3/corda/test/utxo/fungible/contract/MyFungibleState.kt b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/src/main/kotlin/com/r3/corda/test/utxo/fungible/contract/MyFungibleState.kt new file mode 100644 index 00000000..f96b8d3e --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/src/main/kotlin/com/r3/corda/test/utxo/fungible/contract/MyFungibleState.kt @@ -0,0 +1,9 @@ +package com.r3.corda.test.utxo.fungible.contract + +import com.r3.corda.ledger.utxo.fungible.FungibleState +import com.r3.corda.ledger.utxo.fungible.NumericInteger +import com.r3.corda.ledger.utxo.ownable.OwnableState +import net.corda.v5.ledger.utxo.BelongsToContract + +@BelongsToContract(MyFungibleContract::class) +interface MyFungibleState : FungibleState, OwnableState diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/src/main/kotlin/com/r3/corda/test/utxo/fungible/contract/MyFungibleStateA.kt b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/src/main/kotlin/com/r3/corda/test/utxo/fungible/contract/MyFungibleStateA.kt new file mode 100644 index 00000000..8d060f9e --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/src/main/kotlin/com/r3/corda/test/utxo/fungible/contract/MyFungibleStateA.kt @@ -0,0 +1,29 @@ +package com.r3.corda.test.utxo.fungible.contract + +import com.r3.corda.ledger.utxo.fungible.FungibleState +import com.r3.corda.ledger.utxo.fungible.NumericInteger +import net.corda.v5.ledger.utxo.BelongsToContract +import java.security.PublicKey + +@BelongsToContract(MyFungibleContract::class) +data class MyFungibleStateA( + private val quantity: NumericInteger, + private val owner: PublicKey +) : MyFungibleState { + + override fun getOwner(): PublicKey { + return owner + } + + override fun getParticipants(): List { + return listOf(owner) + } + + override fun getQuantity(): NumericInteger { + return quantity + } + + override fun isFungibleWith(other: FungibleState): Boolean { + return other is MyFungibleStateA + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/src/main/kotlin/com/r3/corda/test/utxo/fungible/contract/MyFungibleStateB.kt b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/src/main/kotlin/com/r3/corda/test/utxo/fungible/contract/MyFungibleStateB.kt new file mode 100644 index 00000000..69aad448 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-fungible-test-contract/src/main/kotlin/com/r3/corda/test/utxo/fungible/contract/MyFungibleStateB.kt @@ -0,0 +1,29 @@ +package com.r3.corda.test.utxo.fungible.contract + +import com.r3.corda.ledger.utxo.fungible.FungibleState +import com.r3.corda.ledger.utxo.fungible.NumericInteger +import net.corda.v5.ledger.utxo.BelongsToContract +import java.security.PublicKey + +@BelongsToContract(MyFungibleContract::class) +data class MyFungibleStateB( + private val quantity: NumericInteger, + private val owner: PublicKey +) : MyFungibleState { + + override fun getOwner(): PublicKey { + return owner + } + + override fun getParticipants(): List { + return listOf(owner) + } + + override fun getQuantity(): NumericInteger { + return quantity + } + + override fun isFungibleWith(other: FungibleState): Boolean { + return other is MyFungibleStateB + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/build.gradle b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/build.gradle similarity index 89% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/build.gradle rename to e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/build.gradle index 35715b97..fd365bbc 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/build.gradle +++ b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/build.gradle @@ -7,7 +7,7 @@ cordapp { targetPlatformVersion platformVersion.toInteger() minimumPlatformVersion platformVersion.toInteger() workflow { - name "Advanced UTXO Ledger Demo Identifiable Workflow" + name "Advanced UTXO Ledger Test Identifiable Workflow" versionId 1 vendor "R3" } @@ -18,7 +18,7 @@ dependencies { cordaProvided 'org.jetbrains.kotlin:kotlin-osgi-bundle' cordaProvided 'net.corda:corda-ledger-utxo' - cordapp project(':e2e-tests:cpbs:ledger-utxo-advanced-identifiable-demo-contract') + cordapp project(':e2e-tests:cpbs:ledger-utxo-advanced-identifiable-test-contract') // Common and API packages pulled in as transitive dependencies through client cordapp "com.r3.corda.notary.plugin.nonvalidating:notary-plugin-non-validating-client:$cordaNotaryPluginsVersion" diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiableContractCreateTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiableContractCreateTestFlow.kt new file mode 100644 index 00000000..e2c76e5c --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiableContractCreateTestFlow.kt @@ -0,0 +1,52 @@ +package com.r3.corda.test.utxo.identifiable.workflow + +import com.r3.corda.test.utxo.identifiable.contract.MyContractState +import com.r3.corda.test.utxo.identifiable.contract.MyIdentifiableContract +import com.r3.corda.test.utxo.identifiable.contract.MyIdentifiableState +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.SubFlow +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.ledger.common.NotaryLookup +import net.corda.v5.ledger.utxo.StateAndRef +import net.corda.v5.ledger.utxo.UtxoLedgerService +import java.time.Instant +import java.time.temporal.ChronoUnit +import java.util.UUID + +class IdentifiableContractCreateTestFlow(private val rule: String) : SubFlow>> { + + @CordaInject + private lateinit var memberLookup: MemberLookup + + @CordaInject + private lateinit var notaryLookup: NotaryLookup + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(): List> { + val key = memberLookup.myInfo().firstLedgerKey + val outputs = when (rule) { + "CONTRACT_RULE_CREATE_OUTPUTS" -> listOf(MyContractState(UUID.randomUUID())) + "VALID" -> { + listOf( + MyIdentifiableState(id = null, owner = key), + MyIdentifiableState(id = null, owner = key), + MyContractState(UUID.randomUUID()) + ) + } + else -> throw IllegalArgumentException("Invalid rule type passed in") + } + val transaction = utxoLedgerService.createTransactionBuilder() + .setNotary(notaryLookup.notaryServices.first().name) + .addOutputStates(outputs) + .addSignatories(key) + .addCommand(MyIdentifiableContract.Create()) + .setTimeWindowUntil(Instant.now().plus(10, ChronoUnit.DAYS)) + .toSignedTransaction() + + return utxoLedgerService.finalize(transaction, emptyList()).transaction.outputStateAndRefs + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiableContractDeleteTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiableContractDeleteTestFlow.kt new file mode 100644 index 00000000..b67e5b46 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiableContractDeleteTestFlow.kt @@ -0,0 +1,53 @@ +package com.r3.corda.test.utxo.identifiable.workflow + +import com.r3.corda.test.utxo.identifiable.contract.MyIdentifiableContract +import com.r3.corda.test.utxo.identifiable.contract.MyIdentifiableState +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.SubFlow +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.ledger.common.NotaryLookup +import net.corda.v5.ledger.utxo.StateAndRef +import net.corda.v5.ledger.utxo.UtxoLedgerService +import java.time.Instant +import java.time.temporal.ChronoUnit + +class IdentifiableContractDeleteTestFlow( + private val rule: String, + private val stateAndRefs: List> +) : SubFlow>> { + + @CordaInject + private lateinit var memberLookup: MemberLookup + + @CordaInject + private lateinit var notaryLookup: NotaryLookup + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(): List> { + val key = memberLookup.myInfo().firstLedgerKey + val inputs = when (rule) { + "CONTRACT_RULE_DELETE_INPUTS" -> { + stateAndRefs + .filter { it.state.contractState !is MyIdentifiableState } + .map { it.ref } + } + "VALID" -> { + stateAndRefs.map { it.ref } + } + else -> throw IllegalArgumentException("Invalid rule type passed in") + } + val transaction = utxoLedgerService.createTransactionBuilder() + .setNotary(notaryLookup.notaryServices.first().name) + .addInputStates(inputs) + .addSignatories(key) + .addCommand(MyIdentifiableContract.Delete()) + .setTimeWindowUntil(Instant.now().plus(10, ChronoUnit.DAYS)) + .toSignedTransaction() + + return utxoLedgerService.finalize(transaction, emptyList()).transaction.outputStateAndRefs + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiableContractTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiableContractTestFlow.kt new file mode 100644 index 00000000..43cdccf9 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiableContractTestFlow.kt @@ -0,0 +1,37 @@ +package com.r3.corda.test.utxo.identifiable.workflow + +import net.corda.v5.application.flows.ClientRequestBody +import net.corda.v5.application.flows.ClientStartableFlow +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.FlowEngine +import net.corda.v5.application.marshalling.JsonMarshallingService +import net.corda.v5.base.annotations.Suspendable + +class IdentifiableContractTestFlow : ClientStartableFlow { + + @CordaInject + private lateinit var flowEngine: FlowEngine + + @CordaInject + private lateinit var jsonMarshallingService: JsonMarshallingService + + @Suspendable + override fun call(requestBody: ClientRequestBody): String { + val request = requestBody.getRequestBodyAs(jsonMarshallingService, Request::class.java) + when (request.command) { + "CREATE" -> flowEngine.subFlow(IdentifiableContractCreateTestFlow(request.rule)) + "UPDATE" -> { + val outputs = flowEngine.subFlow(IdentifiableContractCreateTestFlow("VALID")) + flowEngine.subFlow(IdentifiableContractUpdateTestFlow(request.rule, outputs)) + } + "DELETE" -> { + val outputs = flowEngine.subFlow(IdentifiableContractCreateTestFlow("VALID")) + flowEngine.subFlow(IdentifiableContractDeleteTestFlow(request.rule, outputs)) + } + else -> throw IllegalArgumentException("Invalid command type passed in") + } + return "success" + } +} + +private class Request(val command: String, val rule: String) diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiableContractUpdateTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiableContractUpdateTestFlow.kt new file mode 100644 index 00000000..355cc21c --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiableContractUpdateTestFlow.kt @@ -0,0 +1,85 @@ +package com.r3.corda.test.utxo.identifiable.workflow + +import com.r3.corda.test.utxo.identifiable.contract.MyContractState +import com.r3.corda.test.utxo.identifiable.contract.MyIdentifiableContract +import com.r3.corda.test.utxo.identifiable.contract.MyIdentifiableState +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.SubFlow +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.ledger.common.NotaryLookup +import net.corda.v5.ledger.utxo.StateAndRef +import net.corda.v5.ledger.utxo.StateRef +import net.corda.v5.ledger.utxo.UtxoLedgerService +import java.time.Instant +import java.time.temporal.ChronoUnit +import java.util.UUID + +class IdentifiableContractUpdateTestFlow( + private val rule: String, + private val stateAndRefs: List> +) : SubFlow>> { + + @CordaInject + private lateinit var memberLookup: MemberLookup + + @CordaInject + private lateinit var notaryLookup: NotaryLookup + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(): List> { + val key = memberLookup.myInfo().firstLedgerKey + val (inputs, outputs) = when (rule) { + "CONTRACT_RULE_UPDATE_INPUTS" -> { + emptyList() to listOf(MyContractState(UUID.randomUUID())) + } + "CONTRACT_RULE_UPDATE_OUTPUTS" -> { + stateAndRefs.map { it.ref } to emptyList() + } + "CONTRACT_RULE_UPDATE_IDENTIFIER_EXCLUSIVITY" -> { + val stateRef = stateAndRefs.first().ref + stateAndRefs.map { it.ref } to stateAndRefs + .filter { it.state.contractState is MyIdentifiableState } + .map { + (it.state.contractState as MyIdentifiableState).copy( + id = stateRef + ) + } + } + // TODO Currently broken fixed in CORE-13473 + "CONTRACT_RULE_UPDATE_IDENTIFIER_EXCLUSIVITY MISSING_ID" -> { + stateAndRefs.map { it.ref } to stateAndRefs + .filter { it.state.contractState is MyIdentifiableState } + .let { stateAndRefs -> + val missingId = stateAndRefs.drop(1).map { it.state.contractState } + val hasId = stateAndRefs.take(1).map { (it.state.contractState as MyIdentifiableState).copy(id = it.ref) } + missingId + hasId + } + + } + "VALID" -> { + stateAndRefs.map { it.ref } to stateAndRefs + .filter { it.state.contractState is MyIdentifiableState } + .map { + (it.state.contractState as MyIdentifiableState).copy( + id = it.ref + ) + } + } + else -> throw IllegalArgumentException("Invalid rule type passed in") + } + val transaction = utxoLedgerService.createTransactionBuilder() + .setNotary(notaryLookup.notaryServices.first().name) + .addInputStates(inputs) + .addOutputStates(outputs) + .addSignatories(key) + .addCommand(MyIdentifiableContract.Update()) + .setTimeWindowUntil(Instant.now().plus(10, ChronoUnit.DAYS)) + .toSignedTransaction() + + return utxoLedgerService.finalize(transaction, emptyList()).transaction.outputStateAndRefs + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/query/IdentifiablePointerFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiablePointerFlow.kt similarity index 94% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/query/IdentifiablePointerFlow.kt rename to e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiablePointerFlow.kt index 8cf24332..dee17452 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/query/IdentifiablePointerFlow.kt +++ b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiablePointerFlow.kt @@ -1,4 +1,4 @@ -package com.r3.corda.demo.utxo.identifiable.workflow.query +package com.r3.corda.test.utxo.identifiable.workflow import com.r3.corda.ledger.utxo.identifiable.IdentifiablePointer import net.corda.v5.application.flows.ClientRequestBody @@ -9,9 +9,8 @@ import net.corda.v5.application.membership.MemberLookup import net.corda.v5.base.annotations.Suspendable import net.corda.v5.ledger.common.NotaryLookup import net.corda.v5.ledger.utxo.UtxoLedgerService -import com.r3.corda.demo.utxo.identifiable.contract.TestIdentifiableContract -import com.r3.corda.demo.utxo.identifiable.contract.TestIdentifiableState -import com.r3.corda.demo.utxo.identifiable.workflow.firstLedgerKey +import com.r3.corda.test.utxo.identifiable.contract.TestIdentifiableContract +import com.r3.corda.test.utxo.identifiable.contract.TestIdentifiableState import java.time.Instant import java.time.temporal.ChronoUnit diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/query/IdentifiableStateQueryFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiableStateQueryFlow.kt similarity index 95% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/query/IdentifiableStateQueryFlow.kt rename to e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiableStateQueryFlow.kt index e1134da7..eacd3fee 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/query/IdentifiableStateQueryFlow.kt +++ b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/IdentifiableStateQueryFlow.kt @@ -1,4 +1,4 @@ -package com.r3.corda.demo.utxo.identifiable.workflow.query +package com.r3.corda.test.utxo.identifiable.workflow import com.r3.corda.ledger.utxo.identifiable.query.IdentifiableStateQueries import net.corda.v5.application.flows.ClientRequestBody @@ -11,9 +11,8 @@ import net.corda.v5.ledger.common.NotaryLookup import net.corda.v5.ledger.utxo.StateAndRef import net.corda.v5.ledger.utxo.UtxoLedgerService import net.corda.v5.ledger.utxo.query.VaultNamedParameterizedQuery -import com.r3.corda.demo.utxo.identifiable.contract.TestIdentifiableContract -import com.r3.corda.demo.utxo.identifiable.contract.TestIdentifiableState -import com.r3.corda.demo.utxo.identifiable.workflow.firstLedgerKey +import com.r3.corda.test.utxo.identifiable.contract.TestIdentifiableContract +import com.r3.corda.test.utxo.identifiable.contract.TestIdentifiableState import java.time.Instant import java.time.temporal.ChronoUnit diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/MemberInfoExtensions.kt b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/MemberInfoExtensions.kt new file mode 100644 index 00000000..01a49841 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-app/src/main/kotlin/com/r3/corda/test/utxo/identifiable/workflow/MemberInfoExtensions.kt @@ -0,0 +1,7 @@ +package com.r3.corda.test.utxo.identifiable.workflow + +import net.corda.v5.membership.MemberInfo +import java.security.PublicKey + +internal val MemberInfo.firstLedgerKey: PublicKey + get() = ledgerKeys.first() diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-contract/build.gradle b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-contract/build.gradle new file mode 100644 index 00000000..2db3e374 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-contract/build.gradle @@ -0,0 +1,22 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' + id 'net.corda.plugins.cordapp-cpk2' +} + +cordapp { + targetPlatformVersion platformVersion.toInteger() + minimumPlatformVersion platformVersion.toInteger() + contract { + name "Advanced UTXO Ledger Test Identifiable Contract" + versionId 1 + vendor "R3" + } +} + +dependencies { + cordaProvided platform("net.corda:corda-api:$cordaApiVersion") + cordaProvided 'org.jetbrains.kotlin:kotlin-osgi-bundle' + cordaProvided 'net.corda:corda-ledger-utxo' + + cordapp project(":identifiable") +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/identifiable/contract/MyContractState.kt b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/identifiable/contract/MyContractState.kt new file mode 100644 index 00000000..2cabde28 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/identifiable/contract/MyContractState.kt @@ -0,0 +1,16 @@ +package com.r3.corda.test.utxo.identifiable.contract + +import net.corda.v5.ledger.utxo.BelongsToContract +import net.corda.v5.ledger.utxo.ContractState +import java.security.PublicKey +import java.util.UUID + +@BelongsToContract(MyIdentifiableContract::class) +data class MyContractState( + val id: UUID, +) : ContractState { + + override fun getParticipants(): List { + return emptyList() + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/identifiable/contract/MyIdentifiableContract.kt b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/identifiable/contract/MyIdentifiableContract.kt new file mode 100644 index 00000000..850462ef --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/identifiable/contract/MyIdentifiableContract.kt @@ -0,0 +1,52 @@ +package com.r3.corda.test.utxo.identifiable.contract + +import com.r3.corda.ledger.utxo.identifiable.IdentifiableContract +import com.r3.corda.ledger.utxo.identifiable.IdentifiableContractCommand +import com.r3.corda.ledger.utxo.identifiable.IdentifiableContractCreateCommand +import com.r3.corda.ledger.utxo.identifiable.IdentifiableContractDeleteCommand +import com.r3.corda.ledger.utxo.identifiable.IdentifiableContractUpdateCommand +import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction + +class MyIdentifiableContract : IdentifiableContract() { + + override fun getPermittedCommandTypes(): List>> { + return listOf(Create::class.java, Update::class.java, Delete::class.java) + } + + override fun onVerify(transaction: UtxoLedgerTransaction) { + + } + + class Create : IdentifiableContractCreateCommand() { + + override fun getContractStateType(): Class { + return MyIdentifiableState::class.java + } + + override fun onVerify(transaction: UtxoLedgerTransaction) { + + } + } + + class Update : IdentifiableContractUpdateCommand() { + + override fun getContractStateType(): Class { + return MyIdentifiableState::class.java + } + + override fun onVerify(transaction: UtxoLedgerTransaction) { + + } + } + + class Delete : IdentifiableContractDeleteCommand() { + + override fun getContractStateType(): Class { + return MyIdentifiableState::class.java + } + + override fun onVerify(transaction: UtxoLedgerTransaction) { + + } + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/identifiable/contract/MyIdentifiableState.kt b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/identifiable/contract/MyIdentifiableState.kt new file mode 100644 index 00000000..d56c251c --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/identifiable/contract/MyIdentifiableState.kt @@ -0,0 +1,21 @@ +package com.r3.corda.test.utxo.identifiable.contract + +import com.r3.corda.ledger.utxo.identifiable.IdentifiableState +import net.corda.v5.ledger.utxo.BelongsToContract +import net.corda.v5.ledger.utxo.StateRef +import java.security.PublicKey + +@BelongsToContract(MyIdentifiableContract::class) +data class MyIdentifiableState( + private val id: StateRef?, + val owner: PublicKey, +) : IdentifiableState { + + override fun getId(): StateRef? { + return id + } + + override fun getParticipants(): List { + return listOf(owner) + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/identifiable/contract/TestIdentifiableState.kt b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/identifiable/contract/TestIdentifiableState.kt new file mode 100644 index 00000000..ceb9eb2b --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/identifiable/contract/TestIdentifiableState.kt @@ -0,0 +1,46 @@ +package com.r3.corda.test.utxo.identifiable.contract + +import com.r3.corda.ledger.utxo.identifiable.IdentifiableContract +import com.r3.corda.ledger.utxo.identifiable.IdentifiableContractCommand +import com.r3.corda.ledger.utxo.identifiable.IdentifiableContractCreateCommand +import com.r3.corda.ledger.utxo.identifiable.IdentifiableContractDeleteCommand +import com.r3.corda.ledger.utxo.identifiable.IdentifiableContractUpdateCommand +import com.r3.corda.ledger.utxo.identifiable.IdentifiableState +import net.corda.v5.ledger.utxo.BelongsToContract +import net.corda.v5.ledger.utxo.StateRef +import java.security.PublicKey + +@BelongsToContract(TestIdentifiableContract::class) +data class TestIdentifiableState(private val id: StateRef?, private val participants: List) : IdentifiableState { + + override fun getParticipants(): List { + return participants + } + + override fun getId(): StateRef? { + return id + } +} + +class TestIdentifiableContract : IdentifiableContract() { + + class Create : IdentifiableContractCreateCommand() { + override fun getContractStateType(): Class { + return TestIdentifiableState::class.java + } + } + class Update: IdentifiableContractUpdateCommand() { + override fun getContractStateType(): Class { + return TestIdentifiableState::class.java + } + } + class Delete: IdentifiableContractDeleteCommand() { + override fun getContractStateType(): Class { + return TestIdentifiableState::class.java + } + } + + override fun getPermittedCommandTypes(): List>> { + return listOf(Create::class.java, Update::class.java, Delete::class.java) + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-app/build.gradle b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/build.gradle similarity index 89% rename from e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-app/build.gradle rename to e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/build.gradle index 9feb87ba..5086da71 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-app/build.gradle +++ b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/build.gradle @@ -7,7 +7,7 @@ cordapp { targetPlatformVersion platformVersion as Integer minimumPlatformVersion platformVersion as Integer workflow { - name "Advanced UTXO Ledger Demo Issuable Workflow" + name "Advanced UTXO Ledger Test Issuable Workflow" versionId 1 vendor "R3" } @@ -17,7 +17,7 @@ dependencies { cordaProvided platform("net.corda:corda-api:$cordaApiVersion") cordaProvided 'net.corda:corda-ledger-utxo' - cordapp project(':e2e-tests:cpbs:ledger-utxo-advanced-issuable-demo-contract') + cordapp project(':e2e-tests:cpbs:ledger-utxo-advanced-issuable-test-contract') // Common and API packages pulled in as transitive dependencies through client cordapp "com.r3.corda.notary.plugin.nonvalidating:notary-plugin-non-validating-client:$cordaNotaryPluginsVersion" diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/IssuableContractCreateTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/IssuableContractCreateTestFlow.kt new file mode 100644 index 00000000..88a860c6 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/IssuableContractCreateTestFlow.kt @@ -0,0 +1,81 @@ +package com.r3.corda.test.utxo.issuable.workflow + +import com.r3.corda.test.utxo.issuable.contract.MyContractState +import com.r3.corda.test.utxo.issuable.contract.MyIssuableContract +import com.r3.corda.test.utxo.issuable.contract.MyIssuableState +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.InitiatedBy +import net.corda.v5.application.flows.InitiatingFlow +import net.corda.v5.application.flows.ResponderFlow +import net.corda.v5.application.flows.SubFlow +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.application.messaging.FlowMessaging +import net.corda.v5.application.messaging.FlowSession +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.base.types.MemberX500Name +import net.corda.v5.ledger.common.NotaryLookup +import net.corda.v5.ledger.utxo.StateAndRef +import net.corda.v5.ledger.utxo.UtxoLedgerService +import java.security.PublicKey +import java.time.Instant +import java.time.temporal.ChronoUnit +import java.util.UUID + +@InitiatingFlow(protocol = "IssuableContractCreateTestFlow") +class IssuableContractCreateTestFlow( + private val rule: String, + private val issuer: PublicKey, + private val issuerName: MemberX500Name +) : SubFlow>> { + + @CordaInject + private lateinit var flowMessaging: FlowMessaging + + @CordaInject + private lateinit var memberLookup: MemberLookup + + @CordaInject + private lateinit var notaryLookup: NotaryLookup + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(): List> { + val key = memberLookup.myInfo().firstLedgerKey + val signatories = when (rule) { + "CONTRACT_RULE_CREATE_SIGNATORIES" -> listOf(key) + "VALID" -> listOf(issuer, key) + else -> throw IllegalArgumentException("Invalid rule type passed in") + } + val outputs = listOf( + MyIssuableState(issuer, issuerName, participants = listOf(key)), + MyIssuableState(issuer, issuerName, participants = listOf(key)), + MyContractState(UUID.randomUUID()) + ) + val transaction = utxoLedgerService.createTransactionBuilder() + .setNotary(notaryLookup.notaryServices.first().name) + .addOutputStates(outputs) + .addSignatories(signatories) + .addCommand(MyIssuableContract.Create()) + .setTimeWindowUntil(Instant.now().plus(10, ChronoUnit.DAYS)) + .toSignedTransaction() + + val session = flowMessaging.initiateFlow(issuerName) + return utxoLedgerService.finalize(transaction, listOf(session)).transaction.outputStateAndRefs + } +} + +@InitiatedBy(protocol = "IssuableContractCreateTestFlow") +class IssuableContractCreateTestResponderFlow : ResponderFlow { + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(session: FlowSession) { + utxoLedgerService.receiveFinality(session) { + // accept + } + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/IssuableContractDeleteTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/IssuableContractDeleteTestFlow.kt new file mode 100644 index 00000000..9041dddf --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/IssuableContractDeleteTestFlow.kt @@ -0,0 +1,74 @@ +package com.r3.corda.test.utxo.issuable.workflow + +import com.r3.corda.test.utxo.issuable.contract.MyIssuableContract +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.InitiatedBy +import net.corda.v5.application.flows.InitiatingFlow +import net.corda.v5.application.flows.ResponderFlow +import net.corda.v5.application.flows.SubFlow +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.application.messaging.FlowMessaging +import net.corda.v5.application.messaging.FlowSession +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.base.types.MemberX500Name +import net.corda.v5.ledger.common.NotaryLookup +import net.corda.v5.ledger.utxo.StateAndRef +import net.corda.v5.ledger.utxo.UtxoLedgerService +import java.security.PublicKey +import java.time.Instant +import java.time.temporal.ChronoUnit + +@InitiatingFlow(protocol = "IssuableContractDeleteTestFlow") +class IssuableContractDeleteTestFlow( + private val rule: String, + private val issuer: PublicKey, + private val issuerName: MemberX500Name, + private val stateAndRefs: List> +) : SubFlow>> { + + @CordaInject + private lateinit var flowMessaging: FlowMessaging + + @CordaInject + private lateinit var memberLookup: MemberLookup + + @CordaInject + private lateinit var notaryLookup: NotaryLookup + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(): List> { + val key = memberLookup.myInfo().firstLedgerKey + val signatories = when (rule) { + "CONTRACT_RULE_DELETE_SIGNATORIES" -> listOf(key) + "VALID" -> listOf(issuer, key) + else -> throw IllegalArgumentException("Invalid rule type passed in") + } + val transaction = utxoLedgerService.createTransactionBuilder() + .setNotary(notaryLookup.notaryServices.first().name) + .addInputStates(stateAndRefs.map { it.ref }) + .addSignatories(signatories) + .addCommand(MyIssuableContract.Delete()) + .setTimeWindowUntil(Instant.now().plus(10, ChronoUnit.DAYS)) + .toSignedTransaction() + + val session = flowMessaging.initiateFlow(issuerName) + return utxoLedgerService.finalize(transaction, listOf(session)).transaction.outputStateAndRefs + } +} + +@InitiatedBy(protocol = "IssuableContractDeleteTestFlow") +class IssuableContractDeleteTestResponderFlow : ResponderFlow { + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(session: FlowSession) { + utxoLedgerService.receiveFinality(session) { + // accept + } + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/IssuableContractTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/IssuableContractTestFlow.kt new file mode 100644 index 00000000..3e8b82f8 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/IssuableContractTestFlow.kt @@ -0,0 +1,42 @@ +package com.r3.corda.test.utxo.issuable.workflow + +import net.corda.v5.application.flows.ClientRequestBody +import net.corda.v5.application.flows.ClientStartableFlow +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.FlowEngine +import net.corda.v5.application.marshalling.JsonMarshallingService +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.base.types.MemberX500Name + +class IssuableContractTestFlow : ClientStartableFlow { + + @CordaInject + private lateinit var flowEngine: FlowEngine + + @CordaInject + private lateinit var jsonMarshallingService: JsonMarshallingService + + @CordaInject + private lateinit var memberLookup: MemberLookup + + @Suspendable + override fun call(requestBody: ClientRequestBody): String { + val request = requestBody.getRequestBodyAs(jsonMarshallingService, Request::class.java) + val issuerName = MemberX500Name.parse(request.issuer) + val issuer = requireNotNull(memberLookup.lookup(issuerName)) { + "Issuer $issuerName does not exist in the network" + }.firstLedgerKey + when (request.command) { + "CREATE" -> flowEngine.subFlow(IssuableContractCreateTestFlow(request.rule, issuer, issuerName)) + "DELETE" -> { + val outputs = flowEngine.subFlow(IssuableContractCreateTestFlow("VALID", issuer, issuerName)) + flowEngine.subFlow(IssuableContractDeleteTestFlow(request.rule, issuer, issuerName, outputs)) + } + else -> throw IllegalArgumentException("Invalid command type passed in") + } + return "success" + } +} + +private class Request(val command: String, val rule: String, val issuer: String) diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/issuable/workflow/query/IssuableStateQueryFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/IssuableStateQueryFlow.kt similarity index 95% rename from e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/issuable/workflow/query/IssuableStateQueryFlow.kt rename to e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/IssuableStateQueryFlow.kt index 83cbd286..92de9024 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/issuable/workflow/query/IssuableStateQueryFlow.kt +++ b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/IssuableStateQueryFlow.kt @@ -1,4 +1,4 @@ -package com.r3.corda.demo.utxo.issuable.workflow.query +package com.r3.corda.test.utxo.issuable.workflow import com.r3.corda.ledger.utxo.issuable.query.IssuableStateQueries import net.corda.v5.application.crypto.DigestService @@ -13,9 +13,8 @@ import net.corda.v5.ledger.common.NotaryLookup import net.corda.v5.ledger.utxo.StateAndRef import net.corda.v5.ledger.utxo.UtxoLedgerService import net.corda.v5.ledger.utxo.query.VaultNamedParameterizedQuery -import com.r3.corda.demo.utxo.issuable.contract.TestIssuableContract -import com.r3.corda.demo.utxo.issuable.contract.TestIssuableState -import com.r3.corda.demo.utxo.issuable.workflow.firstLedgerKey +import com.r3.corda.test.utxo.issuable.contract.TestIssuableContract +import com.r3.corda.test.utxo.issuable.contract.TestIssuableState import java.security.PublicKey import java.time.Instant import java.time.temporal.ChronoUnit diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/MemberInfoExtensions.kt b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/MemberInfoExtensions.kt new file mode 100644 index 00000000..ba378155 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/MemberInfoExtensions.kt @@ -0,0 +1,7 @@ +package com.r3.corda.test.utxo.issuable.workflow + +import net.corda.v5.membership.MemberInfo +import java.security.PublicKey + +internal val MemberInfo.firstLedgerKey: PublicKey + get() = ledgerKeys.first() diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/issuable/workflow/query/WellKnownIssuableStateQueryFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/WellKnownIssuableStateQueryFlow.kt similarity index 95% rename from e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/issuable/workflow/query/WellKnownIssuableStateQueryFlow.kt rename to e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/WellKnownIssuableStateQueryFlow.kt index 0a0fd3c5..da02d206 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/issuable/workflow/query/WellKnownIssuableStateQueryFlow.kt +++ b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-app/src/main/kotlin/com/r3/corda/test/utxo/issuable/workflow/WellKnownIssuableStateQueryFlow.kt @@ -1,4 +1,4 @@ -package com.r3.corda.demo.utxo.issuable.workflow.query +package com.r3.corda.test.utxo.issuable.workflow import com.r3.corda.ledger.utxo.issuable.query.WellKnownIssuableStateQueries import net.corda.v5.application.flows.ClientRequestBody @@ -11,9 +11,8 @@ import net.corda.v5.ledger.common.NotaryLookup import net.corda.v5.ledger.utxo.StateAndRef import net.corda.v5.ledger.utxo.UtxoLedgerService import net.corda.v5.ledger.utxo.query.VaultNamedParameterizedQuery -import com.r3.corda.demo.utxo.issuable.contract.TestIssuableContract -import com.r3.corda.demo.utxo.issuable.contract.TestIssuableState -import com.r3.corda.demo.utxo.issuable.workflow.firstLedgerKey +import com.r3.corda.test.utxo.issuable.contract.TestIssuableContract +import com.r3.corda.test.utxo.issuable.contract.TestIssuableState import java.time.Instant import java.time.temporal.ChronoUnit diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-contract/build.gradle b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-contract/build.gradle similarity index 89% rename from e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-contract/build.gradle rename to e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-contract/build.gradle index 58d36fb3..e05fa651 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-contract/build.gradle +++ b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-contract/build.gradle @@ -7,7 +7,7 @@ cordapp { targetPlatformVersion platformVersion as Integer minimumPlatformVersion platformVersion as Integer contract { - name "Advanced UTXO Ledger Demo Issuable Contract" + name "Advanced UTXO Ledger Test Issuable Contract" versionId 1 vendor "R3" } diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/issuable/contract/MyContractState.kt b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/issuable/contract/MyContractState.kt new file mode 100644 index 00000000..1ba6fc01 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/issuable/contract/MyContractState.kt @@ -0,0 +1,16 @@ +package com.r3.corda.test.utxo.issuable.contract + +import net.corda.v5.ledger.utxo.BelongsToContract +import net.corda.v5.ledger.utxo.ContractState +import java.security.PublicKey +import java.util.UUID + +@BelongsToContract(TestIssuableContract::class) +data class MyContractState( + val id: UUID, +) : ContractState { + + override fun getParticipants(): List { + return emptyList() + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/issuable/contract/MyIssuableState.kt b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/issuable/contract/MyIssuableState.kt new file mode 100644 index 00000000..1bff43b0 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/issuable/contract/MyIssuableState.kt @@ -0,0 +1,47 @@ +package com.r3.corda.test.utxo.issuable.contract + +import com.r3.corda.ledger.utxo.issuable.IssuableConstraints +import com.r3.corda.ledger.utxo.issuable.IssuableState +import com.r3.corda.ledger.utxo.issuable.WellKnownIssuableState +import net.corda.v5.base.types.MemberX500Name +import net.corda.v5.ledger.utxo.BelongsToContract +import net.corda.v5.ledger.utxo.Command +import net.corda.v5.ledger.utxo.Contract +import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction +import java.security.PublicKey + +@BelongsToContract(MyIssuableContract::class) +data class MyIssuableState( + private val issuer: PublicKey, + private val issuerName: MemberX500Name, + private val participants: List +) : IssuableState, WellKnownIssuableState { + + override fun getParticipants(): List { + return participants + } + + override fun getIssuer(): PublicKey { + return issuer + } + + override fun getIssuerName(): MemberX500Name { + return issuerName + } +} + +class MyIssuableContract : Contract { + + class Create : Command + class Delete : Command + + override fun verify(transaction: UtxoLedgerTransaction) { + val commands = transaction.commands + if (commands.any { it::class.java == Create::class.java }) { + IssuableConstraints.verifyCreate(transaction) + } + if (commands.any { it::class.java == Delete::class.java }) { + IssuableConstraints.verifyDelete(transaction) + } + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/issuable/contract/TestIssuableState.kt b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/issuable/contract/TestIssuableState.kt similarity index 91% rename from e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/issuable/contract/TestIssuableState.kt rename to e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/issuable/contract/TestIssuableState.kt index 9421d8e5..fcb18b42 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/issuable/contract/TestIssuableState.kt +++ b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/issuable/contract/TestIssuableState.kt @@ -1,4 +1,4 @@ -package com.r3.corda.demo.utxo.issuable.contract +package com.r3.corda.test.utxo.issuable.contract import com.r3.corda.ledger.utxo.issuable.IssuableState import com.r3.corda.ledger.utxo.issuable.WellKnownIssuableState @@ -25,14 +25,13 @@ data class TestIssuableState( } override fun getIssuerName(): MemberX500Name { - return issuerName + return issuerName } } class TestIssuableContract : Contract { class Create : Command - class Update : Command class Delete : Command override fun verify(transaction: UtxoLedgerTransaction) { diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/issuable/contract/query/json/TestIssuableStateVaultJsonFactory.kt b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/issuable/contract/query/json/TestIssuableStateVaultJsonFactory.kt similarity index 79% rename from e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/issuable/contract/query/json/TestIssuableStateVaultJsonFactory.kt rename to e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/issuable/contract/query/json/TestIssuableStateVaultJsonFactory.kt index 83bb01eb..95e94b05 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-issuable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/issuable/contract/query/json/TestIssuableStateVaultJsonFactory.kt +++ b/e2e-tests/cpbs/ledger-utxo-advanced-issuable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/issuable/contract/query/json/TestIssuableStateVaultJsonFactory.kt @@ -1,8 +1,8 @@ -package com.r3.corda.demo.utxo.issuable.contract.query.json +package com.r3.corda.test.utxo.issuable.contract.query.json import net.corda.v5.application.marshalling.JsonMarshallingService import net.corda.v5.ledger.utxo.query.json.ContractStateVaultJsonFactory -import com.r3.corda.demo.utxo.issuable.contract.TestIssuableState +import com.r3.corda.test.utxo.issuable.contract.TestIssuableState class TestIssuableStateVaultJsonFactory : ContractStateVaultJsonFactory { diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-app/build.gradle b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/build.gradle similarity index 89% rename from e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-app/build.gradle rename to e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/build.gradle index 7a8808c0..2be1157c 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-app/build.gradle +++ b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/build.gradle @@ -7,7 +7,7 @@ cordapp { targetPlatformVersion platformVersion as Integer minimumPlatformVersion platformVersion as Integer workflow { - name "Advanced UTXO Ledger Demo Ownable Workflow" + name "Advanced UTXO Ledger Test Ownable Workflow" versionId 1 vendor "R3" } @@ -17,7 +17,7 @@ dependencies { cordaProvided platform("net.corda:corda-api:$cordaApiVersion") cordaProvided 'net.corda:corda-ledger-utxo' - cordapp project(':e2e-tests:cpbs:ledger-utxo-advanced-ownable-demo-contract') + cordapp project(':e2e-tests:cpbs:ledger-utxo-advanced-ownable-test-contract') // Common and API packages pulled in as transitive dependencies through client cordapp "com.r3.corda.notary.plugin.nonvalidating:notary-plugin-non-validating-client:$cordaNotaryPluginsVersion" diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/ownable/workflow/MemberInfoExtensions.kt b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/MemberInfoExtensions.kt similarity index 76% rename from e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/ownable/workflow/MemberInfoExtensions.kt rename to e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/MemberInfoExtensions.kt index c5084799..8fa5e983 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/ownable/workflow/MemberInfoExtensions.kt +++ b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/MemberInfoExtensions.kt @@ -1,4 +1,4 @@ -package com.r3.corda.demo.utxo.ownable.workflow +package com.r3.corda.test.utxo.ownable.workflow import net.corda.v5.membership.MemberInfo import java.security.PublicKey diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/OwnableContractCreateTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/OwnableContractCreateTestFlow.kt new file mode 100644 index 00000000..162ee930 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/OwnableContractCreateTestFlow.kt @@ -0,0 +1,80 @@ +package com.r3.corda.test.utxo.ownable.workflow + +import com.r3.corda.test.utxo.ownable.contract.MyContractState +import com.r3.corda.test.utxo.ownable.contract.MyOwnableContract +import com.r3.corda.test.utxo.ownable.contract.MyOwnableState +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.InitiatedBy +import net.corda.v5.application.flows.InitiatingFlow +import net.corda.v5.application.flows.ResponderFlow +import net.corda.v5.application.flows.SubFlow +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.application.messaging.FlowMessaging +import net.corda.v5.application.messaging.FlowSession +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.base.types.MemberX500Name +import net.corda.v5.ledger.common.NotaryLookup +import net.corda.v5.ledger.utxo.StateAndRef +import net.corda.v5.ledger.utxo.UtxoLedgerService +import java.security.PublicKey +import java.time.Instant +import java.time.temporal.ChronoUnit +import java.util.UUID + +@InitiatingFlow(protocol = "OwnableContractCreateTestFlow") +class OwnableContractCreateTestFlow( + private val rule: String, + private val owner: PublicKey, + private val ownerName: MemberX500Name +) : SubFlow>> { + + @CordaInject + private lateinit var flowMessaging: FlowMessaging + + @CordaInject + private lateinit var memberLookup: MemberLookup + + @CordaInject + private lateinit var notaryLookup: NotaryLookup + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(): List> { + val key = memberLookup.myInfo().firstLedgerKey + val signatories = when (rule) { + "VALID" -> listOf(owner, key) + else -> throw IllegalArgumentException("Invalid rule type passed in") + } + val outputs = listOf( + MyOwnableState(owner, ownerName, participants = listOf(key)), + MyOwnableState(owner, ownerName, participants = listOf(key)), + MyContractState(UUID.randomUUID()) + ) + val transaction = utxoLedgerService.createTransactionBuilder() + .setNotary(notaryLookup.notaryServices.first().name) + .addOutputStates(outputs) + .addSignatories(signatories) + .addCommand(MyOwnableContract.Create()) + .setTimeWindowUntil(Instant.now().plus(10, ChronoUnit.DAYS)) + .toSignedTransaction() + + val session = flowMessaging.initiateFlow(ownerName) + return utxoLedgerService.finalize(transaction, listOf(session)).transaction.outputStateAndRefs + } +} + +@InitiatedBy(protocol = "OwnableContractCreateTestFlow") +class OwnableContractCreateTestResponderFlow : ResponderFlow { + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(session: FlowSession) { + utxoLedgerService.receiveFinality(session) { + // accept + } + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/OwnableContractTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/OwnableContractTestFlow.kt new file mode 100644 index 00000000..66b58826 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/OwnableContractTestFlow.kt @@ -0,0 +1,41 @@ +package com.r3.corda.test.utxo.ownable.workflow + +import net.corda.v5.application.flows.ClientRequestBody +import net.corda.v5.application.flows.ClientStartableFlow +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.FlowEngine +import net.corda.v5.application.marshalling.JsonMarshallingService +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.base.types.MemberX500Name + +class OwnableContractTestFlow : ClientStartableFlow { + + @CordaInject + private lateinit var flowEngine: FlowEngine + + @CordaInject + private lateinit var jsonMarshallingService: JsonMarshallingService + + @CordaInject + private lateinit var memberLookup: MemberLookup + + @Suspendable + override fun call(requestBody: ClientRequestBody): String { + val request = requestBody.getRequestBodyAs(jsonMarshallingService, Request::class.java) + val ownerName = MemberX500Name.parse(request.owner) + val owner = requireNotNull(memberLookup.lookup(ownerName)) { + "owner $ownerName does not exist in the network" + }.firstLedgerKey + when (request.command) { + "UPDATE" -> { + val outputs = flowEngine.subFlow(OwnableContractCreateTestFlow("VALID", owner, ownerName)) + flowEngine.subFlow(OwnableContractUpdateTestFlow(request.rule, owner, ownerName, outputs)) + } + else -> throw IllegalArgumentException("Invalid command type passed in") + } + return "success" + } +} + +private class Request(val command: String, val rule: String, val owner: String) diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/OwnableContractUpdateTestFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/OwnableContractUpdateTestFlow.kt new file mode 100644 index 00000000..7683e261 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/OwnableContractUpdateTestFlow.kt @@ -0,0 +1,85 @@ +package com.r3.corda.test.utxo.ownable.workflow + +import com.r3.corda.test.utxo.ownable.contract.MyContractState +import com.r3.corda.test.utxo.ownable.contract.MyOwnableContract +import com.r3.corda.test.utxo.ownable.contract.MyOwnableState +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.InitiatedBy +import net.corda.v5.application.flows.InitiatingFlow +import net.corda.v5.application.flows.ResponderFlow +import net.corda.v5.application.flows.SubFlow +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.application.messaging.FlowMessaging +import net.corda.v5.application.messaging.FlowSession +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.base.types.MemberX500Name +import net.corda.v5.ledger.common.NotaryLookup +import net.corda.v5.ledger.utxo.StateAndRef +import net.corda.v5.ledger.utxo.UtxoLedgerService +import java.security.PublicKey +import java.time.Instant +import java.time.temporal.ChronoUnit +import java.util.UUID + +@InitiatingFlow(protocol = "OwnableContractUpdateTestFlow") +class OwnableContractUpdateTestFlow( + private val rule: String, + private val owner: PublicKey, + private val ownerName: MemberX500Name, + private val stateAndRefs: List> +) : SubFlow>> { + + @CordaInject + private lateinit var flowMessaging: FlowMessaging + + @CordaInject + private lateinit var memberLookup: MemberLookup + + @CordaInject + private lateinit var notaryLookup: NotaryLookup + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(): List> { + val info = memberLookup.myInfo() + val name = info.name + val key = info.firstLedgerKey + val signatories = when (rule) { + "CONTRACT_RULE_UPDATE_SIGNATORIES" -> listOf(key) + "VALID" -> listOf(owner, key) + else -> throw IllegalArgumentException("Invalid rule type passed in") + } + val outputs = listOf( + MyOwnableState(owner = key, ownerName = name, participants = listOf(key)), + MyOwnableState(owner = key, ownerName = name, participants = listOf(key)), + MyContractState(UUID.randomUUID()) + ) + val transaction = utxoLedgerService.createTransactionBuilder() + .setNotary(notaryLookup.notaryServices.first().name) + .addInputStates(stateAndRefs.map { it.ref }) + .addOutputStates(outputs) + .addSignatories(signatories) + .addCommand(MyOwnableContract.Update()) + .setTimeWindowUntil(Instant.now().plus(10, ChronoUnit.DAYS)) + .toSignedTransaction() + + val session = flowMessaging.initiateFlow(ownerName) + return utxoLedgerService.finalize(transaction, listOf(session)).transaction.outputStateAndRefs + } +} + +@InitiatedBy(protocol = "OwnableContractUpdateTestFlow") +class OwnableContractDeleteTestResponderFlow : ResponderFlow { + + @CordaInject + private lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(session: FlowSession) { + utxoLedgerService.receiveFinality(session) { + // accept + } + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/ownable/workflow/query/OwnableStateQueryFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/OwnableStateQueryFlow.kt similarity index 95% rename from e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/ownable/workflow/query/OwnableStateQueryFlow.kt rename to e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/OwnableStateQueryFlow.kt index c7601705..04c08923 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/ownable/workflow/query/OwnableStateQueryFlow.kt +++ b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/OwnableStateQueryFlow.kt @@ -1,4 +1,4 @@ -package com.r3.corda.demo.utxo.ownable.workflow.query +package com.r3.corda.test.utxo.ownable.workflow import com.r3.corda.ledger.utxo.ownable.query.OwnableStateQueries import net.corda.v5.application.crypto.DigestService @@ -13,9 +13,8 @@ import net.corda.v5.ledger.common.NotaryLookup import net.corda.v5.ledger.utxo.StateAndRef import net.corda.v5.ledger.utxo.UtxoLedgerService import net.corda.v5.ledger.utxo.query.VaultNamedParameterizedQuery -import com.r3.corda.demo.utxo.ownable.contract.TestOwnableContract -import com.r3.corda.demo.utxo.ownable.contract.TestOwnableState -import com.r3.corda.demo.utxo.ownable.workflow.firstLedgerKey +import com.r3.corda.test.utxo.ownable.contract.TestOwnableContract +import com.r3.corda.test.utxo.ownable.contract.TestOwnableState import java.security.PublicKey import java.time.Instant import java.time.temporal.ChronoUnit diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/ownable/workflow/query/WellKnownOwnableStateQueryFlow.kt b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/WellKnownOwnableStateQueryFlow.kt similarity index 95% rename from e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/ownable/workflow/query/WellKnownOwnableStateQueryFlow.kt rename to e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/WellKnownOwnableStateQueryFlow.kt index 47ec3e95..3ffd724e 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/ownable/workflow/query/WellKnownOwnableStateQueryFlow.kt +++ b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-app/src/main/kotlin/com/r3/corda/test/utxo/ownable/workflow/WellKnownOwnableStateQueryFlow.kt @@ -1,4 +1,4 @@ -package com.r3.corda.demo.utxo.ownable.workflow.query +package com.r3.corda.test.utxo.ownable.workflow import com.r3.corda.ledger.utxo.ownable.query.WellKnownOwnableStateQueries import net.corda.v5.application.flows.ClientRequestBody @@ -11,9 +11,8 @@ import net.corda.v5.ledger.common.NotaryLookup import net.corda.v5.ledger.utxo.StateAndRef import net.corda.v5.ledger.utxo.UtxoLedgerService import net.corda.v5.ledger.utxo.query.VaultNamedParameterizedQuery -import com.r3.corda.demo.utxo.ownable.contract.TestOwnableContract -import com.r3.corda.demo.utxo.ownable.contract.TestOwnableState -import com.r3.corda.demo.utxo.ownable.workflow.firstLedgerKey +import com.r3.corda.test.utxo.ownable.contract.TestOwnableContract +import com.r3.corda.test.utxo.ownable.contract.TestOwnableState import java.time.Instant import java.time.temporal.ChronoUnit diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-contract/build.gradle b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-contract/build.gradle similarity index 89% rename from e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-contract/build.gradle rename to e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-contract/build.gradle index 9c3def4d..02619035 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-contract/build.gradle +++ b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-contract/build.gradle @@ -7,7 +7,7 @@ cordapp { targetPlatformVersion platformVersion as Integer minimumPlatformVersion platformVersion as Integer contract { - name "Advanced UTXO Ledger Demo Ownable Contract" + name "Advanced UTXO Ledger Test Ownable Contract" versionId 1 vendor "R3" } diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/ownable/contract/MyContractState.kt b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/ownable/contract/MyContractState.kt new file mode 100644 index 00000000..32d2d100 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/ownable/contract/MyContractState.kt @@ -0,0 +1,16 @@ +package com.r3.corda.test.utxo.ownable.contract + +import net.corda.v5.ledger.utxo.BelongsToContract +import net.corda.v5.ledger.utxo.ContractState +import java.security.PublicKey +import java.util.UUID + +@BelongsToContract(TestOwnableContract::class) +data class MyContractState( + val id: UUID, +) : ContractState { + + override fun getParticipants(): List { + return emptyList() + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/ownable/contract/MyOwnableState.kt b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/ownable/contract/MyOwnableState.kt new file mode 100644 index 00000000..f7ea51d4 --- /dev/null +++ b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/ownable/contract/MyOwnableState.kt @@ -0,0 +1,44 @@ +package com.r3.corda.test.utxo.ownable.contract + +import com.r3.corda.ledger.utxo.ownable.OwnableConstraints +import com.r3.corda.ledger.utxo.ownable.OwnableState +import com.r3.corda.ledger.utxo.ownable.WellKnownOwnableState +import net.corda.v5.base.types.MemberX500Name +import net.corda.v5.ledger.utxo.BelongsToContract +import net.corda.v5.ledger.utxo.Command +import net.corda.v5.ledger.utxo.Contract +import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction +import java.security.PublicKey + +@BelongsToContract(MyOwnableContract::class) +data class MyOwnableState( + private val owner: PublicKey, + private val ownerName: MemberX500Name, + private val participants: List +) : OwnableState, WellKnownOwnableState { + + override fun getParticipants(): List { + return participants + } + + override fun getOwner(): PublicKey { + return owner + } + + override fun getOwnerName(): MemberX500Name { + return ownerName + } +} + +class MyOwnableContract : Contract { + + class Create : Command + class Update : Command + + override fun verify(transaction: UtxoLedgerTransaction) { + val commands = transaction.commands + if (commands.any { it::class.java == Update::class.java }) { + OwnableConstraints.verifyUpdate(transaction) + } + } +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/ownable/contract/TestOwnableState.kt b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/ownable/contract/TestOwnableState.kt similarity index 91% rename from e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/ownable/contract/TestOwnableState.kt rename to e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/ownable/contract/TestOwnableState.kt index a53800e4..c75d8901 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/ownable/contract/TestOwnableState.kt +++ b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/ownable/contract/TestOwnableState.kt @@ -1,4 +1,4 @@ -package com.r3.corda.demo.utxo.ownable.contract +package com.r3.corda.test.utxo.ownable.contract import com.r3.corda.ledger.utxo.ownable.OwnableState import com.r3.corda.ledger.utxo.ownable.WellKnownOwnableState @@ -25,14 +25,13 @@ data class TestOwnableState( } override fun getOwnerName(): MemberX500Name { - return ownerName + return ownerName } } class TestOwnableContract : Contract { class Create : Command - class Update : Command class Delete : Command override fun verify(transaction: UtxoLedgerTransaction) { diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/ownable/contract/query/json/TestOwnableStateVaultJsonFactory.kt b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/ownable/contract/query/json/TestOwnableStateVaultJsonFactory.kt similarity index 79% rename from e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/ownable/contract/query/json/TestOwnableStateVaultJsonFactory.kt rename to e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/ownable/contract/query/json/TestOwnableStateVaultJsonFactory.kt index 30b00790..1055635c 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-ownable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/ownable/contract/query/json/TestOwnableStateVaultJsonFactory.kt +++ b/e2e-tests/cpbs/ledger-utxo-advanced-ownable-test-contract/src/main/kotlin/com/r3/corda/test/utxo/ownable/contract/query/json/TestOwnableStateVaultJsonFactory.kt @@ -1,8 +1,8 @@ -package com.r3.corda.demo.utxo.ownable.contract.query.json +package com.r3.corda.test.utxo.ownable.contract.query.json import net.corda.v5.application.marshalling.JsonMarshallingService import net.corda.v5.ledger.utxo.query.json.ContractStateVaultJsonFactory -import com.r3.corda.demo.utxo.ownable.contract.TestOwnableState +import com.r3.corda.test.utxo.ownable.contract.TestOwnableState class TestOwnableStateVaultJsonFactory : ContractStateVaultJsonFactory { diff --git a/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/ChainableTests.kt b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/ChainableTests.kt index bb5eba8f..e6f2de5a 100644 --- a/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/ChainableTests.kt +++ b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/ChainableTests.kt @@ -2,6 +2,7 @@ package com.r3.corda.ledger.utxo.e2etest import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.KotlinModule +import net.corda.e2etest.utilities.RPC_FLOW_STATUS_FAILED import net.corda.e2etest.utilities.RPC_FLOW_STATUS_SUCCESS import net.corda.e2etest.utilities.TEST_NOTARY_CPB_LOCATION import net.corda.e2etest.utilities.TEST_NOTARY_CPI_NAME @@ -23,8 +24,8 @@ import java.util.UUID class ChainableTests { private companion object { - const val TEST_CPI_NAME = "corda-ledger-extensions-ledger-utxo-advanced-chainable-demo-app" - const val TEST_CPB_LOCATION = "/META-INF/corda-ledger-extensions-ledger-utxo-advanced-chainable-demo-app.cpb" + const val TEST_CPI_NAME = "corda-ledger-extensions-ledger-utxo-advanced-chainable-test-app" + const val TEST_CPB_LOCATION = "/META-INF/corda-ledger-extensions-ledger-utxo-advanced-chainable-test-app.cpb" val objectMapper = ObjectMapper().apply { registerModule(KotlinModule.Builder().build()) @@ -76,66 +77,157 @@ class ChainableTests { } @Test - fun `Alice issues vehicle to Bob, bob transfers vehicle to Charlie`() { + fun `chainable contract create command valid`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "CREATE", + "rule" to "VALID" + ), + "com.r3.corda.test.utxo.chainable.workflow.ChainableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(response.flowError).isNull() + } - // Alice issues vehicle to Bob - val vehicleId = UUID.randomUUID() + @Test + fun `chainable contract create command CONTRACT_RULE_CREATE_OUTPUTS fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "CREATE", + "rule" to "CONTRACT_RULE_CREATE_OUTPUTS" + ), + "com.r3.corda.test.utxo.chainable.workflow.ChainableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message).contains("On chainable state(s) creating, at least one chainable state must be created.") + } - val issueVehicleFlowRequestId = startRpcFlow( + @Test + fun `chainable contract create command CONTRACT_RULE_CREATE_POINTERS fails`() { + val request = startRpcFlow( aliceHoldingId, mapOf( - "make" to "reliant", - "model" to "robin", - "id" to vehicleId, - "manufacturer" to aliceX500, - "owner" to bobX500, - "notary" to "O=MyNotaryService-$notaryHoldingId, L=London, C=GB", - "observers" to emptyList() + "command" to "CREATE", + "rule" to "CONTRACT_RULE_CREATE_POINTERS" ), - "com.r3.corda.demo.utxo.chainable.workflow.issue.IssueVehicleFlow\$Initiator" + "com.r3.corda.test.utxo.chainable.workflow.ChainableContractTestFlow" ) - val issueVehicleFlowResult = awaitRpcFlowFinished(aliceHoldingId, issueVehicleFlowRequestId) - assertThat(issueVehicleFlowResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) - assertThat(issueVehicleFlowResult.flowError).isNull() - - val issuedVehicleResponse = objectMapper - .readValue(issueVehicleFlowResult.flowResult, VehicleResponse::class.java) - - assertThat(issuedVehicleResponse.make).isEqualTo("reliant") - assertThat(issuedVehicleResponse.model).isEqualTo("robin") - assertThat(issuedVehicleResponse.id).isEqualTo(vehicleId) - assertThat(issuedVehicleResponse.manufacturer).isEqualTo(aliceX500) - assertThat(issuedVehicleResponse.owner).isEqualTo(bobX500) - - // Bob transfers vehicle to Charlie - val transferVehicleRequestId = startRpcFlow( - bobHoldingId, + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message).contains("On chainable state(s) creating, the previous state pointer of every created chainable state must be null.") + } + + @Test + fun `chainable contract update command valid`() { + val request = startRpcFlow( + aliceHoldingId, mapOf( - "id" to vehicleId, - "owner" to charlieX500, - "observers" to emptyList() + "command" to "UPDATE", + "rule" to "VALID" ), - "com.r3.corda.demo.utxo.chainable.workflow.transfer.TransferVehicleFlow\$Initiator" + "com.r3.corda.test.utxo.chainable.workflow.ChainableContractTestFlow" ) - val transferVehicleFlowResult = awaitRpcFlowFinished(bobHoldingId, transferVehicleRequestId) - assertThat(transferVehicleFlowResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) - assertThat(transferVehicleFlowResult.flowError).isNull() - - val transferVehicleResponse = objectMapper - .readValue(transferVehicleFlowResult.flowResult, VehicleResponse::class.java) - - assertThat(transferVehicleResponse.make).isEqualTo("reliant") - assertThat(transferVehicleResponse.model).isEqualTo("robin") - assertThat(transferVehicleResponse.id).isEqualTo(vehicleId) - assertThat(transferVehicleResponse.manufacturer).isEqualTo(aliceX500) - assertThat(transferVehicleResponse.owner).isEqualTo(charlieX500) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(response.flowError).isNull() } - data class VehicleResponse( - val make: String, - val model: String, - val id: UUID, - val manufacturer: String, - val owner: String - ) + @Test + fun `chainable contract update command CONTRACT_RULE_UPDATE_INPUTS fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "CONTRACT_RULE_UPDATE_INPUTS" + ), + "com.r3.corda.test.utxo.chainable.workflow.ChainableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message).contains("On chainable state(s) updating, at least one chainable state must be consumed.") + } + + @Test + fun `chainable contract update command CONTRACT_RULE_UPDATE_OUTPUTS fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "CONTRACT_RULE_UPDATE_OUTPUTS" + ), + "com.r3.corda.test.utxo.chainable.workflow.ChainableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message).contains("On chainable state(s) updating, at least one chainable state must be created.") + } + + @Test + fun `chainable contract update command CONTRACT_RULE_UPDATE_POINTERS fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "CONTRACT_RULE_UPDATE_POINTERS" + ), + "com.r3.corda.test.utxo.chainable.workflow.ChainableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains("On chainable state(s) updating, the previous state pointer of every created chainable state must not be null.") + } + + @Test + fun `chainable contract update command CONTRACT_RULE_UPDATE_EXCLUSIVE_POINTERS fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "CONTRACT_RULE_UPDATE_EXCLUSIVE_POINTERS" + ), + "com.r3.corda.test.utxo.chainable.workflow.ChainableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains( + "On chainable state(s) updating, the previous state pointer of every created chainable state must be pointing to exactly " + + "one consumed chainable state, exclusively." + ) + } + + @Test + fun `chainable contract delete command valid`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "DELETE", + "rule" to "VALID" + ), + "com.r3.corda.test.utxo.chainable.workflow.ChainableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(response.flowError).isNull() + } + + @Test + fun `chainable contract delete command CONTRACT_RULE_DELETE_INPUTS fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "DELETE", + "rule" to "CONTRACT_RULE_DELETE_INPUTS" + ), + "com.r3.corda.test.utxo.chainable.workflow.ChainableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message).contains("On chainable state(s) deleting, at least one chainable state must be consumed.") + } } diff --git a/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/FungibleTests.kt b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/FungibleTests.kt index f83e3c8c..e087bc5a 100644 --- a/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/FungibleTests.kt +++ b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/FungibleTests.kt @@ -16,8 +16,8 @@ import java.util.* class FungibleTests { private companion object { - const val TEST_CPI_NAME = "corda-ledger-extensions-ledger-utxo-advanced-fungible-demo-app" - const val TEST_CPB_LOCATION = "/META-INF/corda-ledger-extensions-ledger-utxo-advanced-fungible-demo-app.cpb" + const val TEST_CPI_NAME = "corda-ledger-extensions-ledger-utxo-advanced-fungible-test-app" + const val TEST_CPB_LOCATION = "/META-INF/corda-ledger-extensions-ledger-utxo-advanced-fungible-test-app.cpb" val objectMapper = ObjectMapper().apply { registerModule(KotlinModule.Builder().build()) @@ -73,86 +73,233 @@ class FungibleTests { } @Test - fun `Alice issues a token to Bob, Bob then transfers to Charlie, and then Bob burns some quantity of their token`() { - - // Alice issues tokens to Bob - val mintTokenFlowRequestId = startRpcFlow( + fun `fungible contract create command valid`() { + val request = startRpcFlow( aliceHoldingId, mapOf( - "issuer" to aliceX500, - "owner" to bobX500, - "quantity" to 123.45.toScaledBigDecimal(), - "notary" to "O=MyNotaryService-$notaryHoldingId, L=London, C=GB", - "observers" to emptyList() + "command" to "CREATE", + "rule" to "VALID" ), - "com.r3.corda.demo.utxo.fungible.workflow.mint.MintTokenFlow\$Initiator" + "com.r3.corda.test.utxo.fungible.workflow.FungibleContractTestFlow" ) - val mintTokenFlowResult = awaitRpcFlowFinished(aliceHoldingId, mintTokenFlowRequestId) - assertThat(mintTokenFlowResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) - assertThat(mintTokenFlowResult.flowError).isNull() + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(response.flowError).isNull() + } - val mintTokenResponse = objectMapper.readValue(mintTokenFlowResult.flowResult, MintTokenResponse::class.java) + @Test + fun `fungible contract create command CONTRACT_RULE_CREATE_OUTPUTS fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "CREATE", + "rule" to "CONTRACT_RULE_CREATE_OUTPUTS" + ), + "com.r3.corda.test.utxo.fungible.workflow.FungibleContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message).contains("On fungible state(s) creating, at least one fungible state must be created.") + } - assertThat(mintTokenResponse.balance.keys).hasSize(1) - assertThat(mintTokenResponse.balance.values).hasSize(1) + @Test + fun `fungible contract create command CONTRACT_RULE_CREATE_POSITIVE_QUANTITIES fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "CREATE", + "rule" to "CONTRACT_RULE_CREATE_POSITIVE_QUANTITIES" + ), + "com.r3.corda.test.utxo.fungible.workflow.FungibleContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message).contains( + "On fungible state(s) creating, the quantity of every created fungible state must be greater than zero." + ) + } - val owner = mintTokenResponse.balance.keys.single() - val quantity = mintTokenResponse.balance.values.single() + @Test + fun `fungible contract update command valid`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "VALID" + ), + "com.r3.corda.test.utxo.fungible.workflow.FungibleContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(response.flowError).isNull() + } - assertThat(owner).isEqualTo(bobX500) - assertThat(quantity).isEqualTo( 123.45.toScaledBigDecimal()) + @Test + fun `fungible contract update command CONTRACT_RULE_UPDATE_INPUTS fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "CONTRACT_RULE_UPDATE_INPUTS" + ), + "com.r3.corda.test.utxo.fungible.workflow.FungibleContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message).contains("On fungible state(s) updating, at least one fungible state must be consumed.") + } - // Bob transfers tokens to Charlie and receives change - val moveTokenFlowRequestId = startRpcFlow( - bobHoldingId, + @Test + fun `fungible contract update command CONTRACT_RULE_UPDATE_OUTPUTS fails`() { + val request = startRpcFlow( + aliceHoldingId, mapOf( - "issuer" to aliceX500, - "owner" to bobX500, - "shares" to mapOf( - charlieX500 to 100.41.toScaledBigDecimal() - ), - "observers" to emptyList() + "command" to "UPDATE", + "rule" to "CONTRACT_RULE_UPDATE_OUTPUTS" ), - "com.r3.corda.demo.utxo.fungible.workflow.move.MoveTokenFlow\$Initiator" + "com.r3.corda.test.utxo.fungible.workflow.FungibleContractTestFlow" ) - val moveTokenFlowResult = awaitRpcFlowFinished(bobHoldingId, moveTokenFlowRequestId) - assertThat(moveTokenFlowResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) - assertThat(moveTokenFlowResult.flowError).isNull() + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message).contains("On fungible state(s) updating, at least one fungible state must be created.") + } - val moveTokenResponse = objectMapper.readValue(moveTokenFlowResult.flowResult, MoveTokenResponse::class.java) + @Test + fun `fungible contract update command CONTRACT_RULE_UPDATE_POSITIVE_QUANTITIES fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "CONTRACT_RULE_UPDATE_POSITIVE_QUANTITIES" + ), + "com.r3.corda.test.utxo.fungible.workflow.FungibleContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains("On fungible state(s) updating, the quantity of every created fungible state must be greater than zero.") + } - assertThat(moveTokenResponse.balance.keys).hasSize(2) - assertThat(moveTokenResponse.balance.values).hasSize(2) + @Test + fun `fungible contract update command CONTRACT_RULE_UPDATE_SUM fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "CONTRACT_RULE_UPDATE_SUM" + ), + "com.r3.corda.test.utxo.fungible.workflow.FungibleContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains( + "On fungible state(s) updating, the sum of the unscaled values of the consumed states must be equal to the sum of the " + + "unscaled values of the created states." + ) + } - assertThat(moveTokenResponse.balance.keys).containsExactlyInAnyOrder(bobX500, charlieX500) - assertThat(moveTokenResponse.balance.values).containsExactlyInAnyOrder( - 100.41.toScaledBigDecimal(), - 23.04.toScaledBigDecimal() + @Test + fun `fungible contract update command CONTRACT_RULE_UPDATE_GROUP_SUM fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "CONTRACT_RULE_UPDATE_GROUP_SUM" + ), + "com.r3.corda.test.utxo.fungible.workflow.FungibleContractTestFlow" ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains( + "On fungible state(s) updating, the sum of the consumed states that are fungible with each other must be equal to the " + + "sum of the created states that are fungible with each other." + ) + } - // Bob redeems tokens - val burnTokenFlowRequestId = startRpcFlow( - bobHoldingId, + @Test + fun `fungible contract delete command valid`() { + val request = startRpcFlow( + aliceHoldingId, mapOf( - "issuer" to aliceX500, - "owner" to bobX500, - "quantity" to 20.01.toScaledBigDecimal(), - "observers" to emptyList() + "command" to "DELETE", + "rule" to "VALID" ), - "com.r3.corda.demo.utxo.fungible.workflow.burn.BurnTokenFlow\$Initiator" + "com.r3.corda.test.utxo.fungible.workflow.FungibleContractTestFlow" ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(response.flowError).isNull() + } - val burnTokenFlowResult = awaitRpcFlowFinished(bobHoldingId, burnTokenFlowRequestId) - assertThat(burnTokenFlowResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) - assertThat(burnTokenFlowResult.flowError).isNull() + @Test + fun `fungible contract delete command CONTRACT_RULE_DELETE_INPUTS fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "DELETE", + "rule" to "CONTRACT_RULE_DELETE_INPUTS" + ), + "com.r3.corda.test.utxo.fungible.workflow.FungibleContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message).contains("On fungible state(s) deleting, at least one fungible state input must be consumed.") + } - val burnTokenResponse = objectMapper.readValue(burnTokenFlowResult.flowResult, BurnTokenResponse::class.java) + @Test + fun `fungible contract delete command CONTRACT_RULE_DELETE_POSITIVE_QUANTITIES fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "DELETE", + "rule" to "CONTRACT_RULE_DELETE_POSITIVE_QUANTITIES" + ), + "com.r3.corda.test.utxo.fungible.workflow.FungibleContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message).contains( + "On fungible state(s) deleting, the quantity of every created fungible state must be greater than zero." + ) + } - assertThat(burnTokenResponse.burned).isEqualTo(20.01.toScaledBigDecimal()) - assertThat(burnTokenResponse.change).isEqualTo(3.03.toScaledBigDecimal()) + @Test + fun `fungible contract delete command CONTRACT_RULE_DELETE_SUM fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "DELETE", + "rule" to "CONTRACT_RULE_DELETE_SUM" + ), + "com.r3.corda.test.utxo.fungible.workflow.FungibleContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains( + "On fungible state(s) deleting, the sum of the unscaled values of the consumed states must be greater than the sum of " + + "the unscaled values of the created states." + ) } - data class MintTokenResponse(val balance: Map) - data class MoveTokenResponse(val balance: Map) - data class BurnTokenResponse(val quantities: Collection, val burned: BigDecimal, val change: BigDecimal) + @Test + fun `fungible contract delete command CONTRACT_RULE_DELETE_GROUP_SUM fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "DELETE", + "rule" to "CONTRACT_RULE_DELETE_GROUP_SUM" + ), + "com.r3.corda.test.utxo.fungible.workflow.FungibleContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains( + "On fungible state(s) deleting, the sum of consumed states that are fungible with each other must be greater than the " + + "sum of the created states that are fungible with each other." + ) + } } diff --git a/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/IdentifiableTests.kt b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/IdentifiableTests.kt index 31d551b9..df6e2ae8 100644 --- a/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/IdentifiableTests.kt +++ b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/IdentifiableTests.kt @@ -2,6 +2,7 @@ package com.r3.corda.ledger.utxo.e2etest import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.KotlinModule +import net.corda.e2etest.utilities.RPC_FLOW_STATUS_FAILED import net.corda.e2etest.utilities.RPC_FLOW_STATUS_SUCCESS import net.corda.e2etest.utilities.TEST_NOTARY_CPB_LOCATION import net.corda.e2etest.utilities.TEST_NOTARY_CPI_NAME @@ -13,6 +14,7 @@ import net.corda.e2etest.utilities.registerStaticMember import net.corda.e2etest.utilities.startRpcFlow import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS @@ -23,8 +25,8 @@ import java.util.UUID class IdentifiableTests { private companion object { - const val TEST_CPI_NAME = "corda-ledger-extensions-ledger-utxo-advanced-identifiable-demo-app" - const val TEST_CPB_LOCATION = "/META-INF/corda-ledger-extensions-ledger-utxo-advanced-identifiable-demo-app.cpb" + const val TEST_CPI_NAME = "corda-ledger-extensions-ledger-utxo-advanced-identifiable-test-app" + const val TEST_CPB_LOCATION = "/META-INF/corda-ledger-extensions-ledger-utxo-advanced-identifiable-test-app.cpb" val objectMapper = ObjectMapper().apply { registerModule(KotlinModule.Builder().build()) @@ -69,96 +71,13 @@ class IdentifiableTests { registerStaticMember(notaryHoldingId, true) } - @Test - fun `Alice issues a support ticket to Bob, Bob opens and completes the ticket, Alice closes the ticket`() { - - val issueSupportTicketRequestId = startRpcFlow( - aliceHoldingId, - mapOf( - "title" to "Build Corda 5", - "description" to "Build super-duper DLT and call it Corda 5", - "reporter" to aliceX500, - "assignee" to bobX500, - "notary" to "O=MyNotaryService-$notaryHoldingId, L=London, C=GB", - "observers" to emptyList() - ), - "com.r3.corda.demo.utxo.identifiable.workflow.create.CreateSupportTicketFlow\$Initiator" - ) - val createFlowResponse = awaitRpcFlowFinished(aliceHoldingId, issueSupportTicketRequestId) - assertThat(createFlowResponse.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) - assertThat(createFlowResponse.flowError).isNull() - - val createSupportTicketResponse = objectMapper - .readValue(createFlowResponse.flowResult, CreateSupportTicketResponse::class.java) - - val openSupportTicketRequestId = startRpcFlow( - bobHoldingId, - mapOf( - "id" to createSupportTicketResponse.id, - "reporter" to aliceX500, - "status" to "OPEN", - "observers" to emptyList() - ), - "com.r3.corda.demo.utxo.identifiable.workflow.update.UpdateSupportTicketFlow\$Initiator" - ) - val openFlowResult = awaitRpcFlowFinished(bobHoldingId, openSupportTicketRequestId) - assertThat(openFlowResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) - assertThat(openFlowResult.flowError).isNull() - - val openSupportTicketResponse = objectMapper - .readValue(openFlowResult.flowResult, UpdateSupportTicketResponse::class.java) - - assertThat(openSupportTicketResponse.id).isEqualTo(createSupportTicketResponse.id) - assertThat(openSupportTicketResponse.title).isEqualTo(createSupportTicketResponse.title) - - val doneSupportTicketRequestId = startRpcFlow( - bobHoldingId, - mapOf( - "id" to openSupportTicketResponse.id, - "reporter" to aliceX500, - "status" to "DONE", - "observers" to emptyList() - ), - "com.r3.corda.demo.utxo.identifiable.workflow.update.UpdateSupportTicketFlow\$Initiator" - ) - val doneFlowResult = awaitRpcFlowFinished(bobHoldingId, doneSupportTicketRequestId) - assertThat(doneFlowResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) - assertThat(doneFlowResult.flowError).isNull() - - val doneSupportTicketResponse = objectMapper - .readValue(doneFlowResult.flowResult, UpdateSupportTicketResponse::class.java) - - assertThat(doneSupportTicketResponse.id).isEqualTo(openSupportTicketResponse.id) - assertThat(doneSupportTicketResponse.title).isEqualTo(openSupportTicketResponse.title) - - val deleteSupportTicketRequestId = startRpcFlow( - aliceHoldingId, - mapOf( - "id" to doneSupportTicketResponse.id, - "assignee" to bobX500, - "observers" to emptyList() - ), - "com.r3.corda.demo.utxo.identifiable.workflow.delete.DeleteSupportTicketFlow\$Initiator" - ) - - val deleteSupportTicketResult = awaitRpcFlowFinished(aliceHoldingId, deleteSupportTicketRequestId) - assertThat(deleteSupportTicketResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) - assertThat(deleteSupportTicketResult.flowError).isNull() - - val deleteSupportTicketResponse = objectMapper - .readValue(doneFlowResult.flowResult, DeleteSupportTicketResponse::class.java) - - assertThat(deleteSupportTicketResponse.id).isEqualTo(doneSupportTicketResponse.id) - assertThat(deleteSupportTicketResponse.title).isEqualTo(doneSupportTicketResponse.title) - } - @Test fun `query identifiable states`() { val request = startRpcFlow( aliceHoldingId, mapOf(), - "com.r3.corda.demo.utxo.identifiable.workflow.query.IdentifiableStateQueryFlow" + "com.r3.corda.test.utxo.identifiable.workflow.IdentifiableStateQueryFlow" ) val createFlowResponse = awaitRpcFlowFinished(aliceHoldingId, request) assertThat(createFlowResponse.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) @@ -185,7 +104,7 @@ class IdentifiableTests { val request = startRpcFlow( aliceHoldingId, mapOf(), - "com.r3.corda.demo.utxo.identifiable.workflow.query.IdentifiablePointerFlow" + "com.r3.corda.test.utxo.identifiable.workflow.IdentifiablePointerFlow" ) val createFlowResponse = awaitRpcFlowFinished(aliceHoldingId, request) assertThat(createFlowResponse.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) @@ -206,9 +125,155 @@ class IdentifiableTests { assertThat(response.consumed).isEmpty() } - data class CreateSupportTicketResponse(val id: String, val title: String) - data class UpdateSupportTicketResponse(val id: String, val title: String) - data class DeleteSupportTicketResponse(val id: String, val title: String) + @Test + fun `Identifiable contract create command valid`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "CREATE", + "rule" to "VALID" + ), + "com.r3.corda.test.utxo.identifiable.workflow.IdentifiableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(response.flowError).isNull() + } + + @Test + fun `Identifiable contract create command CONTRACT_RULE_CREATE_OUTPUTS fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "CREATE", + "rule" to "CONTRACT_RULE_CREATE_OUTPUTS" + ), + "com.r3.corda.test.utxo.identifiable.workflow.IdentifiableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains("On identifiable state(s) creating, at least one identifiable state must be created.") + } + + @Test + fun `Identifiable contract update command valid`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "VALID" + ), + "com.r3.corda.test.utxo.identifiable.workflow.IdentifiableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(response.flowError).isNull() + } + + @Test + fun `Identifiable contract update command CONTRACT_RULE_UPDATE_INPUTS fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "CONTRACT_RULE_UPDATE_INPUTS" + ), + "com.r3.corda.test.utxo.identifiable.workflow.IdentifiableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains("On identifiable state(s) updating, at least one identifiable state must be consumed.") + } + + @Test + fun `Identifiable contract update command CONTRACT_RULE_UPDATE_OUTPUTS fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "CONTRACT_RULE_UPDATE_OUTPUTS" + ), + "com.r3.corda.test.utxo.identifiable.workflow.IdentifiableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains("On identifiable state(s) updating, at least one identifiable state must be created.") + } + + @Test + fun `Identifiable contract update command CONTRACT_RULE_UPDATE_IDENTIFIER_EXCLUSIVITY fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "CONTRACT_RULE_UPDATE_IDENTIFIER_EXCLUSIVITY" + ), + "com.r3.corda.test.utxo.identifiable.workflow.IdentifiableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains( + "On identifiable state(s) updating, each created identifiable state's identifier must match one consumed identifiable " + + "state's state ref or identifier, exclusively." + ) + } + + // TODO Currently broken fixed in CORE-13473 + @Test + @Disabled + fun `Identifiable contract update command CONTRACT_RULE_UPDATE_IDENTIFIER_EXCLUSIVITY MISSING_ID fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "CONTRACT_RULE_UPDATE_IDENTIFIER_EXCLUSIVITY MISSING_ID" + ), + "com.r3.corda.test.utxo.identifiable.workflow.IdentifiableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains( + "On identifiable state(s) updating, each created identifiable state's identifier must match one consumed identifiable " + + "state's state ref or identifier, exclusively." + ) + } + + @Test + fun `Identifiable contract delete command valid`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "DELETE", + "rule" to "VALID" + ), + "com.r3.corda.test.utxo.identifiable.workflow.IdentifiableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(response.flowError).isNull() + } + + @Test + fun `Identifiable contract delete command CONTRACT_RULE_DELETE_INPUTS fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "DELETE", + "rule" to "CONTRACT_RULE_DELETE_INPUTS" + ), + "com.r3.corda.test.utxo.identifiable.workflow.IdentifiableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains("On identifiable state(s) deleting, at least one identifiable state must be consumed.") + } + data class IdentifiableStateQueryResponse( val before: List, val after: List, diff --git a/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/IssuableTests.kt b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/IssuableTests.kt index 72e40064..cba5bd7b 100644 --- a/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/IssuableTests.kt +++ b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/IssuableTests.kt @@ -2,6 +2,7 @@ package com.r3.corda.ledger.utxo.e2etest import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.KotlinModule +import net.corda.e2etest.utilities.RPC_FLOW_STATUS_FAILED import net.corda.e2etest.utilities.RPC_FLOW_STATUS_SUCCESS import net.corda.e2etest.utilities.TEST_NOTARY_CPB_LOCATION import net.corda.e2etest.utilities.TEST_NOTARY_CPI_NAME @@ -23,8 +24,8 @@ import java.util.UUID class IssuableTests { private companion object { - const val TEST_CPI_NAME = "corda-ledger-extensions-ledger-utxo-advanced-issuable-demo-app" - const val TEST_CPB_LOCATION = "/META-INF/corda-ledger-extensions-ledger-utxo-advanced-issuable-demo-app.cpb" + const val TEST_CPI_NAME = "corda-ledger-extensions-ledger-utxo-advanced-issuable-test-app" + const val TEST_CPB_LOCATION = "/META-INF/corda-ledger-extensions-ledger-utxo-advanced-issuable-test-app.cpb" val objectMapper = ObjectMapper().apply { registerModule(KotlinModule.Builder().build()) @@ -75,7 +76,7 @@ class IssuableTests { val request = startRpcFlow( aliceHoldingId, mapOf(), - "com.r3.corda.demo.utxo.issuable.workflow.query.IssuableStateQueryFlow" + "com.r3.corda.test.utxo.issuable.workflow.IssuableStateQueryFlow" ) val createFlowResponse = awaitRpcFlowFinished(aliceHoldingId, request) assertThat(createFlowResponse.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) @@ -95,7 +96,7 @@ class IssuableTests { val request = startRpcFlow( aliceHoldingId, mapOf(), - "com.r3.corda.demo.utxo.issuable.workflow.query.WellKnownIssuableStateQueryFlow" + "com.r3.corda.test.utxo.issuable.workflow.WellKnownIssuableStateQueryFlow" ) val createFlowResponse = awaitRpcFlowFinished(aliceHoldingId, request) assertThat(createFlowResponse.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) @@ -109,6 +110,72 @@ class IssuableTests { assertThat(response.consumed).isEmpty() } + @Test + fun `Issuable contract create command valid`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "CREATE", + "rule" to "VALID", + "issuer" to bobX500 + ), + "com.r3.corda.test.utxo.issuable.workflow.IssuableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(response.flowError).isNull() + } + + @Test + fun `Issuable contract create command CONTRACT_RULE_CREATE_SIGNATORIES fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "CREATE", + "rule" to "CONTRACT_RULE_CREATE_SIGNATORIES", + "issuer" to bobX500 + ), + "com.r3.corda.test.utxo.issuable.workflow.IssuableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains("On issuable state(s) creating, the issuer of every created issuable state must sign the transaction.") + } + + @Test + fun `Issuable contract delete command valid`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "DELETE", + "rule" to "VALID", + "issuer" to bobX500 + ), + "com.r3.corda.test.utxo.issuable.workflow.IssuableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(response.flowError).isNull() + } + + @Test + fun `Issuable contract delete command CONTRACT_RULE_DELETE_SIGNATORIES fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "DELETE", + "rule" to "CONTRACT_RULE_DELETE_SIGNATORIES", + "issuer" to bobX500 + ), + "com.r3.corda.test.utxo.issuable.workflow.IssuableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains("On issuable state(s) deleting, the issuer of every consumed issuable state must sign the transaction.") + } + data class IssuableStateQueryResponse( val before: List, val after: List, diff --git a/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/OwnableTests.kt b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/OwnableTests.kt index 8295baea..36b70f22 100644 --- a/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/OwnableTests.kt +++ b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/OwnableTests.kt @@ -2,6 +2,7 @@ package com.r3.corda.ledger.utxo.e2etest import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.KotlinModule +import net.corda.e2etest.utilities.RPC_FLOW_STATUS_FAILED import net.corda.e2etest.utilities.RPC_FLOW_STATUS_SUCCESS import net.corda.e2etest.utilities.TEST_NOTARY_CPB_LOCATION import net.corda.e2etest.utilities.TEST_NOTARY_CPI_NAME @@ -23,8 +24,8 @@ import java.util.UUID class OwnableTests { private companion object { - const val TEST_CPI_NAME = "corda-ledger-extensions-ledger-utxo-advanced-ownable-demo-app" - const val TEST_CPB_LOCATION = "/META-INF/corda-ledger-extensions-ledger-utxo-advanced-ownable-demo-app.cpb" + const val TEST_CPI_NAME = "corda-ledger-extensions-ledger-utxo-advanced-ownable-test-app" + const val TEST_CPB_LOCATION = "/META-INF/corda-ledger-extensions-ledger-utxo-advanced-ownable-test-app.cpb" val objectMapper = ObjectMapper().apply { registerModule(KotlinModule.Builder().build()) @@ -75,7 +76,7 @@ class OwnableTests { val request = startRpcFlow( aliceHoldingId, mapOf(), - "com.r3.corda.demo.utxo.ownable.workflow.query.OwnableStateQueryFlow" + "com.r3.corda.test.utxo.ownable.workflow.OwnableStateQueryFlow" ) val createFlowResponse = awaitRpcFlowFinished(aliceHoldingId, request) assertThat(createFlowResponse.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) @@ -95,7 +96,7 @@ class OwnableTests { val request = startRpcFlow( aliceHoldingId, mapOf(), - "com.r3.corda.demo.utxo.ownable.workflow.query.WellKnownOwnableStateQueryFlow" + "com.r3.corda.test.utxo.ownable.workflow.WellKnownOwnableStateQueryFlow" ) val createFlowResponse = awaitRpcFlowFinished(aliceHoldingId, request) assertThat(createFlowResponse.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) @@ -109,6 +110,39 @@ class OwnableTests { assertThat(response.consumed).isEmpty() } + @Test + fun `Ownable contract update command valid`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "VALID", + "owner" to bobX500 + ), + "com.r3.corda.test.utxo.ownable.workflow.OwnableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(response.flowError).isNull() + } + + @Test + fun `Ownable contract update command CONTRACT_RULE_UPDATE_SIGNATORIES fails`() { + val request = startRpcFlow( + aliceHoldingId, + mapOf( + "command" to "UPDATE", + "rule" to "CONTRACT_RULE_UPDATE_SIGNATORIES", + "owner" to bobX500 + ), + "com.r3.corda.test.utxo.ownable.workflow.OwnableContractTestFlow" + ) + val response = awaitRpcFlowFinished(aliceHoldingId, request) + assertThat(response.flowStatus).isEqualTo(RPC_FLOW_STATUS_FAILED) + assertThat(response.flowError?.message) + .contains("On ownable state(s) updating, the owner of every consumed ownable state must sign the transaction.") + } + data class OwnableStateQueryResponse( val before: List, val after: List, diff --git a/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/demo/ChainableDemoTests.kt b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/demo/ChainableDemoTests.kt new file mode 100644 index 00000000..e6c16fa1 --- /dev/null +++ b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/demo/ChainableDemoTests.kt @@ -0,0 +1,142 @@ +package com.r3.corda.ledger.utxo.e2etest.demo + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.KotlinModule +import com.r3.corda.ledger.utxo.e2etest.uploadTrustedCertificate +import net.corda.e2etest.utilities.RPC_FLOW_STATUS_SUCCESS +import net.corda.e2etest.utilities.TEST_NOTARY_CPB_LOCATION +import net.corda.e2etest.utilities.TEST_NOTARY_CPI_NAME +import net.corda.e2etest.utilities.awaitRpcFlowFinished +import net.corda.e2etest.utilities.conditionallyUploadCordaPackage +import net.corda.e2etest.utilities.getHoldingIdShortHash +import net.corda.e2etest.utilities.getOrCreateVirtualNodeFor +import net.corda.e2etest.utilities.registerStaticMember +import net.corda.e2etest.utilities.startRpcFlow +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS +import java.util.UUID + +@Suppress("Unused", "FunctionName") +@TestInstance(PER_CLASS) +class ChainableDemoTests { + + private companion object { + const val TEST_CPI_NAME = "corda-ledger-extensions-ledger-utxo-advanced-chainable-demo-app" + const val TEST_CPB_LOCATION = "/META-INF/corda-ledger-extensions-ledger-utxo-advanced-chainable-demo-app.cpb" + + val objectMapper = ObjectMapper().apply { + registerModule(KotlinModule.Builder().build()) + } + } + + private val testRunUniqueId = UUID.randomUUID() + private val groupId = UUID.randomUUID().toString() + private val cpiName = "${TEST_CPI_NAME}_$testRunUniqueId" + private val notaryCpiName = "${TEST_NOTARY_CPI_NAME}_$testRunUniqueId" + + private val aliceX500 = "CN=Alice-${testRunUniqueId}, OU=Application, O=R3, L=London, C=GB" + private val bobX500 = "CN=Bob-${testRunUniqueId}, OU=Application, O=R3, L=London, C=GB" + private val charlieX500 = "CN=Charlie-${testRunUniqueId}, OU=Application, O=R3, L=London, C=GB" + private val notaryX500 = "CN=Notary-${testRunUniqueId}, OU=Application, O=R3, L=London, C=GB" + + private val aliceHoldingId: String = getHoldingIdShortHash(aliceX500, groupId) + private val bobHoldingId: String = getHoldingIdShortHash(bobX500, groupId) + private val charlieHoldingId: String = getHoldingIdShortHash(charlieX500, groupId) + private val notaryHoldingId: String = getHoldingIdShortHash(notaryX500, groupId) + + private val staticMemberList = listOf( + aliceX500, + bobX500, + charlieX500, + notaryX500 + ) + + @BeforeAll + fun beforeAll() { + uploadTrustedCertificate() + conditionallyUploadCordaPackage(cpiName, TEST_CPB_LOCATION, groupId, staticMemberList) + conditionallyUploadCordaPackage(notaryCpiName, TEST_NOTARY_CPB_LOCATION, groupId, staticMemberList) + + val aliceActualHoldingId = getOrCreateVirtualNodeFor(aliceX500, cpiName) + val bobActualHoldingId = getOrCreateVirtualNodeFor(bobX500, cpiName) + val charlieActualHoldingId = getOrCreateVirtualNodeFor(charlieX500, cpiName) + val notaryActualHoldingId = getOrCreateVirtualNodeFor(notaryX500, notaryCpiName) + + assertThat(aliceActualHoldingId).isEqualTo(aliceHoldingId) + assertThat(bobActualHoldingId).isEqualTo(bobHoldingId) + assertThat(charlieActualHoldingId).isEqualTo(charlieHoldingId) + assertThat(notaryActualHoldingId).isEqualTo(notaryHoldingId) + + registerStaticMember(aliceHoldingId) + registerStaticMember(bobHoldingId) + registerStaticMember(charlieHoldingId) + registerStaticMember(notaryHoldingId, true) + } + + @Test + fun `Alice issues vehicle to Bob, bob transfers vehicle to Charlie`() { + + // Alice issues vehicle to Bob + val vehicleId = UUID.randomUUID() + + val issueVehicleFlowRequestId = startRpcFlow( + aliceHoldingId, + mapOf( + "make" to "reliant", + "model" to "robin", + "id" to vehicleId, + "manufacturer" to aliceX500, + "owner" to bobX500, + "notary" to "O=MyNotaryService-$notaryHoldingId, L=London, C=GB", + "observers" to emptyList() + ), + "com.r3.corda.demo.utxo.chainable.workflow.issue.IssueVehicleFlow\$Initiator" + ) + val issueVehicleFlowResult = awaitRpcFlowFinished(aliceHoldingId, issueVehicleFlowRequestId) + assertThat(issueVehicleFlowResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(issueVehicleFlowResult.flowError).isNull() + + val issuedVehicleResponse = objectMapper + .readValue(issueVehicleFlowResult.flowResult, VehicleResponse::class.java) + + assertThat(issuedVehicleResponse.make).isEqualTo("reliant") + assertThat(issuedVehicleResponse.model).isEqualTo("robin") + assertThat(issuedVehicleResponse.id).isEqualTo(vehicleId) + assertThat(issuedVehicleResponse.manufacturer).isEqualTo(aliceX500) + assertThat(issuedVehicleResponse.owner).isEqualTo(bobX500) + + // Bob transfers vehicle to Charlie + val transferVehicleRequestId = startRpcFlow( + bobHoldingId, + mapOf( + "id" to vehicleId, + "owner" to charlieX500, + "observers" to emptyList() + ), + "com.r3.corda.demo.utxo.chainable.workflow.transfer.TransferVehicleFlow\$Initiator" + ) + val transferVehicleFlowResult = awaitRpcFlowFinished(bobHoldingId, transferVehicleRequestId) + assertThat(transferVehicleFlowResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(transferVehicleFlowResult.flowError).isNull() + + val transferVehicleResponse = objectMapper + .readValue(transferVehicleFlowResult.flowResult, VehicleResponse::class.java) + + assertThat(transferVehicleResponse.make).isEqualTo("reliant") + assertThat(transferVehicleResponse.model).isEqualTo("robin") + assertThat(transferVehicleResponse.id).isEqualTo(vehicleId) + assertThat(transferVehicleResponse.manufacturer).isEqualTo(aliceX500) + assertThat(transferVehicleResponse.owner).isEqualTo(charlieX500) + } + + data class VehicleResponse( + val make: String, + val model: String, + val id: UUID, + val manufacturer: String, + val owner: String + ) +} diff --git a/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/demo/FungibleDemoTests.kt b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/demo/FungibleDemoTests.kt new file mode 100644 index 00000000..f1fe56bf --- /dev/null +++ b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/demo/FungibleDemoTests.kt @@ -0,0 +1,159 @@ +package com.r3.corda.ledger.utxo.e2etest.demo + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.KotlinModule +import com.r3.corda.ledger.utxo.e2etest.uploadTrustedCertificate +import net.corda.e2etest.utilities.* +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS +import java.math.BigDecimal +import java.util.* + +@Suppress("Unused", "FunctionName") +@TestInstance(PER_CLASS) +class FungibleDemoTests { + + private companion object { + const val TEST_CPI_NAME = "corda-ledger-extensions-ledger-utxo-advanced-fungible-demo-app" + const val TEST_CPB_LOCATION = "/META-INF/corda-ledger-extensions-ledger-utxo-advanced-fungible-demo-app.cpb" + + val objectMapper = ObjectMapper().apply { + registerModule(KotlinModule.Builder().build()) + } + } + + private val testRunUniqueId = UUID.randomUUID() + private val groupId = UUID.randomUUID().toString() + private val cpiName = "${TEST_CPI_NAME}_$testRunUniqueId" + private val notaryCpiName = "${TEST_NOTARY_CPI_NAME}_$testRunUniqueId" + + private val aliceX500 = "CN=Alice-${testRunUniqueId}, OU=Application, O=R3, L=London, C=GB" + private val bobX500 = "CN=Bob-${testRunUniqueId}, OU=Application, O=R3, L=London, C=GB" + private val charlieX500 = "CN=Charlie-${testRunUniqueId}, OU=Application, O=R3, L=London, C=GB" + private val notaryX500 = "CN=Notary-${testRunUniqueId}, OU=Application, O=R3, L=London, C=GB" + + private val aliceHoldingId: String = getHoldingIdShortHash(aliceX500, groupId) + private val bobHoldingId: String = getHoldingIdShortHash(bobX500, groupId) + private val charlieHoldingId: String = getHoldingIdShortHash(charlieX500, groupId) + private val notaryHoldingId: String = getHoldingIdShortHash(notaryX500, groupId) + + private val staticMemberList = listOf( + aliceX500, + bobX500, + charlieX500, + notaryX500 + ) + + private fun Double.toScaledBigDecimal(scale: Int = 2): BigDecimal { + return this.toBigDecimal().setScale(scale) + } + + @BeforeAll + fun beforeAll() { + uploadTrustedCertificate() + conditionallyUploadCordaPackage(cpiName, TEST_CPB_LOCATION, groupId, staticMemberList) + conditionallyUploadCordaPackage(notaryCpiName, TEST_NOTARY_CPB_LOCATION, groupId, staticMemberList) + + val aliceActualHoldingId = getOrCreateVirtualNodeFor(aliceX500, cpiName) + val bobActualHoldingId = getOrCreateVirtualNodeFor(bobX500, cpiName) + val charlieActualHoldingId = getOrCreateVirtualNodeFor(charlieX500, cpiName) + val notaryActualHoldingId = getOrCreateVirtualNodeFor(notaryX500, notaryCpiName) + + assertThat(aliceActualHoldingId).isEqualTo(aliceHoldingId) + assertThat(bobActualHoldingId).isEqualTo(bobHoldingId) + assertThat(charlieActualHoldingId).isEqualTo(charlieHoldingId) + assertThat(notaryActualHoldingId).isEqualTo(notaryHoldingId) + + registerStaticMember(aliceHoldingId) + registerStaticMember(bobHoldingId) + registerStaticMember(charlieHoldingId) + registerStaticMember(notaryHoldingId, true) + } + + @Test + fun `Alice issues a token to Bob, Bob then transfers to Charlie, and then Bob burns some quantity of their token`() { + + // Alice issues tokens to Bob + val mintTokenFlowRequestId = startRpcFlow( + aliceHoldingId, + mapOf( + "issuer" to aliceX500, + "owner" to bobX500, + "quantity" to 123.45.toScaledBigDecimal(), + "notary" to "O=MyNotaryService-$notaryHoldingId, L=London, C=GB", + "observers" to emptyList() + ), + "com.r3.corda.demo.utxo.fungible.workflow.mint.MintTokenFlow\$Initiator" + ) + val mintTokenFlowResult = awaitRpcFlowFinished(aliceHoldingId, mintTokenFlowRequestId) + assertThat(mintTokenFlowResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(mintTokenFlowResult.flowError).isNull() + + val mintTokenResponse = objectMapper.readValue(mintTokenFlowResult.flowResult, MintTokenResponse::class.java) + + assertThat(mintTokenResponse.balance.keys).hasSize(1) + assertThat(mintTokenResponse.balance.values).hasSize(1) + + val owner = mintTokenResponse.balance.keys.single() + val quantity = mintTokenResponse.balance.values.single() + + assertThat(owner).isEqualTo(bobX500) + assertThat(quantity).isEqualTo(123.45.toScaledBigDecimal()) + + // Bob transfers tokens to Charlie and receives change + val moveTokenFlowRequestId = startRpcFlow( + bobHoldingId, + mapOf( + "issuer" to aliceX500, + "owner" to bobX500, + "shares" to mapOf( + charlieX500 to 100.41.toScaledBigDecimal() + ), + "observers" to emptyList() + ), + "com.r3.corda.demo.utxo.fungible.workflow.move.MoveTokenFlow\$Initiator" + ) + val moveTokenFlowResult = awaitRpcFlowFinished(bobHoldingId, moveTokenFlowRequestId) + assertThat(moveTokenFlowResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(moveTokenFlowResult.flowError).isNull() + + val moveTokenResponse = objectMapper.readValue(moveTokenFlowResult.flowResult, MoveTokenResponse::class.java) + + assertThat(moveTokenResponse.balance.keys).hasSize(2) + assertThat(moveTokenResponse.balance.values).hasSize(2) + + assertThat(moveTokenResponse.balance.keys).containsExactlyInAnyOrder(bobX500, charlieX500) + assertThat(moveTokenResponse.balance.values).containsExactlyInAnyOrder( + 100.41.toScaledBigDecimal(), + 23.04.toScaledBigDecimal() + ) + + // Bob redeems tokens + val burnTokenFlowRequestId = startRpcFlow( + bobHoldingId, + mapOf( + "issuer" to aliceX500, + "owner" to bobX500, + "quantity" to 20.01.toScaledBigDecimal(), + "observers" to emptyList() + ), + "com.r3.corda.demo.utxo.fungible.workflow.burn.BurnTokenFlow\$Initiator" + ) + + val burnTokenFlowResult = awaitRpcFlowFinished(bobHoldingId, burnTokenFlowRequestId) + assertThat(burnTokenFlowResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(burnTokenFlowResult.flowError).isNull() + + val burnTokenResponse = objectMapper.readValue(burnTokenFlowResult.flowResult, BurnTokenResponse::class.java) + + assertThat(burnTokenResponse.burned).isEqualTo(20.01.toScaledBigDecimal()) + assertThat(burnTokenResponse.change).isEqualTo(3.03.toScaledBigDecimal()) + } + + data class MintTokenResponse(val balance: Map) + data class MoveTokenResponse(val balance: Map) + data class BurnTokenResponse(val quantities: Collection, val burned: BigDecimal, val change: BigDecimal) +} diff --git a/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/demo/IdentifiableDemoTests.kt b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/demo/IdentifiableDemoTests.kt new file mode 100644 index 00000000..70b464ba --- /dev/null +++ b/e2e-tests/src/e2eTest/kotlin/com/r3/corda/ledger/utxo/e2etest/demo/IdentifiableDemoTests.kt @@ -0,0 +1,159 @@ +package com.r3.corda.ledger.utxo.e2etest.demo + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.KotlinModule +import com.r3.corda.ledger.utxo.e2etest.uploadTrustedCertificate +import net.corda.e2etest.utilities.RPC_FLOW_STATUS_SUCCESS +import net.corda.e2etest.utilities.TEST_NOTARY_CPB_LOCATION +import net.corda.e2etest.utilities.TEST_NOTARY_CPI_NAME +import net.corda.e2etest.utilities.awaitRpcFlowFinished +import net.corda.e2etest.utilities.conditionallyUploadCordaPackage +import net.corda.e2etest.utilities.getHoldingIdShortHash +import net.corda.e2etest.utilities.getOrCreateVirtualNodeFor +import net.corda.e2etest.utilities.registerStaticMember +import net.corda.e2etest.utilities.startRpcFlow +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS +import java.util.UUID + +@Suppress("Unused", "FunctionName") +@TestInstance(PER_CLASS) +class IdentifiableDemoTests { + + private companion object { + const val TEST_CPI_NAME = "corda-ledger-extensions-ledger-utxo-advanced-identifiable-demo-app" + const val TEST_CPB_LOCATION = "/META-INF/corda-ledger-extensions-ledger-utxo-advanced-identifiable-demo-app.cpb" + + val objectMapper = ObjectMapper().apply { + registerModule(KotlinModule.Builder().build()) + } + } + + private val testRunUniqueId = UUID.randomUUID() + private val groupId = UUID.randomUUID().toString() + private val cpiName = "${TEST_CPI_NAME}_$testRunUniqueId" + private val notaryCpiName = "${TEST_NOTARY_CPI_NAME}_$testRunUniqueId" + + private val aliceX500 = "CN=Alice-${testRunUniqueId}, OU=Application, O=R3, L=London, C=GB" + private val bobX500 = "CN=Bob-${testRunUniqueId}, OU=Application, O=R3, L=London, C=GB" + private val notaryX500 = "CN=Notary-${testRunUniqueId}, OU=Application, O=R3, L=London, C=GB" + + private val aliceHoldingId: String = getHoldingIdShortHash(aliceX500, groupId) + private val bobHoldingId: String = getHoldingIdShortHash(bobX500, groupId) + private val notaryHoldingId: String = getHoldingIdShortHash(notaryX500, groupId) + + private val staticMemberList = listOf( + aliceX500, + bobX500, + notaryX500 + ) + + @BeforeAll + fun beforeAll() { + uploadTrustedCertificate() + conditionallyUploadCordaPackage(cpiName, TEST_CPB_LOCATION, groupId, staticMemberList) + conditionallyUploadCordaPackage(notaryCpiName, TEST_NOTARY_CPB_LOCATION, groupId, staticMemberList) + + val aliceActualHoldingId = getOrCreateVirtualNodeFor(aliceX500, cpiName) + val bobActualHoldingId = getOrCreateVirtualNodeFor(bobX500, cpiName) + val notaryActualHoldingId = getOrCreateVirtualNodeFor(notaryX500, notaryCpiName) + + assertThat(aliceActualHoldingId).isEqualTo(aliceHoldingId) + assertThat(bobActualHoldingId).isEqualTo(bobHoldingId) + assertThat(notaryActualHoldingId).isEqualTo(notaryHoldingId) + + registerStaticMember(aliceHoldingId) + registerStaticMember(bobHoldingId) + registerStaticMember(notaryHoldingId, true) + } + + @Test + fun `Alice issues a support ticket to Bob, Bob opens and completes the ticket, Alice closes the ticket`() { + + val issueSupportTicketRequestId = startRpcFlow( + aliceHoldingId, + mapOf( + "title" to "Build Corda 5", + "description" to "Build super-duper DLT and call it Corda 5", + "reporter" to aliceX500, + "assignee" to bobX500, + "notary" to "O=MyNotaryService-$notaryHoldingId, L=London, C=GB", + "observers" to emptyList() + ), + "com.r3.corda.demo.utxo.identifiable.workflow.create.CreateSupportTicketFlow\$Initiator" + ) + val createFlowResponse = awaitRpcFlowFinished(aliceHoldingId, issueSupportTicketRequestId) + assertThat(createFlowResponse.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(createFlowResponse.flowError).isNull() + + val createSupportTicketResponse = objectMapper + .readValue(createFlowResponse.flowResult, CreateSupportTicketResponse::class.java) + + val openSupportTicketRequestId = startRpcFlow( + bobHoldingId, + mapOf( + "id" to createSupportTicketResponse.id, + "reporter" to aliceX500, + "status" to "OPEN", + "observers" to emptyList() + ), + "com.r3.corda.demo.utxo.identifiable.workflow.update.UpdateSupportTicketFlow\$Initiator" + ) + val openFlowResult = awaitRpcFlowFinished(bobHoldingId, openSupportTicketRequestId) + assertThat(openFlowResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(openFlowResult.flowError).isNull() + + val openSupportTicketResponse = objectMapper + .readValue(openFlowResult.flowResult, UpdateSupportTicketResponse::class.java) + + assertThat(openSupportTicketResponse.id).isEqualTo(createSupportTicketResponse.id) + assertThat(openSupportTicketResponse.title).isEqualTo(createSupportTicketResponse.title) + + val doneSupportTicketRequestId = startRpcFlow( + bobHoldingId, + mapOf( + "id" to openSupportTicketResponse.id, + "reporter" to aliceX500, + "status" to "DONE", + "observers" to emptyList() + ), + "com.r3.corda.demo.utxo.identifiable.workflow.update.UpdateSupportTicketFlow\$Initiator" + ) + val doneFlowResult = awaitRpcFlowFinished(bobHoldingId, doneSupportTicketRequestId) + assertThat(doneFlowResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(doneFlowResult.flowError).isNull() + + val doneSupportTicketResponse = objectMapper + .readValue(doneFlowResult.flowResult, UpdateSupportTicketResponse::class.java) + + assertThat(doneSupportTicketResponse.id).isEqualTo(openSupportTicketResponse.id) + assertThat(doneSupportTicketResponse.title).isEqualTo(openSupportTicketResponse.title) + + val deleteSupportTicketRequestId = startRpcFlow( + aliceHoldingId, + mapOf( + "id" to doneSupportTicketResponse.id, + "assignee" to bobX500, + "observers" to emptyList() + ), + "com.r3.corda.demo.utxo.identifiable.workflow.delete.DeleteSupportTicketFlow\$Initiator" + ) + + val deleteSupportTicketResult = awaitRpcFlowFinished(aliceHoldingId, deleteSupportTicketRequestId) + assertThat(deleteSupportTicketResult.flowStatus).isEqualTo(RPC_FLOW_STATUS_SUCCESS) + assertThat(deleteSupportTicketResult.flowError).isNull() + + val deleteSupportTicketResponse = objectMapper + .readValue(doneFlowResult.flowResult, DeleteSupportTicketResponse::class.java) + + assertThat(deleteSupportTicketResponse.id).isEqualTo(doneSupportTicketResponse.id) + assertThat(deleteSupportTicketResponse.title).isEqualTo(doneSupportTicketResponse.title) + } + + data class CreateSupportTicketResponse(val id: String, val title: String) + data class UpdateSupportTicketResponse(val id: String, val title: String) + data class DeleteSupportTicketResponse(val id: String, val title: String) +} diff --git a/examples/ledger-utxo-advanced-chainable-demo-app/build.gradle b/examples/ledger-utxo-advanced-chainable-demo-app/build.gradle new file mode 100644 index 00000000..61134994 --- /dev/null +++ b/examples/ledger-utxo-advanced-chainable-demo-app/build.gradle @@ -0,0 +1,25 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' + id 'net.corda.plugins.cordapp-cpb2' +} + +cordapp { + targetPlatformVersion platformVersion as Integer + minimumPlatformVersion platformVersion as Integer + workflow { + name "Advanced UTXO Ledger Demo Chainable Workflow" + versionId 1 + vendor "R3" + } +} + +dependencies { + cordaProvided platform("net.corda:corda-api:$cordaApiVersion") + cordaProvided 'org.jetbrains.kotlin:kotlin-osgi-bundle' + cordaProvided 'net.corda:corda-ledger-utxo' + + cordapp project(':examples:ledger-utxo-advanced-chainable-demo-contract') + + // Common and API packages pulled in as transitive dependencies through client + cordapp "com.r3.corda.notary.plugin.nonvalidating:notary-plugin-non-validating-client:$cordaNotaryPluginsVersion" +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/AppLogger.kt b/examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/AppLogger.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/AppLogger.kt rename to examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/AppLogger.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/MemberInfoExtensions.kt b/examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/MemberInfoExtensions.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/MemberInfoExtensions.kt rename to examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/MemberInfoExtensions.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/MemberLookupExtensions.kt b/examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/MemberLookupExtensions.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/MemberLookupExtensions.kt rename to examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/MemberLookupExtensions.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/NotaryLookupExtensions.kt b/examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/NotaryLookupExtensions.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/NotaryLookupExtensions.kt rename to examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/NotaryLookupExtensions.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/UtxoTransactionBuilderExtensions.kt b/examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/UtxoTransactionBuilderExtensions.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/UtxoTransactionBuilderExtensions.kt rename to examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/UtxoTransactionBuilderExtensions.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleFlow.kt b/examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleFlow.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleFlow.kt rename to examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleFlow.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleFlowResponder.kt b/examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleFlowResponder.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleFlowResponder.kt rename to examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleFlowResponder.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleRequest.kt b/examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleRequest.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleRequest.kt rename to examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleRequest.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleResponse.kt b/examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleResponse.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleResponse.kt rename to examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/issue/IssueVehicleResponse.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleFlow.kt b/examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleFlow.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleFlow.kt rename to examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleFlow.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleFlowResponder.kt b/examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleFlowResponder.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleFlowResponder.kt rename to examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleFlowResponder.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleRequest.kt b/examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleRequest.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleRequest.kt rename to examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleRequest.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleResponse.kt b/examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleResponse.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleResponse.kt rename to examples/ledger-utxo-advanced-chainable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/chainable/workflow/transfer/TransferVehicleResponse.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-contract/build.gradle b/examples/ledger-utxo-advanced-chainable-demo-contract/build.gradle similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-contract/build.gradle rename to examples/ledger-utxo-advanced-chainable-demo-contract/build.gradle diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/StateAndRefExtensions.kt b/examples/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/StateAndRefExtensions.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/StateAndRefExtensions.kt rename to examples/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/StateAndRefExtensions.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/Vehicle.kt b/examples/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/Vehicle.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/Vehicle.kt rename to examples/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/Vehicle.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/VehicleContract.kt b/examples/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/VehicleContract.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/VehicleContract.kt rename to examples/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/VehicleContract.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/query/json/VehicleVaultJsonFactory.kt b/examples/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/query/json/VehicleVaultJsonFactory.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/query/json/VehicleVaultJsonFactory.kt rename to examples/ledger-utxo-advanced-chainable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/chainable/contract/query/json/VehicleVaultJsonFactory.kt diff --git a/examples/ledger-utxo-advanced-fungible-demo-app/build.gradle b/examples/ledger-utxo-advanced-fungible-demo-app/build.gradle new file mode 100644 index 00000000..72c61b4d --- /dev/null +++ b/examples/ledger-utxo-advanced-fungible-demo-app/build.gradle @@ -0,0 +1,27 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' + id 'net.corda.plugins.cordapp-cpb2' +} + +cordapp { + targetPlatformVersion platformVersion.toInteger() + minimumPlatformVersion platformVersion.toInteger() + workflow { + name "Advanced UTXO Ledger Demo Fungible Workflow" + versionId 1 + vendor "R3" + } +} + +dependencies { + cordaProvided platform("net.corda:corda-api:$cordaApiVersion") + cordaProvided 'org.jetbrains.kotlin:kotlin-osgi-bundle' + cordaProvided 'net.corda:corda-ledger-utxo' + + cordapp project(":fungible") + + cordapp project(':examples:ledger-utxo-advanced-fungible-demo-contract') + + // Common and API packages pulled in as transitive dependencies through client + cordapp "com.r3.corda.notary.plugin.nonvalidating:notary-plugin-non-validating-client:$cordaNotaryPluginsVersion" +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/AppLogger.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/AppLogger.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/AppLogger.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/AppLogger.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/MemberInfoExtensions.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/MemberInfoExtensions.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/MemberInfoExtensions.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/MemberInfoExtensions.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/MemberLookupExtensions.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/MemberLookupExtensions.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/MemberLookupExtensions.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/MemberLookupExtensions.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/NotaryLookupExtensions.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/NotaryLookupExtensions.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/NotaryLookupExtensions.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/NotaryLookupExtensions.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/NumericExtensions.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/NumericExtensions.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/NumericExtensions.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/NumericExtensions.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/UtxoLedgerServiceExtensions.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/UtxoLedgerServiceExtensions.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/UtxoLedgerServiceExtensions.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/UtxoLedgerServiceExtensions.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/UtxoTransactionBuilderExtensions.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/UtxoTransactionBuilderExtensions.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/UtxoTransactionBuilderExtensions.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/UtxoTransactionBuilderExtensions.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenFlow.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenFlow.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenFlow.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenFlow.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenFlowResponder.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenFlowResponder.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenFlowResponder.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenFlowResponder.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenRequest.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenRequest.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenRequest.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenRequest.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenResponse.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenResponse.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenResponse.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenResponse.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenSelector.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenSelector.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenSelector.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/burn/BurnTokenSelector.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenFlow.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenFlow.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenFlow.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenFlow.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenFlowResponder.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenFlowResponder.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenFlowResponder.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenFlowResponder.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenRequest.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenRequest.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenRequest.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenRequest.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenResponse.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenResponse.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenResponse.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/mint/MintTokenResponse.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenFlow.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenFlow.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenFlow.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenFlow.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenFlowResponder.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenFlowResponder.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenFlowResponder.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenFlowResponder.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenRequest.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenRequest.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenRequest.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenRequest.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenResponse.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenResponse.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenResponse.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenResponse.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenSelector.kt b/examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenSelector.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenSelector.kt rename to examples/ledger-utxo-advanced-fungible-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/fungible/workflow/move/MoveTokenSelector.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-contract/build.gradle b/examples/ledger-utxo-advanced-fungible-demo-contract/build.gradle similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-contract/build.gradle rename to examples/ledger-utxo-advanced-fungible-demo-contract/build.gradle diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/fungible/contract/Token.kt b/examples/ledger-utxo-advanced-fungible-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/fungible/contract/Token.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/fungible/contract/Token.kt rename to examples/ledger-utxo-advanced-fungible-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/fungible/contract/Token.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/fungible/contract/TokenContract.kt b/examples/ledger-utxo-advanced-fungible-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/fungible/contract/TokenContract.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/fungible/contract/TokenContract.kt rename to examples/ledger-utxo-advanced-fungible-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/fungible/contract/TokenContract.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/fungible/contract/query/json/TokenVaultJsonFactory.kt b/examples/ledger-utxo-advanced-fungible-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/fungible/contract/query/json/TokenVaultJsonFactory.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-fungible-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/fungible/contract/query/json/TokenVaultJsonFactory.kt rename to examples/ledger-utxo-advanced-fungible-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/fungible/contract/query/json/TokenVaultJsonFactory.kt diff --git a/examples/ledger-utxo-advanced-identifiable-demo-app/build.gradle b/examples/ledger-utxo-advanced-identifiable-demo-app/build.gradle new file mode 100644 index 00000000..3d16b014 --- /dev/null +++ b/examples/ledger-utxo-advanced-identifiable-demo-app/build.gradle @@ -0,0 +1,25 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' + id 'net.corda.plugins.cordapp-cpb2' +} + +cordapp { + targetPlatformVersion platformVersion.toInteger() + minimumPlatformVersion platformVersion.toInteger() + workflow { + name "Advanced UTXO Ledger Demo Identifiable Workflow" + versionId 1 + vendor "R3" + } +} + +dependencies { + cordaProvided platform("net.corda:corda-api:$cordaApiVersion") + cordaProvided 'org.jetbrains.kotlin:kotlin-osgi-bundle' + cordaProvided 'net.corda:corda-ledger-utxo' + + cordapp project(':examples:ledger-utxo-advanced-identifiable-demo-contract') + + // Common and API packages pulled in as transitive dependencies through client + cordapp "com.r3.corda.notary.plugin.nonvalidating:notary-plugin-non-validating-client:$cordaNotaryPluginsVersion" +} diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/AppLogger.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/AppLogger.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/AppLogger.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/AppLogger.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/MemberInfoExtensions.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/MemberInfoExtensions.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/MemberInfoExtensions.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/MemberInfoExtensions.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/MemberLookupExtensions.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/MemberLookupExtensions.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/MemberLookupExtensions.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/MemberLookupExtensions.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/NotaryLookupExtensions.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/NotaryLookupExtensions.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/NotaryLookupExtensions.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/NotaryLookupExtensions.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/UtxoTransactionBuilderExtensions.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/UtxoTransactionBuilderExtensions.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/UtxoTransactionBuilderExtensions.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/UtxoTransactionBuilderExtensions.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketFlow.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketFlow.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketFlow.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketFlow.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketFlowResponder.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketFlowResponder.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketFlowResponder.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketFlowResponder.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketRequest.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketRequest.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketRequest.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketRequest.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketResponse.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketResponse.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketResponse.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/create/CreateSupportTicketResponse.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketFlow.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketFlow.kt similarity index 98% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketFlow.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketFlow.kt index 706c7316..c6e75df5 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketFlow.kt +++ b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketFlow.kt @@ -1,6 +1,5 @@ package com.r3.corda.demo.utxo.identifiable.workflow.delete -import net.corda.v5.application.crypto.DigestService import net.corda.v5.application.flows.ClientRequestBody import net.corda.v5.application.flows.ClientStartableFlow import net.corda.v5.application.flows.CordaInject diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketFlowResponder.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketFlowResponder.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketFlowResponder.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketFlowResponder.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketRequest.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketRequest.kt similarity index 93% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketRequest.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketRequest.kt index dfe67334..e02c48cd 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketRequest.kt +++ b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketRequest.kt @@ -1,13 +1,11 @@ package com.r3.corda.demo.utxo.identifiable.workflow.delete import com.r3.corda.ledger.utxo.identifiable.query.IdentifiableStateQueries -import net.corda.v5.application.crypto.DigestService import net.corda.v5.application.messaging.FlowMessaging import net.corda.v5.application.messaging.FlowSession import net.corda.v5.base.annotations.Suspendable import net.corda.v5.base.types.MemberX500Name import net.corda.v5.ledger.utxo.StateAndRef -import net.corda.v5.ledger.utxo.StateRef import net.corda.v5.ledger.utxo.UtxoLedgerService import com.r3.corda.demo.utxo.identifiable.contract.SupportTicket import java.time.Instant diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketResponse.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketResponse.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketResponse.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/delete/DeleteSupportTicketResponse.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketFlow.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketFlow.kt similarity index 98% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketFlow.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketFlow.kt index e8e53621..00ac264a 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketFlow.kt +++ b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketFlow.kt @@ -1,6 +1,5 @@ package com.r3.corda.demo.utxo.identifiable.workflow.update -import net.corda.v5.application.crypto.DigestService import net.corda.v5.application.flows.ClientRequestBody import net.corda.v5.application.flows.ClientStartableFlow import net.corda.v5.application.flows.CordaInject diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketFlowResponder.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketFlowResponder.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketFlowResponder.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketFlowResponder.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketRequest.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketRequest.kt similarity index 94% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketRequest.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketRequest.kt index d61327a7..56d00db0 100644 --- a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketRequest.kt +++ b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketRequest.kt @@ -1,13 +1,11 @@ package com.r3.corda.demo.utxo.identifiable.workflow.update import com.r3.corda.ledger.utxo.identifiable.query.IdentifiableStateQueries -import net.corda.v5.application.crypto.DigestService import net.corda.v5.application.messaging.FlowMessaging import net.corda.v5.application.messaging.FlowSession import net.corda.v5.base.annotations.Suspendable import net.corda.v5.base.types.MemberX500Name import net.corda.v5.ledger.utxo.StateAndRef -import net.corda.v5.ledger.utxo.StateRef import net.corda.v5.ledger.utxo.UtxoLedgerService import com.r3.corda.demo.utxo.identifiable.contract.SupportTicket import com.r3.corda.demo.utxo.identifiable.contract.SupportTicketStatus diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketResponse.kt b/examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketResponse.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketResponse.kt rename to examples/ledger-utxo-advanced-identifiable-demo-app/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/workflow/update/UpdateSupportTicketResponse.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-contract/build.gradle b/examples/ledger-utxo-advanced-identifiable-demo-contract/build.gradle similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-contract/build.gradle rename to examples/ledger-utxo-advanced-identifiable-demo-contract/build.gradle diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/SupportTicket.kt b/examples/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/SupportTicket.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/SupportTicket.kt rename to examples/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/SupportTicket.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/SupportTicketContract.kt b/examples/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/SupportTicketContract.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/SupportTicketContract.kt rename to examples/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/SupportTicketContract.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/SupportTicketStatus.kt b/examples/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/SupportTicketStatus.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/SupportTicketStatus.kt rename to examples/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/SupportTicketStatus.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/TestIdentifiableState.kt b/examples/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/TestIdentifiableState.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/TestIdentifiableState.kt rename to examples/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/TestIdentifiableState.kt diff --git a/e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/query/json/SupportTicketVaultJsonFactory.kt b/examples/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/query/json/SupportTicketVaultJsonFactory.kt similarity index 100% rename from e2e-tests/cpbs/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/query/json/SupportTicketVaultJsonFactory.kt rename to examples/ledger-utxo-advanced-identifiable-demo-contract/src/main/kotlin/com/r3/corda/demo/utxo/identifiable/contract/query/json/SupportTicketVaultJsonFactory.kt diff --git a/fungible/src/main/java/com/r3/corda/ledger/utxo/fungible/FungibleConstraints.java b/fungible/src/main/java/com/r3/corda/ledger/utxo/fungible/FungibleConstraints.java index fe9caf2b..8aef9840 100644 --- a/fungible/src/main/java/com/r3/corda/ledger/utxo/fungible/FungibleConstraints.java +++ b/fungible/src/main/java/com/r3/corda/ledger/utxo/fungible/FungibleConstraints.java @@ -115,7 +115,11 @@ public static > void verifyUpdate(@NotNull final Utxo Check.isNotEmpty(inputs, CONTRACT_RULE_UPDATE_INPUTS); Check.isNotEmpty(outputs, CONTRACT_RULE_UPDATE_OUTPUTS); Check.all(outputs, it -> it.getQuantity().getUnscaledValue().compareTo(BigInteger.ZERO) > 0, CONTRACT_RULE_UPDATE_POSITIVE_QUANTITIES); - Check.isEqual(FungibleUtils.sum(inputs), FungibleUtils.sum(outputs), CONTRACT_RULE_UPDATE_SUM); + + BigInteger inputsSum = FungibleUtils.sum(inputs); + BigInteger outputsSum = FungibleUtils.sum(outputs); + + Check.isEqual(inputsSum, outputsSum, CONTRACT_RULE_UPDATE_SUM); for (final T input : inputs) { @@ -174,6 +178,7 @@ public static > void verifyDelete(@NotNull final Utxo Check.isNotEmpty(inputs, CONTRACT_RULE_DELETE_INPUTS); Check.all(outputs, it -> it.getQuantity().getUnscaledValue().compareTo(BigInteger.ZERO) > 0, CONTRACT_RULE_DELETE_POSITIVE_QUANTITIES); + Check.isGreaterThan(FungibleUtils.sum(inputs), FungibleUtils.sum(outputs), CONTRACT_RULE_DELETE_SUM); // We have to check all inputs and outputs, because we might create an extra output for which there is no input. diff --git a/gradle.properties b/gradle.properties index 8cb8760f..92375f26 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,9 +12,8 @@ cordaApiVersion=5.0.0.763-Iguana1.0 # E2e test utilities should come from runtime-os cordaRuntimeOsVersion=5.0.0.0-Iguana1.0 -# Specify the version of the notary plugins to use. -# Currently packaged as part of corda-runtime-os, so should be set to a corda-runtime-os version - # 1682496474857 published 26/4/23 -cordaNotaryPluginsVersion=5.0.0.0-beta+ +# Specify the version of the notary plugins to use, if left blank this will default to value of cordaRuntimeOsVersion +cordaNotaryPluginsVersion= # Specify the version of the cordapp-cpb2 and cordapp-cpk2 plugins cordaPluginsVersion=7.0.3 diff --git a/settings.gradle b/settings.gradle index 4502ff9c..11670ebb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -108,22 +108,27 @@ rootProject.name = 'corda-utxo-ledger-extensions' include 'base' include 'chainable' +include 'examples:ledger-utxo-advanced-chainable-demo-app' +include 'examples:ledger-utxo-advanced-chainable-demo-contract' +include 'examples:ledger-utxo-advanced-fungible-demo-app' +include 'examples:ledger-utxo-advanced-fungible-demo-contract' +include 'examples:ledger-utxo-advanced-identifiable-demo-app' +include 'examples:ledger-utxo-advanced-identifiable-demo-contract' include 'e2e-tests' -include 'e2e-tests:cpbs:ledger-utxo-advanced-chainable-demo-app' -include 'e2e-tests:cpbs:ledger-utxo-advanced-chainable-demo-contract' -include 'e2e-tests:cpbs:ledger-utxo-advanced-fungible-demo-app' -include 'e2e-tests:cpbs:ledger-utxo-advanced-fungible-demo-contract' -include 'e2e-tests:cpbs:ledger-utxo-advanced-identifiable-demo-app' -include 'e2e-tests:cpbs:ledger-utxo-advanced-identifiable-demo-contract' -include 'e2e-tests:cpbs:ledger-utxo-advanced-issuable-demo-app' -include 'e2e-tests:cpbs:ledger-utxo-advanced-issuable-demo-contract' -include 'e2e-tests:cpbs:ledger-utxo-advanced-ownable-demo-app' -include 'e2e-tests:cpbs:ledger-utxo-advanced-ownable-demo-contract' +include 'e2e-tests:cpbs:ledger-utxo-advanced-chainable-test-app' +include 'e2e-tests:cpbs:ledger-utxo-advanced-chainable-test-contract' +include 'e2e-tests:cpbs:ledger-utxo-advanced-fungible-test-app' +include 'e2e-tests:cpbs:ledger-utxo-advanced-fungible-test-contract' +include 'e2e-tests:cpbs:ledger-utxo-advanced-identifiable-test-app' +include 'e2e-tests:cpbs:ledger-utxo-advanced-identifiable-test-contract' +include 'e2e-tests:cpbs:ledger-utxo-advanced-issuable-test-app' +include 'e2e-tests:cpbs:ledger-utxo-advanced-issuable-test-contract' +include 'e2e-tests:cpbs:ledger-utxo-advanced-ownable-test-app' +include 'e2e-tests:cpbs:ledger-utxo-advanced-ownable-test-contract' include 'fungible' include 'identifiable' include 'issuable' include 'ownable' - include 'testing' gradleEnterprise {