-
Notifications
You must be signed in to change notification settings - Fork 76
/
UpdateEvolvableTokenFlow.kt
84 lines (74 loc) · 4.25 KB
/
UpdateEvolvableTokenFlow.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package com.r3.corda.lib.tokens.workflows.flows.evolvable
import co.paralleluniverse.fibers.Suspendable
import com.r3.corda.lib.tokens.contracts.states.EvolvableTokenType
import com.r3.corda.lib.tokens.workflows.internal.flows.finality.ObserverAwareFinalityFlow
import net.corda.core.contracts.StateAndRef
import net.corda.core.flows.CollectSignaturesFlow
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSession
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
/**
* A flow to update an existing evolvable token type which is already recorded on the ledger. This is an IN-LINE flow
* which means it MUST be invoked with a subFlow call from an Initiating Flow.
*
* @property oldStateAndRef the existing evolvable token type to update
* @property newState the new version of the evolvable token type
* @property participantSessions a list of sessions for participants in the evolvable token types
* @property observerSessions a list of sessions for any observers to the create observable token transaction
*/
class UpdateEvolvableTokenFlow @JvmOverloads constructor(
val oldStateAndRef: StateAndRef<EvolvableTokenType>,
val newState: EvolvableTokenType,
val participantSessions: List<FlowSession>,
val observerSessions: List<FlowSession> = emptyList()
) : FlowLogic<SignedTransaction>() {
/**
* Simple notification class to inform counterparties of their role. In this instance, informs participants if
* they are required to sign the command. This is intended to allow maintainers to sign commands while participants
* and other observers merely finalise the transaction.
*/
@CordaSerializable
data class Notification(val signatureRequired: Boolean = false)
@Suspendable
override fun call(): SignedTransaction {
require(ourIdentity in oldStateAndRef.state.data.maintainers) {
"This flow can only be started by existing maintainers of the EvolvableTokenType."
}
// Create a transaction which updates the ledger with the new evolvable token.
// The tokenHolders listed as maintainers in the old state should be the signers.
// TODO Should this be both old and new maintainer lists?
val utx = addUpdateEvolvableToken(
TransactionBuilder(notary = oldStateAndRef.state.notary),
oldStateAndRef,
newState
)
// Sign the transaction proposal (creating a partially signed transaction, or ptx)
val ptx: SignedTransaction = serviceHub.signInitialTransaction(utx)
// Gather signatures from other maintainers
val otherMaintainerSessions = participantSessions.filter { it.counterparty in evolvableTokens.otherMaintainers(ourIdentity) }
otherMaintainerSessions.forEach { it.send(Notification(signatureRequired = true)) }
val stx = subFlow(CollectSignaturesFlow(
partiallySignedTx = ptx,
sessionsToCollectFrom = otherMaintainerSessions
))
// Distribute to all observers, including maintainers, participants, and subscribers (via distribution list)
val wellKnownObserverSessions = participantSessions.filter { it.counterparty in wellKnownObservers }
val allObserverSessions = (wellKnownObserverSessions + observerSessions).toSet()
observerSessions.forEach { it.send(Notification(signatureRequired = false)) }
return subFlow(ObserverAwareFinalityFlow(signedTransaction = stx, allSessions = otherMaintainerSessions + allObserverSessions))
}
// TODO Refactor it more.
private val oldState get() = oldStateAndRef.state.data
private val evolvableTokens = listOf(oldState, newState)
private fun otherObservers(subscribers: Set<Party>): Set<AbstractParty> {
return (evolvableTokens.participants() + subscribers).minus(evolvableTokens.maintainers()).minus(this.ourIdentity)
}
private val wellKnownObservers
get(): List<Party> {
return otherObservers(subscribersForState(newState, serviceHub)).map { serviceHub.identityService.wellKnownPartyFromAnonymous(it)!! }
}
}