Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DO NOT MERGE #90

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ cordapp {
// The cordapp section contains either a workflow or contract subsection depending on the type of component.
// Declares the type and metadata of the CPK (this CPB has one CPK).
contract {
name "ContractsModuleNameHere"
name "Apples utxo example contract"
versionId 1
vendor "VendorNameHere"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.r3.developers.apples.contracts

import net.corda.v5.ledger.utxo.Command

interface AppleCommands : Command {
class Issue : AppleCommands
class Redeem : AppleCommands
class PackBasket : AppleCommands
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.r3.developers.apples.contracts

import com.r3.developers.apples.states.AppleStamp
import net.corda.v5.ledger.utxo.Contract
import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction
import java.lang.IllegalArgumentException

class AppleStampContract : Contract {
override fun verify(transaction: UtxoLedgerTransaction) {
when (val command = transaction.commands.first()){
is AppleCommands.Issue -> {
val output = transaction.getOutputStates(AppleStamp::class.java).first()
require(transaction.outputContractStates.size == 1){
"This transaction should only have one AppleStamp state as output"
}
require(output.stampDesc.isNotBlank()){
"The output AppleStamp state should have a clear description of the type of redeemable goods"
}
}
is AppleCommands.Redeem -> {
val inputs = transaction.getInputStates(AppleStamp::class.java)
require (inputs.size == 1) {
"This transaction should only have one AppleStamp as input"
}
require (transaction.signatories.contains(inputs.first().holder)){
"The holder of the input AppleStamp state must be a signatory to the transaction"
}
}
else -> {
throw IllegalArgumentException("Incorrect type of AppleStamp commands: ${command::class.java.name}")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.r3.developers.apples.contracts

import com.r3.developers.apples.states.AppleStamp
import com.r3.developers.apples.states.BasketOfApples
import net.corda.v5.ledger.utxo.Contract
import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction
import java.lang.IllegalArgumentException

class BasketOfApplesContract : Contract {
override fun verify(transaction: UtxoLedgerTransaction) {
when (val command = transaction.commands.first()){
is AppleCommands.PackBasket -> {
val output = transaction.getOutputStates(BasketOfApples::class.java).first()
require(transaction.outputContractStates.size == 1){
"This transaction should only output one BasketOfApples state"
}
require(output.description.isNotBlank()){
"The output BasketOfApples state should have clear description of Apple product"
}
require(output.weight > 0){
"The output BasketOfApples weight should have non zero weight"
}
}
is AppleCommands.Redeem -> {
require(transaction.inputContractStates.size == 2){
"This transaction should consume two states"
}
val stampInputs = transaction.getInputStates(AppleStamp::class.java)
val basketInputs = transaction.getInputStates(BasketOfApples::class.java)
require(stampInputs.isNotEmpty() && basketInputs.isNotEmpty()){
"This transaction should have exactly one AppleStamp and one BasketOfApples input state"
}
require(stampInputs.single().issuer == basketInputs.single().farm){
"The issuer of the Apple Stamp should be the producing farm of this basket of apples"
}
require(basketInputs.single().weight > 0){
"The basket of apple has to weigh more than 0"
}
}
else ->{
throw IllegalArgumentException("Incorrect type of BasketOfApples commands: ${command::class.java.name}")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.r3.developers.apples.states

import com.r3.developers.apples.contracts.AppleStampContract
import net.corda.v5.ledger.utxo.BelongsToContract
import net.corda.v5.ledger.utxo.ContractState
import java.security.PublicKey
import java.util.*

@BelongsToContract(AppleStampContract::class)
class AppleStamp (
val id : UUID,
val stampDesc : String,
val issuer : PublicKey,
val holder : PublicKey,
private val participants : List<PublicKey>
): ContractState{

override fun getParticipants(): List<PublicKey> = participants

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.r3.developers.apples.states

import com.r3.developers.apples.contracts.BasketOfApplesContract
import net.corda.v5.ledger.utxo.BelongsToContract
import net.corda.v5.ledger.utxo.ContractState
import java.security.PublicKey
import java.util.*

@BelongsToContract(BasketOfApplesContract::class)
class BasketOfApples (
val description : String,
val farm : PublicKey,
val owner : PublicKey,
val weight : Int,
private val participants : List<PublicKey>
) : ContractState {

override fun getParticipants():List<PublicKey> = participants

fun changeOwner(buyer: PublicKey): BasketOfApples {
val participants = listOf(farm,buyer)
return BasketOfApples(description,farm,buyer,weight,participants)
}
}
2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,5 @@ cpiUploadDefault=10000
# R3 internal repository
# Use this version when pointing to artefacts in artifactory that have not been published to S3
artifactoryContextUrl=https://software.r3.com/artifactory
[email protected]
cordaArtifactoryPassword=AKCp8ohxcU4MwKiWcZ7NceHx2ttX3hJD9VjaUDXvgovBrdn8vuDd6WGtMj7qKbVKNxsCpkyZU
2 changes: 1 addition & 1 deletion workflows/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ cordapp {
// The cordapp section contains either a workflow or contract subsection depending on the type of component.
// Declares the type and metadata of the CPK (this CPB has one CPK).
workflow {
name "WorkflowsModuleNameHere"
name "Apples utxo example workflow"
versionId 1
vendor "VendorNameHere"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.r3.developers.apples.workflows

import com.r3.developers.apples.contracts.AppleCommands
import com.r3.developers.apples.states.AppleStamp
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.InitiatingFlow
import net.corda.v5.application.marshalling.JsonMarshallingService
import net.corda.v5.application.membership.MemberLookup
import net.corda.v5.application.messaging.FlowMessaging
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.UtxoLedgerService
import java.lang.Exception
import java.lang.IllegalArgumentException
import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.*


@InitiatingFlow(protocol = "create-and-issue-apple-stamp")
class CreateAndIssueAppleStampFlow : ClientStartableFlow {

@CordaInject
lateinit var flowMessaging: FlowMessaging

@CordaInject
lateinit var jsonMarshallingService: JsonMarshallingService

@CordaInject
lateinit var memberLookup: MemberLookup

@CordaInject
lateinit var notaryLookup: NotaryLookup

@CordaInject
lateinit var utxoLedgerService: UtxoLedgerService

private data class CreateAndIssueAppleStampRequest(
val stampDescription: String,
val holder: MemberX500Name
)

@Suspendable
override fun call(requestBody: ClientRequestBody): String {
val request = requestBody.getRequestBodyAs(
jsonMarshallingService,
CreateAndIssueAppleStampRequest::class.java)
val stampDescription = request.stampDescription
val holderName = request.holder

val notaryInfo = notaryLookup.notaryServices.single()

val issuer = memberLookup.myInfo().ledgerKeys.first()
val holder = memberLookup.lookup(holderName)
?.let {it.ledgerKeys.first()}
?: throw IllegalArgumentException("The holder $holderName does not exist within the network")

val newStamp = AppleStamp(
id = UUID.randomUUID(),
stampDesc = stampDescription,
issuer = issuer,
holder = holder,
participants = listOf(issuer,holder)
)

val transaction = utxoLedgerService.createTransactionBuilder()
.setNotary(notaryInfo.name)
.addOutputState(newStamp)
.addCommand(AppleCommands.Issue())
.setTimeWindowUntil(Instant.now().plus(1,ChronoUnit.DAYS))
.addSignatories(listOf(issuer,holder))
.toSignedTransaction()

val session = flowMessaging.initiateFlow(holderName)
return try{
utxoLedgerService.finalize(transaction, listOf(session))
newStamp.id.toString()
}catch (e : Exception){
"Flow failed, message: ${e.message}"
}
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.r3.developers.apples.workflows

import net.corda.v5.application.flows.CordaInject
import net.corda.v5.application.flows.InitiatedBy
import net.corda.v5.application.flows.ResponderFlow
import net.corda.v5.application.messaging.FlowSession
import net.corda.v5.base.annotations.Suspendable
import net.corda.v5.ledger.utxo.UtxoLedgerService

@InitiatedBy(protocol = "create-and-issue-apple-stamp")
class CreateAndIssueAppleStampResponderFlow : ResponderFlow {

@CordaInject
lateinit var utxoLedgerService: UtxoLedgerService

@Suspendable
override fun call(session: FlowSession) {
utxoLedgerService.receiveFinality(session){
transaction ->
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.r3.developers.apples.workflows
import com.r3.developers.apples.contracts.AppleCommands
import com.r3.developers.apples.states.BasketOfApples
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.marshalling.JsonMarshallingService
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 java.time.Instant
import java.time.temporal.ChronoUnit


class PackageApplesFlow : ClientStartableFlow {
@CordaInject
lateinit var jsonMarshallingService: JsonMarshallingService

@CordaInject
lateinit var memberLookup: MemberLookup

@CordaInject
lateinit var notaryLookup: NotaryLookup

@CordaInject
lateinit var utxoLedgerService: UtxoLedgerService

private data class PackageApplesRequest(
val appleDescription: String,
val weight : Int
)

@Suspendable
override fun call(requestBody: ClientRequestBody): String {
val request = requestBody.getRequestBodyAs(jsonMarshallingService, PackageApplesRequest::class.java)
val appleDescription = request.appleDescription
val weight = request.weight

val notary = notaryLookup.notaryServices.single()

val myKey = memberLookup.myInfo().ledgerKeys.first()

val basket = BasketOfApples(
description = appleDescription,
farm = myKey,
owner = myKey,
weight = weight,
participants = listOf(myKey)
)

val transaction = utxoLedgerService.createTransactionBuilder()
.setNotary(notary.name)
.addOutputState(basket)
.addCommand(AppleCommands.PackBasket())
.setTimeWindowUntil(Instant.now().plus(1,ChronoUnit.DAYS))
.addSignatories(listOf(myKey))
.toSignedTransaction()

return try {
utxoLedgerService.finalize(transaction, emptyList()).toString()
}catch(e: Exception){
"Flow failed, message: ${e.message}"
}
}
}
Loading