Skip to content

Commit

Permalink
CORE-13661 Contract E2E tests (#39)
Browse files Browse the repository at this point in the history
Create E2E contract tests.

Move demo apps into an `examples` module.
  • Loading branch information
lankydan authored Jul 11, 2023
1 parent 2e383d0 commit 57292ef
Show file tree
Hide file tree
Showing 139 changed files with 2,964 additions and 281 deletions.
14 changes: 9 additions & 5 deletions e2e-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 "com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand All @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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<List<StateAndRef<*>>> {

@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<StateAndRef<*>> {
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
}
}
Original file line number Diff line number Diff line change
@@ -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<StateAndRef<*>>
) : SubFlow<List<StateAndRef<*>>> {

@CordaInject
private lateinit var memberLookup: MemberLookup

@CordaInject
private lateinit var notaryLookup: NotaryLookup

@CordaInject
private lateinit var utxoLedgerService: UtxoLedgerService

@Suspendable
override fun call(): List<StateAndRef<*>> {
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
}
}
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -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<StateAndRef<*>>
) : SubFlow<List<StateAndRef<*>>> {

@CordaInject
private lateinit var memberLookup: MemberLookup

@CordaInject
private lateinit var notaryLookup: NotaryLookup

@CordaInject
private lateinit var utxoLedgerService: UtxoLedgerService

@Suspendable
override fun call(): List<StateAndRef<*>> {
val key = memberLookup.myInfo().firstLedgerKey
val (inputs, outputs) = when (rule) {
"CONTRACT_RULE_UPDATE_INPUTS" -> {
emptyList<StateRef>() 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
}
}
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -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")
}
Original file line number Diff line number Diff line change
@@ -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<Class<out ChainableContractCommand<*>>> {
return listOf(Create::class.java, Update::class.java, Delete::class.java)
}

override fun onVerify(transaction: UtxoLedgerTransaction) {

}

class Create : ChainableContractCreateCommand<MyChainableState>() {

override fun getContractStateType(): Class<MyChainableState> {
return MyChainableState::class.java
}

override fun onVerify(transaction: UtxoLedgerTransaction) {

}
}

class Update : ChainableContractUpdateCommand<MyChainableState>() {

override fun getContractStateType(): Class<MyChainableState> {
return MyChainableState::class.java
}

override fun onVerify(transaction: UtxoLedgerTransaction) {

}
}

class Delete : ChainableContractDeleteCommand<MyChainableState>() {

override fun getContractStateType(): Class<MyChainableState> {
return MyChainableState::class.java
}

override fun onVerify(transaction: UtxoLedgerTransaction) {

}
}
}
Loading

0 comments on commit 57292ef

Please sign in to comment.