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

Split the Issue, Move and Redeem flows into the general and an extra custom flow #47

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,15 @@ import com.r3.corda.sdk.token.contracts.utilities.withNotary
import com.r3.corda.sdk.token.workflow.utilities.addPartyToDistributionList
import net.corda.core.contracts.Amount
import net.corda.core.contracts.TransactionState
import net.corda.core.flows.FinalityFlow
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSession
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.ReceiveFinalityFlow
import net.corda.core.flows.StartableByRPC
import net.corda.core.contracts.requireThat
import net.corda.core.flows.*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try not to use * imports

import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.StatesToRecord
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker
import java.security.PublicKey

/**
* This flow takes a bunch of parameters and is used to issue a token or an amount of some token on the ledger to
Expand Down Expand Up @@ -61,9 +57,7 @@ import net.corda.core.utilities.ProgressTracker
*/
object IssueToken {

@InitiatingFlow
@StartableByRPC
class Initiator<T : TokenType>(
abstract class Primary<T : TokenType>(
val token: T,
val issueTo: AbstractParty,
val notary: Party,
Expand All @@ -72,26 +66,27 @@ object IssueToken {
) : FlowLogic<SignedTransaction>() {
companion object {
object DIST_LIST : ProgressTracker.Step("Adding party to distribution list.")
object EXTRA_FLOW : ProgressTracker.Step("Starting extra flow")
object SIGNING : ProgressTracker.Step("Signing transaction proposal.")
object RECORDING : ProgressTracker.Step("Recording signed transaction.") {
override fun childProgressTracker() = FinalityFlow.tracker()
}

fun tracker() = ProgressTracker(DIST_LIST, SIGNING, RECORDING)
fun tracker() = ProgressTracker(DIST_LIST, EXTRA_FLOW, SIGNING, RECORDING)
}

override val progressTracker: ProgressTracker = tracker()

@Suspendable
override fun call(): SignedTransaction {
// This is the identity which will be used to issue tokens.
// We also need a session for the other side.
val me: Party = ourIdentity
val holderParty = serviceHub.identityService.wellKnownPartyFromAnonymous(issueTo)
?: throw IllegalArgumentException("Called IssueToken flow with anonymous party that node doesn't know about. " +
"Make sure that RequestConfidentialIdentity flow is called before.")
val holderSession = if (session == null) initiateFlow(holderParty) else session
abstract fun transactionExtra(me: Party,
holderParty: Party,
holderSession: FlowSession,
builder: TransactionBuilder): List<PublicKey>

open fun issue(me: Party,
holderParty: Party,
holderSession: FlowSession,
builder: TransactionBuilder): Unit {
// Create the issued token. We add this to the commands for grouping.
val issuedToken: IssuedTokenType<T> = token issuedBy me

Expand Down Expand Up @@ -119,32 +114,96 @@ object IssueToken {

// Create the transaction.
val transactionState: TransactionState<AbstractToken> = heldToken withNotary notary
val utx: TransactionBuilder = TransactionBuilder(notary = notary).apply {

builder.apply {
addCommand(data = IssueTokenCommand(issuedToken), keys = listOf(me.owningKey))
addOutputState(state = transactionState)
}
}

@Suspendable
override fun call(): SignedTransaction {
// This is the identity which will be used to issue tokens.
// We also need a session for the other side.
val me: Party = ourIdentity
val holderParty = serviceHub.identityService.wellKnownPartyFromAnonymous(issueTo)
?: throw IllegalArgumentException("Called IssueToken flow with anonymous party that node doesn't know about. " +
"Make sure that RequestConfidentialIdentity flow is called before.")
val holderSession = if (session == null) initiateFlow(holderParty) else session

val builder = TransactionBuilder(notary = notary)
issue(me, holderParty, holderSession, builder)

progressTracker.currentStep = EXTRA_FLOW
val extraKeys = transactionExtra(me, holderParty, holderSession, builder)

progressTracker.currentStep = SIGNING
// Sign the transaction. Only Concrete Parties should be used here.
val stx: SignedTransaction = serviceHub.signInitialTransaction(utx)
val ptx: SignedTransaction = serviceHub.signInitialTransaction(builder, listOf(me.owningKey))
progressTracker.currentStep = RECORDING
// Can issue to yourself, but finality flow doesn't take a session then.
val sessions = if (me == holderParty) emptyList() else listOf(holderSession)

val stx = ptx +
if (!serviceHub.myInfo.isLegalIdentity(holderSession.counterparty)) {
subFlow(CollectSignatureFlow(ptx, holderSession, extraKeys))
} else {
listOf()
}

return subFlow(FinalityFlow(transaction = stx,
progressTracker = RECORDING.childProgressTracker(),
sessions = sessions
))
}
}

@InitiatedBy(Initiator::class)
class Responder(val otherSession: FlowSession) : FlowLogic<Unit>() {
abstract class Secondary(val otherSession: FlowSession) : FlowLogic<Unit>() {

@Suspendable
abstract fun checkTransaction(stx: SignedTransaction)

@Suspendable
override fun call(): Unit {
// We must do this check because FinalityFlow does not send locally and we want to be able to issue to ourselves.
if (!serviceHub.myInfo.isLegalIdentity(otherSession.counterparty)) {

val signTransactionFlow = object : SignTransactionFlow(otherSession) {
override fun checkTransaction(stx: SignedTransaction) = this@Secondary.checkTransaction(stx)
}

val txId = subFlow(signTransactionFlow).id

// Resolve the issuance transaction.
subFlow(ReceiveFinalityFlow(otherSideSession = otherSession, statesToRecord = StatesToRecord.ONLY_RELEVANT))
subFlow(ReceiveFinalityFlow(otherSideSession = otherSession,
statesToRecord = StatesToRecord.ONLY_RELEVANT, expectedTxId = txId))
}
}
}

@InitiatingFlow
@StartableByRPC
class Initiator<T : TokenType>(
token: T,
issueTo: AbstractParty,
notary: Party,
amount: Amount<T>? = null,
session: FlowSession? = null
) : Primary<T>(token, issueTo, notary, amount, session) {

@Suspendable
override fun transactionExtra(me: Party,
holderParty: Party,
holderSession: FlowSession,
builder: TransactionBuilder): List<PublicKey> {
return listOf()
}
}

@InitiatedBy(Initiator::class)
class Responder(otherSession: FlowSession) : Secondary(otherSession) {

@Suspendable
override fun checkTransaction(stx: SignedTransaction) = requireThat { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,74 +5,137 @@ import com.r3.corda.sdk.token.contracts.types.TokenType
import com.r3.corda.sdk.token.workflow.selection.TokenSelection
import com.r3.corda.sdk.token.workflow.selection.generateMoveNonFungible
import net.corda.core.contracts.Amount
import net.corda.core.flows.FinalityFlow
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSession
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.ReceiveFinalityFlow
import net.corda.core.flows.StartableByRPC
import net.corda.core.contracts.requireThat
import net.corda.core.flows.*
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.StatesToRecord
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker
import java.security.PublicKey

object MoveToken {

@InitiatingFlow
@StartableByRPC
class Initiator<T : TokenType>(
abstract class Primary<T : TokenType>(
val ownedToken: T,
val holder: AbstractParty,
val amount: Amount<T>? = null,
val session: FlowSession? = null
) : FlowLogic<SignedTransaction>() {
companion object {
object GENERATE_MOVE : ProgressTracker.Step("Generating tokens move.")
object EXTRA_FLOW : ProgressTracker.Step("Starting extra flow")
object SIGNING : ProgressTracker.Step("Signing transaction proposal.")
object RECORDING : ProgressTracker.Step("Recording signed transaction.") {
override fun childProgressTracker() = FinalityFlow.tracker()
}

fun tracker() = ProgressTracker(GENERATE_MOVE, SIGNING, RECORDING)
fun tracker() = ProgressTracker(GENERATE_MOVE, EXTRA_FLOW, SIGNING, RECORDING)
}

override val progressTracker: ProgressTracker = tracker()

@Suspendable
abstract fun transactionExtra(me: Party,
holderParty: Party,
holderSession: FlowSession,
builder: TransactionBuilder): List<PublicKey>

@Suspendable
open fun move(holderParty: Party,
holderSession: FlowSession): Pair<TransactionBuilder, List<PublicKey>> {

return if (amount == null) {
generateMoveNonFungible(serviceHub.vaultService, ownedToken, holder)
} else {
val tokenSelection = TokenSelection(serviceHub)
tokenSelection.generateMove(TransactionBuilder(), amount, holder)
}
}

@Suspendable
override fun call(): SignedTransaction {
val me: Party = ourIdentity
val holderParty = serviceHub.identityService.wellKnownPartyFromAnonymous(holder)
?: throw IllegalArgumentException("Called MoveToken flow with anonymous party that node doesn't know about. " +
"Make sure that RequestConfidentialIdentity flow is called before.")
val holderSession = if (session == null) initiateFlow(holderParty) else session

progressTracker.currentStep = GENERATE_MOVE
val (builder, keys) = if (amount == null) {
generateMoveNonFungible(serviceHub.vaultService, ownedToken, holder)
} else {
val tokenSelection = TokenSelection(serviceHub)
tokenSelection.generateMove(TransactionBuilder(), amount, holder)
}
val (builder, keys) = move(holderParty, holderSession)

progressTracker.currentStep = EXTRA_FLOW
val extraKeys = transactionExtra(me, holderParty, holderSession, builder)

progressTracker.currentStep = SIGNING
// WARNING: At present, the recipient will not be signed up to updates from the token maintainer.
val stx: SignedTransaction = serviceHub.signInitialTransaction(builder, keys)
val ptx: SignedTransaction = serviceHub.signInitialTransaction(builder, keys)
progressTracker.currentStep = RECORDING
val sessions = if (ourIdentity == holderParty) emptyList() else listOf(holderSession)

val stx = ptx +
if (!serviceHub.myInfo.isLegalIdentity(holderSession.counterparty)) {
subFlow(CollectSignatureFlow(ptx, holderSession, extraKeys))
} else {
listOf()
}

return subFlow(FinalityFlow(transaction = stx, sessions = sessions))
}
}

// TODO Don't really need it anymore as it calls only finality flow
@InitiatedBy(Initiator::class)
class Responder(val otherSession: FlowSession) : FlowLogic<Unit>() {
abstract class Secondary(val otherSession: FlowSession) : FlowLogic<Unit>() {

@Suspendable
abstract fun checkTransaction(stx: SignedTransaction)

@Suspendable
override fun call(): Unit {
// Resolve the issuance transaction.
if (!serviceHub.myInfo.isLegalIdentity(otherSession.counterparty)) {
subFlow(ReceiveFinalityFlow(otherSideSession = otherSession, statesToRecord = StatesToRecord.ONLY_RELEVANT))

val parties = serviceHub.identityService.wellKnownPartyFromAnonymous(otherSession.counterparty)
?: throw IllegalArgumentException("Called MoveToken flow with anonymous party that node doesn't know about. " +
"Make sure that RequestConfidentialIdentity flow is called before.")

parties

val signTransactionFlow = object : SignTransactionFlow(otherSession) {
override fun checkTransaction(stx: SignedTransaction) = this@Secondary.checkTransaction(stx)
}

val txId = subFlow(signTransactionFlow).id

// Resolve the issuance transaction.
subFlow(ReceiveFinalityFlow(otherSideSession = otherSession,
statesToRecord = StatesToRecord.ONLY_RELEVANT, expectedTxId = txId))
}
}
}

@InitiatingFlow
@StartableByRPC
class Initiator<T : TokenType>(
ownedToken: T,
holder: AbstractParty,
amount: Amount<T>? = null,
session: FlowSession? = null
) : Primary<T>(ownedToken, holder, amount, session) {

@Suspendable
override fun transactionExtra(me: Party,
holderParty: Party,
holderSession: FlowSession,
builder: TransactionBuilder): List<PublicKey> {
return listOf()
}
}

@InitiatedBy(Initiator::class)
class Responder(otherSession: FlowSession) : Secondary(otherSession) {

@Suspendable
override fun checkTransaction(stx: SignedTransaction) = requireThat { }
}
}
Loading