Introducing InitiatedBy annotation to be used on initiated flows to simplify flow registration.

This removes the need to do manual registration using the PluginServiceHub. As a result CordaPluginRegistry.servicePlugins is no longer needed. For oracles and services there is a CorDappService annotation.

I've also fixed the InitiatingFlow annotation such that client flows can be customised (sub-typed) without it breaking the flow sessions.
This commit is contained in:
Shams Asari
2017-05-19 16:14:48 +01:00
parent e117ab7703
commit 329e5ff17b
66 changed files with 892 additions and 760 deletions

View File

@ -9,9 +9,9 @@ import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.Party
import net.corda.core.node.PluginServiceHub
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.unconsumedStates
import net.corda.core.serialization.CordaSerializable
@ -21,13 +21,6 @@ import net.corda.flows.FinalityFlow
import net.corda.flows.ResolveTransactionsFlow
import java.util.*
object FxTransactionDemoTutorial {
// Would normally be called by custom service init in a CorDapp
fun registerFxProtocols(pluginHub: PluginServiceHub) {
pluginHub.registerServiceFlow(ForeignExchangeFlow::class.java, ::ForeignExchangeRemoteFlow)
}
}
@CordaSerializable
private data class FxRequest(val tradeId: String,
val amount: Amount<Issued<Currency>>,
@ -212,6 +205,7 @@ class ForeignExchangeFlow(val tradeId: String,
// DOCEND 3
}
@InitiatedBy(ForeignExchangeFlow::class)
class ForeignExchangeRemoteFlow(val source: Party) : FlowLogic<Unit>() {
@Suspendable
override fun call() {

View File

@ -6,10 +6,10 @@ import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.containsAny
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.PluginServiceHub
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.linearHeadsOfType
import net.corda.core.serialization.CordaSerializable
@ -19,22 +19,13 @@ import net.corda.flows.FinalityFlow
import java.security.PublicKey
import java.time.Duration
object WorkflowTransactionBuildTutorial {
// Would normally be called by custom service init in a CorDapp
fun registerWorkflowProtocols(pluginHub: PluginServiceHub) {
pluginHub.registerServiceFlow(SubmitCompletionFlow::class.java, ::RecordCompletionFlow)
}
}
// DOCSTART 1
// Helper method to locate the latest Vault version of a LinearState from a possibly out of date StateRef
inline fun <reified T : LinearState> ServiceHub.latest(ref: StateRef): StateAndRef<T> {
val linearHeads = vaultService.linearHeadsOfType<T>()
val original = toStateAndRef<T>(ref)
return linearHeads.get(original.state.data.linearId)!!
return linearHeads[original.state.data.linearId]!!
}
// DOCEND 1
// Minimal state model of a manual approval process
@ -87,7 +78,7 @@ data class TradeApprovalContract(override val legalContractReference: SecureHash
"Issue of new WorkflowContract must not include any inputs" using (tx.inputs.isEmpty())
"Issue of new WorkflowContract must be in a unique transaction" using (tx.outputs.size == 1)
}
val issued = tx.outputs.get(0) as TradeApprovalContract.State
val issued = tx.outputs[0] as TradeApprovalContract.State
requireThat {
"Issue requires the source Party as signer" using (command.signers.contains(issued.source.owningKey))
"Initial Issue state must be NEW" using (issued.state == WorkflowState.NEW)
@ -96,9 +87,9 @@ data class TradeApprovalContract(override val legalContractReference: SecureHash
is Commands.Completed -> {
val stateGroups = tx.groupStates(TradeApprovalContract.State::class.java) { it.linearId }
require(stateGroups.size == 1) { "Must be only a single proposal in transaction" }
for (group in stateGroups) {
val before = group.inputs.single()
val after = group.outputs.single()
for ((inputs, outputs) in stateGroups) {
val before = inputs.single()
val after = outputs.single()
requireThat {
"Only a non-final trade can be modified" using (before.state == WorkflowState.NEW)
"Output must be a final state" using (after.state in setOf(WorkflowState.APPROVED, WorkflowState.REJECTED))
@ -227,6 +218,7 @@ class SubmitCompletionFlow(val ref: StateRef, val verdict: WorkflowState) : Flow
* Then after checking to sign it and eventually store the fully notarised
* transaction to the ledger.
*/
@InitiatedBy(SubmitCompletionFlow::class)
class RecordCompletionFlow(val source: Party) : FlowLogic<Unit>() {
@Suspendable
override fun call(): Unit {

View File

@ -29,14 +29,11 @@ class FxTransactionBuildTutorialTest {
val notaryService = ServiceInfo(ValidatingNotaryService.type)
notaryNode = net.createNode(
legalName = DUMMY_NOTARY.name,
overrideServices = mapOf(Pair(notaryService, DUMMY_NOTARY_KEY)),
overrideServices = mapOf(notaryService to DUMMY_NOTARY_KEY),
advertisedServices = *arrayOf(ServiceInfo(NetworkMapService.type), notaryService))
nodeA = net.createPartyNode(notaryNode.info.address)
nodeB = net.createPartyNode(notaryNode.info.address)
FxTransactionDemoTutorial.registerFxProtocols(nodeA.services)
FxTransactionDemoTutorial.registerFxProtocols(nodeB.services)
WorkflowTransactionBuildTutorial.registerWorkflowProtocols(nodeA.services)
WorkflowTransactionBuildTutorial.registerWorkflowProtocols(nodeB.services)
nodeB.registerInitiatedFlow(ForeignExchangeRemoteFlow::class.java)
}
@After

View File

@ -4,7 +4,6 @@ import net.corda.core.contracts.LinearState
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef
import net.corda.core.getOrThrow
import net.corda.core.node.ServiceEntry
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.linearHeadsOfType
@ -30,7 +29,7 @@ class WorkflowTransactionBuildTutorialTest {
private inline fun <reified T : LinearState> ServiceHub.latest(ref: StateRef): StateAndRef<T> {
val linearHeads = vaultService.linearHeadsOfType<T>()
val original = storageService.validatedTransactions.getTransaction(ref.txhash)!!.tx.outRef<T>(ref.index)
return linearHeads.get(original.state.data.linearId)!!
return linearHeads[original.state.data.linearId]!!
}
@Before
@ -43,10 +42,7 @@ class WorkflowTransactionBuildTutorialTest {
advertisedServices = *arrayOf(ServiceInfo(NetworkMapService.type), notaryService))
nodeA = net.createPartyNode(notaryNode.info.address)
nodeB = net.createPartyNode(notaryNode.info.address)
FxTransactionDemoTutorial.registerFxProtocols(nodeA.services)
FxTransactionDemoTutorial.registerFxProtocols(nodeB.services)
WorkflowTransactionBuildTutorial.registerWorkflowProtocols(nodeA.services)
WorkflowTransactionBuildTutorial.registerWorkflowProtocols(nodeB.services)
nodeA.registerInitiatedFlow(RecordCompletionFlow::class.java)
}
@After