Rename protocol to flow.

This commit is contained in:
rick.parker 2016-11-22 11:56:47 +00:00
parent d8e09f7174
commit f68529d1fd
120 changed files with 1157 additions and 1164 deletions

View File

@ -7,24 +7,24 @@ import net.corda.core.contracts.Amount
import net.corda.core.contracts.Issued
import net.corda.core.contracts.PartyAndReference
import net.corda.core.contracts.USD
import net.corda.core.flows.StateMachineRunId
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.StateMachineTransactionMapping
import net.corda.core.node.services.Vault
import net.corda.core.protocols.StateMachineRunId
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.SignedTransaction
import net.corda.flows.CashCommand
import net.corda.flows.CashFlow
import net.corda.node.driver.driver
import net.corda.node.services.User
import net.corda.node.services.config.configureTestSSL
import net.corda.node.services.messaging.ArtemisMessagingComponent
import net.corda.node.services.messaging.StateMachineUpdate
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.startProtocolPermission
import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.protocols.CashCommand
import net.corda.protocols.CashProtocol
import net.corda.testing.expect
import net.corda.testing.expectEvents
import net.corda.testing.sequence
@ -56,7 +56,7 @@ class NodeMonitorModelTest {
val driverStarted = CountDownLatch(1)
driverThread = thread {
driver {
val cashUser = User("user1", "test", permissions = setOf(startProtocolPermission<CashProtocol>()))
val cashUser = User("user1", "test", permissions = setOf(startFlowPermission<CashFlow>()))
val aliceNodeFuture = startNode("Alice", rpcUsers = listOf(cashUser))
val notaryNodeFuture = startNode("Notary", advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type)))

View File

@ -5,7 +5,7 @@ import net.corda.core.contracts.*
import net.corda.core.crypto.Party
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.TransactionBuilder
import net.corda.protocols.CashCommand
import net.corda.flows.CashCommand
/**
* [Generator]s for incoming/outgoing events to/from the [WalletMonitorService]. Internally it keeps track of owned

View File

@ -1,18 +1,18 @@
package net.corda.client.model
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.value.ObservableValue
import javafx.collections.ObservableList
import javafx.collections.ObservableMap
import net.corda.client.fxutils.*
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.StateMachineRunId
import net.corda.core.node.services.StateMachineTransactionMapping
import net.corda.core.protocols.StateMachineRunId
import net.corda.core.transactions.SignedTransaction
import net.corda.node.services.messaging.StateMachineUpdate
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.value.ObservableValue
import javafx.collections.ObservableList
import javafx.collections.ObservableMap
import org.fxmisc.easybind.EasyBind
data class GatheredTransactionData(
@ -59,7 +59,7 @@ sealed class TransactionCreateStatus(val message: String?) {
override fun toString(): String = message ?: javaClass.simpleName
}
data class ProtocolStatus(
data class FlowStatus(
val status: String
)
@ -71,12 +71,12 @@ sealed class StateMachineStatus(val stateMachineName: String) {
data class StateMachineData(
val id: StateMachineRunId,
val protocolStatus: ObservableValue<ProtocolStatus?>,
val flowStatus: ObservableValue<FlowStatus?>,
val stateMachineStatus: ObservableValue<StateMachineStatus>
)
/**
* This model provides an observable list of transactions and what state machines/protocols recorded them
* This model provides an observable list of transactions and what state machines/flows recorded them
*/
class GatheredTransactionDataModel {
@ -92,7 +92,7 @@ class GatheredTransactionDataModel {
when (update) {
is StateMachineUpdate.Added -> {
val added: SimpleObjectProperty<StateMachineStatus> =
SimpleObjectProperty(StateMachineStatus.Added(update.stateMachineInfo.protocolLogicClassName))
SimpleObjectProperty(StateMachineStatus.Added(update.stateMachineInfo.flowLogicClassName))
map[update.id] = added
}
is StateMachineUpdate.Removed -> {
@ -103,7 +103,7 @@ class GatheredTransactionDataModel {
}
}
private val stateMachineDataList = LeftOuterJoinedMap(stateMachineStatus, progressEvents) { id, status, progress ->
StateMachineData(id, progress.map { it?.let { ProtocolStatus(it.message) } }, status)
StateMachineData(id, progress.map { it?.let { FlowStatus(it.message) } }, status)
}.getObservableValues()
private val stateMachineDataMap = stateMachineDataList.associateBy(StateMachineData::id)
private val smTxMappingList = stateMachineTransactionMapping.recordInSequence()

View File

@ -3,18 +3,18 @@ package net.corda.client.model
import com.google.common.net.HostAndPort
import javafx.beans.property.SimpleObjectProperty
import net.corda.client.CordaRPCClient
import net.corda.core.flows.StateMachineRunId
import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.StateMachineTransactionMapping
import net.corda.core.node.services.Vault
import net.corda.core.protocols.StateMachineRunId
import net.corda.core.transactions.SignedTransaction
import net.corda.flows.CashCommand
import net.corda.flows.CashFlow
import net.corda.node.services.config.NodeSSLConfiguration
import net.corda.node.services.messaging.CordaRPCOps
import net.corda.node.services.messaging.StateMachineInfo
import net.corda.node.services.messaging.StateMachineUpdate
import net.corda.node.services.messaging.startProtocol
import net.corda.protocols.CashCommand
import net.corda.protocols.CashProtocol
import net.corda.node.services.messaging.startFlow
import rx.Observable
import rx.subjects.PublishSubject
@ -63,7 +63,7 @@ class NodeMonitorModel {
val proxy = client.proxy()
val (stateMachines, stateMachineUpdates) = proxy.stateMachinesAndUpdates()
// Extract the protocol tracking stream
// Extract the flow tracking stream
// TODO is there a nicer way of doing this? Stream of streams in general results in code like this...
val currentProgressTrackerUpdates = stateMachines.mapNotNull { stateMachine ->
ProgressTrackingEvent.createStreamFromStateMachineInfo(stateMachine)
@ -100,7 +100,7 @@ class NodeMonitorModel {
// Client -> Service
clientToServiceSource.subscribe {
proxy.startProtocol(::CashProtocol, it)
proxy.startFlow(::CashFlow, it)
}
proxyObservable.set(proxy)
}

View File

@ -4,9 +4,9 @@ import net.corda.core.contracts.clauses.Clause
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogicRef
import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.node.services.ServiceType
import net.corda.core.protocols.ProtocolLogicRef
import net.corda.core.protocols.ProtocolLogicRefFactory
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.serialization.serialize
import net.corda.core.transactions.TransactionBuilder
@ -206,16 +206,16 @@ data class ScheduledStateRef(val ref: StateRef, override val scheduledAt: Instan
/**
* This class represents the lifecycle activity that a contract state of type [LinearState] would like to perform at a given point in time.
* e.g. run a fixing protocol.
* e.g. run a fixing flow.
*
* Note the use of [ProtocolLogicRef] to represent a safe way to transport a [ProtocolLogic] out of the contract sandbox.
* Note the use of [FlowLogicRef] to represent a safe way to transport a [FlowLogic] out of the contract sandbox.
*
* Currently we support only protocol based activities as we expect there to be a transaction generated off the back of
* Currently we support only flow based activities as we expect there to be a transaction generated off the back of
* the activity, otherwise we have to start tracking secondary state on the platform of which scheduled activities
* for a particular [ContractState] have been processed/fired etc. If the activity is not "on ledger" then the
* scheduled activity shouldn't be either.
*/
data class ScheduledActivity(val logicRef: ProtocolLogicRef, override val scheduledAt: Instant) : Scheduled
data class ScheduledActivity(val logicRef: FlowLogicRef, override val scheduledAt: Instant) : Scheduled
/**
* A state that evolves by superseding itself, all of which share the common "linearId".
@ -261,16 +261,16 @@ interface SchedulableState : ContractState {
* [ContractState], what that activity is and at what point in time it should be initiated.
* This can be used to implement deadlines for payment or processing of financial instruments according to a schedule.
*
* The state has no reference to it's own StateRef, so supply that for use as input to any ProtocolLogic constructed.
* The state has no reference to it's own StateRef, so supply that for use as input to any FlowLogic constructed.
*
* @return null if there is no activity to schedule.
*/
fun nextScheduledActivity(thisStateRef: StateRef, protocolLogicRefFactory: ProtocolLogicRefFactory): ScheduledActivity?
fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity?
}
/**
* Interface representing an agreement that exposes various attributes that are common. Implementing it simplifies
* implementation of general protocols that manipulate many agreement types.
* implementation of general flows that manipulate many agreement types.
*/
interface DealState : LinearState {
/** Human readable well known reference (e.g. trade reference) */
@ -293,7 +293,7 @@ interface DealState : LinearState {
/**
* Generate a partial transaction representing an agreement (command) to this deal, allowing a general
* deal/agreement protocol to generate the necessary transaction for potential implementations.
* deal/agreement flow to generate the necessary transaction for potential implementations.
*
* TODO: Currently this is the "inception" transaction but in future an offer of some description might be an input state ref
*

View File

@ -1,4 +1,4 @@
package net.corda.core.protocols
package net.corda.core.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.Party
@ -9,9 +9,9 @@ import org.slf4j.Logger
import rx.Observable
/**
* A sub-class of [ProtocolLogic<T>] implements a protocol flow using direct, straight line blocking code. Thus you
* can write complex protocol logic in an ordinary fashion, without having to think about callbacks, restarting after
* a node crash, how many instances of your protocol there are running and so on.
* A sub-class of [FlowLogic<T>] implements a flow using direct, straight line blocking code. Thus you
* can write complex flow logic in an ordinary fashion, without having to think about callbacks, restarting after
* a node crash, how many instances of your flow there are running and so on.
*
* Invoking the network will cause the call stack to be suspended onto the heap and then serialized to a database using
* the Quasar fibers framework. Because of this, if you need access to data that might change over time, you should
@ -19,32 +19,32 @@ import rx.Observable
* service across calls to send/receive/sendAndReceive because the world might change in arbitrary ways out from
* underneath you, for instance, if the node is restarted or reconfigured!
*
* Additionally, be aware of what data you pin either via the stack or in your [ProtocolLogic] implementation. Very large
* Additionally, be aware of what data you pin either via the stack or in your [FlowLogic] implementation. Very large
* objects or datasets will hurt performance by increasing the amount of data stored in each checkpoint.
*
* If you'd like to use another ProtocolLogic class as a component of your own, construct it on the fly and then pass
* it to the [subProtocol] method. It will return the result of that protocol when it completes.
* If you'd like to use another FlowLogic class as a component of your own, construct it on the fly and then pass
* it to the [subFlow] method. It will return the result of that flow when it completes.
*/
abstract class ProtocolLogic<out T> {
abstract class FlowLogic<out T> {
/** Reference to the [Fiber] instance that is the top level controller for the entire flow. */
lateinit var psm: ProtocolStateMachine<*>
lateinit var fsm: FlowStateMachine<*>
/** This is where you should log things to. */
val logger: Logger get() = psm.logger
val logger: Logger get() = fsm.logger
/**
* Provides access to big, heavy classes that may be reconstructed from time to time, e.g. across restarts. It is
* only available once the protocol has started, which means it cannnot be accessed in the constructor. Either
* only available once the flow has started, which means it cannnot be accessed in the constructor. Either
* access this lazily or from inside [call].
*/
val serviceHub: ServiceHub get() = psm.serviceHub
val serviceHub: ServiceHub get() = fsm.serviceHub
private var sessionProtocol: ProtocolLogic<*> = this
private var sessionFlow: FlowLogic<*> = this
/**
* Return the marker [Class] which [party] has used to register the counterparty protocol that is to execute on the
* other side. The default implementation returns the class object of this ProtocolLogic, but any [Class] instance
* Return the marker [Class] which [party] has used to register the counterparty flow that is to execute on the
* other side. The default implementation returns the class object of this FlowLogic, but any [Class] instance
* will do as long as the other side registers with it.
*/
open fun getCounterpartyMarker(party: Party): Class<*> = javaClass
@ -56,44 +56,44 @@ abstract class ProtocolLogic<out T> {
@Suspendable
fun <T : Any> sendAndReceive(otherParty: Party, payload: Any, receiveType: Class<T>): UntrustworthyData<T> {
return psm.sendAndReceive(otherParty, payload, receiveType, sessionProtocol)
return fsm.sendAndReceive(otherParty, payload, receiveType, sessionFlow)
}
inline fun <reified T : Any> receive(otherParty: Party): UntrustworthyData<T> = receive(otherParty, T::class.java)
@Suspendable
fun <T : Any> receive(otherParty: Party, receiveType: Class<T>): UntrustworthyData<T> {
return psm.receive(otherParty, receiveType, sessionProtocol)
return fsm.receive(otherParty, receiveType, sessionFlow)
}
@Suspendable
fun send(otherParty: Party, payload: Any) {
psm.send(otherParty, payload, sessionProtocol)
fsm.send(otherParty, payload, sessionFlow)
}
/**
* Invokes the given subprotocol by simply passing through this [ProtocolLogic]s reference to the
* [ProtocolStateMachine] and then calling the [call] method.
* @param shareParentSessions In certain situations the need arises to use the same sessions the parent protocol has
* already established. However this also prevents the subprotocol from creating new sessions with those parties.
* Invokes the given subflow by simply passing through this [FlowLogic]s reference to the
* [FlowStateMachine] and then calling the [call] method.
* @param shareParentSessions In certain situations the need arises to use the same sessions the parent flow has
* already established. However this also prevents the subflow from creating new sessions with those parties.
* For this reason the default value is false.
*/
// TODO Rethink the default value for shareParentSessions
// TODO shareParentSessions is a bit too low-level and perhaps can be expresed in a better way
@Suspendable
fun <R> subProtocol(subLogic: ProtocolLogic<R>, shareParentSessions: Boolean = false): R {
subLogic.psm = psm
fun <R> subFlow(subLogic: FlowLogic<R>, shareParentSessions: Boolean = false): R {
subLogic.fsm = fsm
maybeWireUpProgressTracking(subLogic)
if (shareParentSessions) {
subLogic.sessionProtocol = this
subLogic.sessionFlow = this
}
val result = subLogic.call()
// It's easy to forget this when writing protocols so we just step it to the DONE state when it completes.
// It's easy to forget this when writing flows so we just step it to the DONE state when it completes.
subLogic.progressTracker?.currentStep = ProgressTracker.DONE
return result
}
private fun maybeWireUpProgressTracking(subLogic: ProtocolLogic<*>) {
private fun maybeWireUpProgressTracking(subLogic: FlowLogic<*>) {
val ours = progressTracker
val theirs = subLogic.progressTracker
@ -108,11 +108,11 @@ abstract class ProtocolLogic<out T> {
/**
* Override this to provide a [ProgressTracker]. If one is provided and stepped, the framework will do something
* helpful with the progress reports. If this protocol is invoked as a sub-protocol of another, then the
* tracker will be made a child of the current step in the parent. If it's null, this protocol doesn't track
* helpful with the progress reports. If this flow is invoked as a sub-flow of another, then the
* tracker will be made a child of the current step in the parent. If it's null, this flow doesn't track
* progress.
*
* Note that this has to return a tracker before the protocol is invoked. You can't change your mind half way
* Note that this has to return a tracker before the flow is invoked. You can't change your mind half way
* through.
*/
open val progressTracker: ProgressTracker? = null

View File

@ -1,13 +1,10 @@
package net.corda.core.protocols
package net.corda.core.flows
import com.google.common.primitives.Primitives
import net.corda.core.contracts.StateRef
import net.corda.core.crypto.SecureHash
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.protocols.TwoPartyDealProtocol
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.time.Duration
import java.util.*
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
@ -15,63 +12,63 @@ import kotlin.reflect.jvm.javaType
import kotlin.reflect.primaryConstructor
/**
* A class for conversion to and from [ProtocolLogic] and [ProtocolLogicRef] instances.
* A class for conversion to and from [FlowLogic] and [FlowLogicRef] instances.
*
* Validation of types is performed on the way in and way out in case this object is passed between JVMs which might have differing
* whitelists.
*
* TODO: Ways to populate whitelist of "blessed" protocols per node/party
* TODO: Ways to populate whitelist of "blessed" flows per node/party
* TODO: Ways to populate argument types whitelist. Per node/party or global?
* TODO: Align with API related logic for passing in ProtocolLogic references (ProtocolRef)
* TODO: Align with API related logic for passing in FlowLogic references (FlowRef)
* TODO: Actual support for AppContext / AttachmentsClassLoader
*/
class ProtocolLogicRefFactory(private val protocolWhitelist: Map<String, Set<String>>) : SingletonSerializeAsToken() {
class FlowLogicRefFactory(private val flowWhitelist: Map<String, Set<String>>) : SingletonSerializeAsToken() {
constructor() : this(mapOf())
// Pending real dependence on AppContext for class loading etc
@Suppress("UNUSED_PARAMETER")
private fun validateProtocolClassName(className: String, appContext: AppContext) {
private fun validateFlowClassName(className: String, appContext: AppContext) {
// TODO: make this specific to the attachments in the [AppContext] by including [SecureHash] in whitelist check
require(protocolWhitelist.containsKey(className)) { "${ProtocolLogic::class.java.simpleName} of ${ProtocolLogicRef::class.java.simpleName} must have type on the whitelist: $className" }
require(flowWhitelist.containsKey(className)) { "${FlowLogic::class.java.simpleName} of ${FlowLogicRef::class.java.simpleName} must have type on the whitelist: $className" }
}
// Pending real dependence on AppContext for class loading etc
@Suppress("UNUSED_PARAMETER")
private fun validateArgClassName(className: String, argClassName: String, appContext: AppContext) {
// TODO: consider more carefully what to whitelist and how to secure protocols
// TODO: consider more carefully what to whitelist and how to secure flows
// For now automatically accept standard java.lang.* and kotlin.* types.
// All other types require manual specification at ProtocolLogicRefFactory construction time.
// All other types require manual specification at FlowLogicRefFactory construction time.
if (argClassName.startsWith("java.lang.") || argClassName.startsWith("kotlin.")) {
return
}
// TODO: make this specific to the attachments in the [AppContext] by including [SecureHash] in whitelist check
require(protocolWhitelist[className]!!.contains(argClassName)) { "Args to $className must have types on the args whitelist: $argClassName, but it has ${protocolWhitelist[className]}" }
require(flowWhitelist[className]!!.contains(argClassName)) { "Args to $className must have types on the args whitelist: $argClassName, but it has ${flowWhitelist[className]}" }
}
/**
* Create a [ProtocolLogicRef] for the Kotlin primary constructor of a named [ProtocolLogic]
* Create a [FlowLogicRef] for the Kotlin primary constructor of a named [FlowLogic]
*/
fun createKotlin(protocolLogicClassName: String, args: Map<String,Any?>, attachments: List<SecureHash> = emptyList()): ProtocolLogicRef {
fun createKotlin(flowLogicClassName: String, args: Map<String, Any?>, attachments: List<SecureHash> = emptyList()): FlowLogicRef {
val context = AppContext(attachments)
validateProtocolClassName(protocolLogicClassName, context)
validateFlowClassName(flowLogicClassName, context)
for(arg in args.values.filterNotNull()) {
validateArgClassName(protocolLogicClassName, arg.javaClass.name, context)
validateArgClassName(flowLogicClassName, arg.javaClass.name, context)
}
val clazz = Class.forName(protocolLogicClassName)
require(ProtocolLogic::class.java.isAssignableFrom(clazz)) { "$protocolLogicClassName is not a ProtocolLogic" }
val clazz = Class.forName(flowLogicClassName)
require(FlowLogic::class.java.isAssignableFrom(clazz)) { "$flowLogicClassName is not a FlowLogic" }
@Suppress("UNCHECKED_CAST")
val logic = clazz as Class<ProtocolLogic<ProtocolLogic<*>>>
val logic = clazz as Class<FlowLogic<FlowLogic<*>>>
return createKotlin(logic, args)
}
/**
* Create a [ProtocolLogicRef] for the Kotlin primary constructor or Java constructor and the given args.
* Create a [FlowLogicRef] for the Kotlin primary constructor or Java constructor and the given args.
*/
fun create(type: Class<out ProtocolLogic<*>>, vararg args: Any?): ProtocolLogicRef {
fun create(type: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef {
val constructor = type.kotlin.primaryConstructor ?: return createJava(type, *args)
if (constructor.parameters.size < args.size) {
throw IllegalProtocolLogicException(type, "due to too many arguments supplied to kotlin primary constructor")
throw IllegalFlowLogicException(type, "due to too many arguments supplied to kotlin primary constructor")
}
// Build map of args from array
val argsMap = args.zip(constructor.parameters).map { Pair(it.second.name!!, it.first) }.toMap()
@ -79,24 +76,24 @@ class ProtocolLogicRefFactory(private val protocolWhitelist: Map<String, Set<Str
}
/**
* Create a [ProtocolLogicRef] by trying to find a Kotlin constructor that matches the given args.
* Create a [FlowLogicRef] by trying to find a Kotlin constructor that matches the given args.
*
* TODO: Rethink language specific naming.
*/
fun createKotlin(type: Class<out ProtocolLogic<*>>, args: Map<String, Any?>): ProtocolLogicRef {
fun createKotlin(type: Class<out FlowLogic<*>>, args: Map<String, Any?>): FlowLogicRef {
// TODO: we need to capture something about the class loader or "application context" into the ref,
// perhaps as some sort of ThreadLocal style object. For now, just create an empty one.
val appContext = AppContext(emptyList())
validateProtocolClassName(type.name, appContext)
validateFlowClassName(type.name, appContext)
// Check we can find a constructor and populate the args to it, but don't call it
createConstructor(appContext, type, args)
return ProtocolLogicRef(type.name, appContext, args)
return FlowLogicRef(type.name, appContext, args)
}
/**
* Create a [ProtocolLogicRef] by trying to find a Java constructor that matches the given args.
* Create a [FlowLogicRef] by trying to find a Java constructor that matches the given args.
*/
private fun createJava(type: Class<out ProtocolLogic<*>>, vararg args: Any?): ProtocolLogicRef {
private fun createJava(type: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef {
// Build map for each
val argsMap = HashMap<String, Any?>(args.size)
var index = 0
@ -104,22 +101,22 @@ class ProtocolLogicRefFactory(private val protocolWhitelist: Map<String, Set<Str
return createKotlin(type, argsMap)
}
fun toProtocolLogic(ref: ProtocolLogicRef): ProtocolLogic<*> {
validateProtocolClassName(ref.protocolLogicClassName, ref.appContext)
val klass = Class.forName(ref.protocolLogicClassName, true, ref.appContext.classLoader).asSubclass(ProtocolLogic::class.java)
fun toFlowLogic(ref: FlowLogicRef): FlowLogic<*> {
validateFlowClassName(ref.flowLogicClassName, ref.appContext)
val klass = Class.forName(ref.flowLogicClassName, true, ref.appContext.classLoader).asSubclass(FlowLogic::class.java)
return createConstructor(ref.appContext, klass, ref.args)()
}
private fun createConstructor(appContext: AppContext, clazz: Class<out ProtocolLogic<*>>, args: Map<String, Any?>): () -> ProtocolLogic<*> {
private fun createConstructor(appContext: AppContext, clazz: Class<out FlowLogic<*>>, args: Map<String, Any?>): () -> FlowLogic<*> {
for (constructor in clazz.kotlin.constructors) {
val params = buildParams(appContext, clazz, constructor, args) ?: continue
// If we get here then we matched every parameter
return { constructor.callBy(params) }
}
throw IllegalProtocolLogicException(clazz, "as could not find matching constructor for: $args")
throw IllegalFlowLogicException(clazz, "as could not find matching constructor for: $args")
}
private fun buildParams(appContext: AppContext, clazz: Class<out ProtocolLogic<*>>, constructor: KFunction<ProtocolLogic<*>>, args: Map<String, Any?>): HashMap<KParameter, Any?>? {
private fun buildParams(appContext: AppContext, clazz: Class<out FlowLogic<*>>, constructor: KFunction<FlowLogic<*>>, args: Map<String, Any?>): HashMap<KParameter, Any?>? {
val params = hashMapOf<KParameter, Any?>()
val usedKeys = hashSetOf<String>()
for (parameter in constructor.parameters) {
@ -164,15 +161,15 @@ class ProtocolLogicRefFactory(private val protocolWhitelist: Map<String, Set<Str
}
}
class IllegalProtocolLogicException(type: Class<*>, msg: String) : IllegalArgumentException("${ProtocolLogicRef::class.java.simpleName} cannot be constructed for ${ProtocolLogic::class.java.simpleName} of type ${type.name} $msg")
class IllegalFlowLogicException(type: Class<*>, msg: String) : IllegalArgumentException("${FlowLogicRef::class.java.simpleName} cannot be constructed for ${FlowLogic::class.java.simpleName} of type ${type.name} $msg")
/**
* A class representing a [ProtocolLogic] instance which would be possible to safely pass out of the contract sandbox.
* A class representing a [FlowLogic] instance which would be possible to safely pass out of the contract sandbox.
*
* Only allows a String reference to the ProtocolLogic class, and only allows restricted argument types as per [ProtocolLogicRefFactory].
* Only allows a String reference to the FlowLogic class, and only allows restricted argument types as per [FlowLogicRefFactory].
*/
// TODO: align this with the existing [ProtocolRef] in the bank-side API (probably replace some of the API classes)
data class ProtocolLogicRef internal constructor(val protocolLogicClassName: String, val appContext: AppContext, val args: Map<String, Any?>)
// TODO: align this with the existing [FlowRef] in the bank-side API (probably replace some of the API classes)
data class FlowLogicRef internal constructor(val flowLogicClassName: String, val appContext: AppContext, val args: Map<String, Any?>)
/**
* This is just some way to track what attachments need to be in the class loader, but may later include some app

View File

@ -1,4 +1,4 @@
package net.corda.core.protocols
package net.corda.core.flows
import co.paralleluniverse.fibers.Suspendable
import com.google.common.util.concurrent.ListenableFuture
@ -19,25 +19,25 @@ data class StateMachineRunId private constructor(val uuid: UUID) {
}
/**
* A ProtocolStateMachine instance is a suspendable fiber that delegates all actual logic to a [ProtocolLogic] instance.
* For any given flow there is only one PSM, even if that protocol invokes subprotocols.
* A FlowStateMachine instance is a suspendable fiber that delegates all actual logic to a [FlowLogic] instance.
* For any given flow there is only one PSM, even if that flow invokes subflows.
*
* These classes are created by the [StateMachineManager] when a new protocol is started at the topmost level. If
* a protocol invokes a sub-protocol, then it will pass along the PSM to the child. The call method of the topmost
* These classes are created by the [StateMachineManager] when a new flow is started at the topmost level. If
* a flow invokes a sub-flow, then it will pass along the PSM to the child. The call method of the topmost
* logic element gets to return the value that the entire state machine resolves to.
*/
interface ProtocolStateMachine<R> {
interface FlowStateMachine<R> {
@Suspendable
fun <T : Any> sendAndReceive(otherParty: Party,
payload: Any,
receiveType: Class<T>,
sessionProtocol: ProtocolLogic<*>): UntrustworthyData<T>
sessionFlow: FlowLogic<*>): UntrustworthyData<T>
@Suspendable
fun <T : Any> receive(otherParty: Party, receiveType: Class<T>, sessionProtocol: ProtocolLogic<*>): UntrustworthyData<T>
fun <T : Any> receive(otherParty: Party, receiveType: Class<T>, sessionFlow: FlowLogic<*>): UntrustworthyData<T>
@Suspendable
fun send(otherParty: Party, payload: Any, sessionProtocol: ProtocolLogic<*>)
fun send(otherParty: Party, payload: Any, sessionFlow: FlowLogic<*>)
val serviceHub: ServiceHub
val logger: Logger
@ -48,4 +48,4 @@ interface ProtocolStateMachine<R> {
val resultFuture: ListenableFuture<R>
}
class ProtocolSessionException(message: String) : Exception(message)
class FlowSessionException(message: String) : Exception(message)

View File

@ -66,7 +66,7 @@ interface MessagingService {
* available via type casting. Once this function returns the message is queued for delivery but not necessarily
* delivered: if the recipients are offline then the message could be queued hours or days later.
*
* There is no way to know if a message has been received. If your protocol requires this, you need the recipient
* There is no way to know if a message has been received. If your flow requires this, you need the recipient
* to send an ACK message back.
*/
fun send(message: Message, target: MessageRecipients)

View File

@ -22,19 +22,19 @@ abstract class CordaPluginRegistry {
open val staticServeDirs: Map<String, String> = emptyMap()
/**
* A Map with an entry for each consumed protocol used by the webAPIs.
* The key of each map entry should contain the ProtocolLogic<T> class name.
* The associated map values are the union of all concrete class names passed to the protocol constructor.
* A Map with an entry for each consumed flow used by the webAPIs.
* The key of each map entry should contain the FlowLogic<T> class name.
* The associated map values are the union of all concrete class names passed to the flow constructor.
* Standard java.lang.* and kotlin.* types do not need to be included explicitly.
* This is used to extend the white listed protocols that can be initiated from the ServiceHub invokeProtocolAsync method.
* This is used to extend the white listed flows that can be initiated from the ServiceHub invokeFlowAsync method.
*/
open val requiredProtocols: Map<String, Set<String>> = emptyMap()
open val requiredFlows: Map<String, Set<String>> = emptyMap()
/**
* List of additional long lived services to be hosted within the node.
* They are expected to have a single parameter constructor that takes a [PluginServiceHub] as input.
* The [PluginServiceHub] will be fully constructed before the plugin service is created and will
* allow access to the protocol factory and protocol initiation entry points there.
* allow access to the flow factory and flow initiation entry points there.
*/
open val servicePlugins: List<Class<*>> = emptyList()

View File

@ -7,7 +7,7 @@ import net.corda.core.node.services.ServiceType
/**
* Information for an advertised service including the service specific identity information.
* The identity can be used in protocols and is distinct from the Node's legalIdentity
* The identity can be used in flows and is distinct from the Node's legalIdentity
*/
data class ServiceEntry(val info: ServiceInfo, val identity: Party)

View File

@ -1,7 +1,7 @@
package net.corda.core.node
import net.corda.core.crypto.Party
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.flows.FlowLogic
import kotlin.reflect.KClass
/**
@ -9,22 +9,22 @@ import kotlin.reflect.KClass
*/
interface PluginServiceHub : ServiceHub {
/**
* Register the protocol factory we wish to use when a initiating party attempts to communicate with us. The
* Register the flow factory we wish to use when a initiating party attempts to communicate with us. The
* registration is done against a marker [KClass] which is sent in the session handshake by the other party. If this
* marker class has been registered then the corresponding factory will be used to create the protocol which will
* marker class has been registered then the corresponding factory will be used to create the flow which will
* communicate with the other side. If there is no mapping then the session attempt is rejected.
* @param markerClass The marker [KClass] present in a session initiation attempt, which is a 1:1 mapping to a [Class]
* using the <pre>::class</pre> construct. Conventionally this is a [ProtocolLogic] subclass, however any class can
* be used, with the default being the class of the initiating protocol. This enables the registration to be of the
* form: registerProtocolInitiator(InitiatorProtocol::class, ::InitiatedProtocol)
* @param protocolFactory The protocol factory generating the initiated protocol.
* using the <pre>::class</pre> construct. Conventionally this is a [FlowLogic] subclass, however any class can
* be used, with the default being the class of the initiating flow. This enables the registration to be of the
* form: registerFlowInitiator(InitiatorFlow::class, ::InitiatedFlow)
* @param flowFactory The flow factory generating the initiated flow.
*/
// TODO: remove dependency on Kotlin relfection (Kotlin KClass -> Java Class).
fun registerProtocolInitiator(markerClass: KClass<*>, protocolFactory: (Party) -> ProtocolLogic<*>)
fun registerFlowInitiator(markerClass: KClass<*>, flowFactory: (Party) -> FlowLogic<*>)
/**
* Return the protocol factory that has been registered with [markerClass], or null if no factory is found.
* Return the flow factory that has been registered with [markerClass], or null if no factory is found.
*/
fun getProtocolFactory(markerClass: Class<*>): ((Party) -> ProtocolLogic<*>)?
fun getFlowFactory(markerClass: Class<*>): ((Party) -> FlowLogic<*>)?
}

View File

@ -3,10 +3,10 @@ package net.corda.core.node
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionResolutionException
import net.corda.core.contracts.TransactionState
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowStateMachine
import net.corda.core.messaging.MessagingService
import net.corda.core.node.services.*
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.protocols.ProtocolStateMachine
import net.corda.core.transactions.SignedTransaction
import java.security.KeyPair
import java.time.Clock
@ -16,7 +16,7 @@ import java.time.Clock
* mocked out. This class is useful to pass to chunks of pluggable code that might have need of many different kinds of
* functionality and you don't want to hard-code which types in the interface.
*
* Any services exposed to protocols (public view) need to implement [SerializeAsToken] or similar to avoid their internal
* Any services exposed to flows (public view) need to implement [SerializeAsToken] or similar to avoid their internal
* state from being serialized in checkpoints.
*/
interface ServiceHub {
@ -49,16 +49,16 @@ interface ServiceHub {
}
/**
* Will check [logicType] and [args] against a whitelist and if acceptable then construct and initiate the protocol.
* Will check [logicType] and [args] against a whitelist and if acceptable then construct and initiate the flow.
*
* @throws IllegalProtocolLogicException or IllegalArgumentException if there are problems with the [logicType] or [args].
* @throws IllegalFlowLogicException or IllegalArgumentException if there are problems with the [logicType] or [args].
*/
fun <T : Any> invokeProtocolAsync(logicType: Class<out ProtocolLogic<T>>, vararg args: Any?): ProtocolStateMachine<T>
fun <T : Any> invokeFlowAsync(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowStateMachine<T>
/**
* Helper property to shorten code for fetching the Node's KeyPair associated with the
* public legalIdentity Party from the key management service.
* Typical use is during signing in protocols and for unit test signing.
* Typical use is during signing in flows and for unit test signing.
*
* TODO: legalIdentity can now be composed of multiple keys, should we return a list of keyPairs here? Right now
* the logic assumes the legal identity has a composite key with only one node
@ -70,7 +70,7 @@ interface ServiceHub {
* public notaryIdentity Party from the key management service. It is assumed that this is only
* used in contexts where the Node knows it is hosting a Notary Service. Otherwise, it will throw
* an IllegalArgumentException.
* Typical use is during signing in protocols and for unit test signing.
* Typical use is during signing in flows and for unit test signing.
*/
val notaryIdentityKey: KeyPair get() = this.keyManagementService.toKeyPair(this.myInfo.notaryIdentity.owningKey.keys)

View File

@ -220,7 +220,7 @@ interface KeyManagementService {
/**
* A sketch of an interface to a simple key/value storage system. Intended for persistence of simple blobs like
* transactions, serialised protocol state machines and so on. Again, this isn't intended to imply lack of SQL or
* transactions, serialised flow state machines and so on. Again, this isn't intended to imply lack of SQL or
* anything like that, this interface is only big enough to support the prototyping work.
*/
interface StorageService {

View File

@ -1,14 +1,14 @@
package net.corda.core.node.services
import net.corda.core.crypto.SecureHash
import net.corda.core.protocols.StateMachineRunId
import net.corda.core.flows.StateMachineRunId
import rx.Observable
data class StateMachineTransactionMapping(val stateMachineRunId: StateMachineRunId, val transactionId: SecureHash)
/**
* This is the interface to storage storing state machine -> recorded tx mappings. Any time a transaction is recorded
* during a protocol run [addMapping] should be called.
* during a flow run [addMapping] should be called.
*/
interface StateMachineRecordedTransactionMappingStorage {
fun addMapping(stateMachineRunId: StateMachineRunId, transactionId: SecureHash)

View File

@ -58,7 +58,7 @@ open class TransactionBuilder(
/**
* Places a [TimestampCommand] in this transaction, removing any existing command if there is one.
* The command requires a signature from the Notary service, which acts as a Timestamp Authority.
* The signature can be obtained using [NotaryProtocol].
* The signature can be obtained using [NotaryFlow].
*
* The window of time in which the final timestamp may lie is defined as [time] +/- [timeTolerance].
* If you want a non-symmetrical time window you must add the command via [addCommand] yourself. The tolerance

View File

@ -69,7 +69,7 @@ class WireTransaction(
/**
* Looks up identities and attachments from storage to generate a [LedgerTransaction]. A transaction is expected to
* have been fully resolved using the resolution protocol by this point.
* have been fully resolved using the resolution flow by this point.
*
* @throws FileNotFoundException if a required attachment was not found in storage.
* @throws TransactionResolutionException if an input points to a transaction not found in storage.

View File

@ -1,4 +1,4 @@
package net.corda.protocols
package net.corda.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.ContractState
@ -8,26 +8,26 @@ import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.Party
import net.corda.core.crypto.signWithECDSA
import net.corda.core.flows.FlowLogic
import net.corda.core.node.recordTransactions
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.UntrustworthyData
import net.corda.protocols.AbstractStateReplacementProtocol.Acceptor
import net.corda.protocols.AbstractStateReplacementProtocol.Instigator
import net.corda.flows.AbstractStateReplacementFlow.Acceptor
import net.corda.flows.AbstractStateReplacementFlow.Instigator
/**
* Abstract protocol to be used for replacing one state with another, for example when changing the notary of a state.
* Abstract flow to be used for replacing one state with another, for example when changing the notary of a state.
* Notably this requires a one to one replacement of states, states cannot be split, merged or issued as part of these
* protocols.
* flows.
*
* The [Instigator] assembles the transaction for state replacement and sends out change proposals to all participants
* ([Acceptor]) of that state. If participants agree to the proposed change, they each sign the transaction.
* Finally, [Instigator] sends the transaction containing all signatures back to each participant so they can record it and
* use the new updated state for future transactions.
*/
abstract class AbstractStateReplacementProtocol<T> {
abstract class AbstractStateReplacementFlow<T> {
interface Proposal<out T> {
val stateRef: StateRef
val modification: T
@ -36,7 +36,7 @@ abstract class AbstractStateReplacementProtocol<T> {
abstract class Instigator<out S : ContractState, T>(val originalState: StateAndRef<S>,
val modification: T,
override val progressTracker: ProgressTracker = tracker()) : ProtocolLogic<StateAndRef<S>>() {
override val progressTracker: ProgressTracker = tracker()) : FlowLogic<StateAndRef<S>>() {
companion object {
object SIGNING : ProgressTracker.Step("Requesting signatures from other parties")
@ -107,12 +107,12 @@ abstract class AbstractStateReplacementProtocol<T> {
@Suspendable
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.WithKey {
progressTracker.currentStep = NOTARY
return subProtocol(NotaryProtocol.Client(stx))
return subFlow(NotaryFlow.Client(stx))
}
}
abstract class Acceptor<T>(val otherSide: Party,
override val progressTracker: ProgressTracker = tracker()) : ProtocolLogic<Unit>() {
override val progressTracker: ProgressTracker = tracker()) : FlowLogic<Unit>() {
companion object {
object VERIFYING : ProgressTracker.Step("Verifying state replacement proposal")
@ -194,7 +194,7 @@ abstract class AbstractStateReplacementProtocol<T> {
@Suspendable
private fun checkDependenciesValid(stx: SignedTransaction) {
subProtocol(ResolveTransactionsProtocol(stx.tx, otherSide))
subFlow(ResolveTransactionsFlow(stx.tx, otherSide))
}
private fun sign(stx: SignedTransaction): DigitalSignature.WithKey {
@ -203,7 +203,7 @@ abstract class AbstractStateReplacementProtocol<T> {
}
}
// TODO: similar classes occur in other places (NotaryProtocol), need to consolidate
// TODO: similar classes occur in other places (NotaryFlow), need to consolidate
data class Result private constructor(val sig: DigitalSignature.WithKey?, val error: StateReplacementRefused?) {
companion object {
fun withError(error: StateReplacementRefused) = Result(null, error)

View File

@ -1,22 +1,22 @@
package net.corda.protocols
package net.corda.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic
import net.corda.core.node.recordTransactions
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.transactions.SignedTransaction
/**
* Notify all involved parties about a transaction, including storing a copy. Normally this would be called via
* [FinalityProtocol].
* [FinalityFlow].
*
* @param notarisedTransaction transaction which has been notarised (if needed) and is ready to notify nodes about.
* @param participants a list of participants involved in the transaction.
* @return a list of participants who were successfully notified of the transaction.
*/
class BroadcastTransactionProtocol(val notarisedTransaction: SignedTransaction,
val participants: Set<Party>) : ProtocolLogic<Unit>() {
class BroadcastTransactionFlow(val notarisedTransaction: SignedTransaction,
val participants: Set<Party>) : FlowLogic<Unit>() {
data class NotifyTxRequest(val tx: SignedTransaction)

View File

@ -1,4 +1,4 @@
package net.corda.protocols
package net.corda.flows
import net.corda.core.contracts.Attachment
import net.corda.core.crypto.Party
@ -11,8 +11,8 @@ import java.io.InputStream
* Given a set of hashes either loads from from local storage or requests them from the other peer. Downloaded
* attachments are saved to local storage automatically.
*/
class FetchAttachmentsProtocol(requests: Set<SecureHash>,
otherSide: Party) : FetchDataProtocol<Attachment, ByteArray>(requests, otherSide) {
class FetchAttachmentsFlow(requests: Set<SecureHash>,
otherSide: Party) : FetchDataFlow<Attachment, ByteArray>(requests, otherSide) {
override fun load(txid: SecureHash): Attachment? = serviceHub.storageService.attachments.openAttachment(txid)

View File

@ -1,17 +1,17 @@
package net.corda.protocols
package net.corda.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.NamedByHash
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.flows.FlowLogic
import net.corda.core.utilities.UntrustworthyData
import net.corda.protocols.FetchDataProtocol.DownloadedVsRequestedDataMismatch
import net.corda.protocols.FetchDataProtocol.HashNotFound
import net.corda.flows.FetchDataFlow.DownloadedVsRequestedDataMismatch
import net.corda.flows.FetchDataFlow.HashNotFound
import java.util.*
/**
* An abstract protocol for fetching typed data from a remote peer.
* An abstract flow for fetching typed data from a remote peer.
*
* Given a set of hashes (IDs), either loads them from local disk or asks the remote peer to provide them.
*
@ -26,9 +26,9 @@ import java.util.*
* @param T The ultimate type of the data being fetched.
* @param W The wire type of the data being fetched, for when it isn't the same as the ultimate type.
*/
abstract class FetchDataProtocol<T : NamedByHash, in W : Any>(
abstract class FetchDataFlow<T : NamedByHash, in W : Any>(
protected val requests: Set<SecureHash>,
protected val otherSide: Party) : ProtocolLogic<FetchDataProtocol.Result<T>>() {
protected val otherSide: Party) : FlowLogic<FetchDataFlow.Result<T>>() {
open class BadAnswer : Exception()
class HashNotFound(val requested: SecureHash) : BadAnswer()
@ -88,7 +88,7 @@ abstract class FetchDataProtocol<T : NamedByHash, in W : Any>(
}
val answers = response.requireNoNulls().map { convert(it) }
// Check transactions actually hash to what we requested, if this fails the remote node
// is a malicious protocol violator or buggy.
// is a malicious flow violator or buggy.
for ((index, item) in answers.withIndex())
if (item.id != requests[index])
throw DownloadedVsRequestedDataMismatch(requests[index], item.id)

View File

@ -1,4 +1,4 @@
package net.corda.protocols
package net.corda.flows
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
@ -8,12 +8,12 @@ import net.corda.core.transactions.SignedTransaction
* Given a set of tx hashes (IDs), either loads them from local disk or asks the remote peer to provide them.
*
* A malicious response in which the data provided by the remote peer does not hash to the requested hash results in
* [FetchDataProtocol.DownloadedVsRequestedDataMismatch] being thrown. If the remote peer doesn't have an entry, it
* results in a [FetchDataProtocol.HashNotFound] exception. Note that returned transactions are not inserted into
* [FetchDataFlow.DownloadedVsRequestedDataMismatch] being thrown. If the remote peer doesn't have an entry, it
* results in a [FetchDataFlow.HashNotFound] exception. Note that returned transactions are not inserted into
* the database, because it's up to the caller to actually verify the transactions are valid.
*/
class FetchTransactionsProtocol(requests: Set<SecureHash>, otherSide: Party) :
FetchDataProtocol<SignedTransaction, SignedTransaction>(requests, otherSide) {
class FetchTransactionsFlow(requests: Set<SecureHash>, otherSide: Party) :
FetchDataFlow<SignedTransaction, SignedTransaction>(requests, otherSide) {
override fun load(txid: SecureHash): SignedTransaction? = serviceHub.storageService.validatedTransactions.getTransaction(txid)
}

View File

@ -1,8 +1,8 @@
package net.corda.protocols
package net.corda.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.Party
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.flows.FlowLogic
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker
@ -14,9 +14,9 @@ import net.corda.core.utilities.ProgressTracker
* @param participants a list of participants involved in the transaction.
* @return a list of participants who were successfully notified of the transaction.
*/
class FinalityProtocol(val transaction: SignedTransaction,
class FinalityFlow(val transaction: SignedTransaction,
val participants: Set<Party>,
override val progressTracker: ProgressTracker = tracker()): ProtocolLogic<Unit>() {
override val progressTracker: ProgressTracker = tracker()) : FlowLogic<Unit>() {
companion object {
object NOTARISING : ProgressTracker.Step("Requesting signature by notary service")
object BROADCASTING : ProgressTracker.Step("Broadcasting transaction to participants")
@ -31,7 +31,7 @@ class FinalityProtocol(val transaction: SignedTransaction,
progressTracker.currentStep = NOTARISING
// Notarise the transaction if needed
val notarisedTransaction = if (needsNotarySignature(transaction)) {
val notarySig = subProtocol(NotaryProtocol.Client(transaction))
val notarySig = subFlow(NotaryFlow.Client(transaction))
transaction.withAdditionalSignature(notarySig)
} else {
transaction
@ -39,7 +39,7 @@ class FinalityProtocol(val transaction: SignedTransaction,
// Let everyone else know about the transaction
progressTracker.currentStep = BROADCASTING
subProtocol(BroadcastTransactionProtocol(notarisedTransaction, participants))
subFlow(BroadcastTransactionFlow(notarisedTransaction, participants))
}
private fun needsNotarySignature(stx: SignedTransaction) = stx.tx.notary != null && hasNoNotarySignature(stx)

View File

@ -1,4 +1,4 @@
package net.corda.protocols
package net.corda.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.ContractState
@ -10,11 +10,11 @@ import net.corda.core.crypto.Party
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.UntrustworthyData
import net.corda.protocols.NotaryChangeProtocol.Acceptor
import net.corda.protocols.NotaryChangeProtocol.Instigator
import net.corda.flows.NotaryChangeFlow.Acceptor
import net.corda.flows.NotaryChangeFlow.Instigator
/**
* A protocol to be used for changing a state's Notary. This is required since all input states to a transaction
* A flow to be used for changing a state's Notary. This is required since all input states to a transaction
* must point to the same notary.
*
* The [Instigator] assembles the transaction for notary replacement and sends out change proposals to all participants
@ -22,18 +22,18 @@ import net.corda.protocols.NotaryChangeProtocol.Instigator
* Finally, [Instigator] sends the transaction containing all signatures back to each participant so they can record it and
* use the new updated state for future transactions.
*/
object NotaryChangeProtocol: AbstractStateReplacementProtocol<Party>() {
object NotaryChangeFlow : AbstractStateReplacementFlow<Party>() {
data class Proposal(override val stateRef: StateRef,
override val modification: Party,
override val stx: SignedTransaction) : AbstractStateReplacementProtocol.Proposal<Party>
override val stx: SignedTransaction) : AbstractStateReplacementFlow.Proposal<Party>
class Instigator<T : ContractState>(originalState: StateAndRef<T>,
newNotary: Party,
progressTracker: ProgressTracker = tracker())
: AbstractStateReplacementProtocol.Instigator<T, Party>(originalState, newNotary, progressTracker) {
: AbstractStateReplacementFlow.Instigator<T, Party>(originalState, newNotary, progressTracker) {
override fun assembleProposal(stateRef: StateRef, modification: Party, stx: SignedTransaction): AbstractStateReplacementProtocol.Proposal<Party>
override fun assembleProposal(stateRef: StateRef, modification: Party, stx: SignedTransaction): AbstractStateReplacementFlow.Proposal<Party>
= Proposal(stateRef, modification, stx)
override fun assembleTx(): Pair<SignedTransaction, List<CompositeKey>> {
@ -51,7 +51,7 @@ object NotaryChangeProtocol: AbstractStateReplacementProtocol<Party>() {
class Acceptor(otherSide: Party,
override val progressTracker: ProgressTracker = tracker())
: AbstractStateReplacementProtocol.Acceptor<Party>(otherSide) {
: AbstractStateReplacementFlow.Acceptor<Party>(otherSide) {
/**
* Check the notary change proposal.
@ -61,7 +61,7 @@ object NotaryChangeProtocol: AbstractStateReplacementProtocol<Party>() {
* TODO: In more difficult cases this should call for human attention to manually verify and approve the proposal
*/
@Suspendable
override fun verifyProposal(maybeProposal: UntrustworthyData<AbstractStateReplacementProtocol.Proposal<Party>>): AbstractStateReplacementProtocol.Proposal<Party> {
override fun verifyProposal(maybeProposal: UntrustworthyData<AbstractStateReplacementFlow.Proposal<Party>>): AbstractStateReplacementFlow.Proposal<Party> {
return maybeProposal.unwrap { proposal ->
val newNotary = proposal.modification
val isNotary = serviceHub.networkMapCache.notaryNodes.any { it.notaryIdentity == newNotary }

View File

@ -1,28 +1,28 @@
package net.corda.protocols
package net.corda.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.*
import net.corda.core.flows.FlowLogic
import net.corda.core.node.services.TimestampChecker
import net.corda.core.node.services.UniquenessException
import net.corda.core.node.services.UniquenessProvider
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.serialization.serialize
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.UntrustworthyData
object NotaryProtocol {
object NotaryFlow {
/**
* A protocol to be used for obtaining a signature from a [NotaryService] ascertaining the transaction
* A flow to be used for obtaining a signature from a [NotaryService] ascertaining the transaction
* timestamp is correct and none of its inputs have been used in another completed transaction.
*
* @throws NotaryException in case the any of the inputs to the transaction have been consumed
* by another transaction or the timestamp is invalid.
*/
open class Client(private val stx: SignedTransaction,
override val progressTracker: ProgressTracker = Client.tracker()) : ProtocolLogic<DigitalSignature.WithKey>() {
override val progressTracker: ProgressTracker = Client.tracker()) : FlowLogic<DigitalSignature.WithKey>() {
companion object {
@ -91,7 +91,7 @@ object NotaryProtocol {
*/
open class Service(val otherSide: Party,
val timestampChecker: TimestampChecker,
val uniquenessProvider: UniquenessProvider) : ProtocolLogic<Unit>() {
val uniquenessProvider: UniquenessProvider) : FlowLogic<Unit>() {
@Suspendable
override fun call() {

View File

@ -1,22 +1,22 @@
package net.corda.protocols
package net.corda.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.checkedAdd
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic
import net.corda.core.node.recordTransactions
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import java.util.*
// TODO: This code is currently unit tested by TwoPartyTradeProtocolTests, it should have its own tests.
// TODO: This code is currently unit tested by TwoPartyTradeFlowTests, it should have its own tests.
// TODO: It may be a clearer API if we make the primary c'tor private here, and only allow a single tx to be "resolved".
/**
* This protocol is used to verify the validity of a transaction by recursively checking the validity of all the
* This flow is used to verify the validity of a transaction by recursively checking the validity of all the
* dependencies. Once a transaction is checked it's inserted into local storage so it can be relayed and won't be
* checked again.
*
@ -24,12 +24,12 @@ import java.util.*
* transaction are resolved and then the transaction itself is verified. Again, if successful, the results are inserted
* into the database as long as a [SignedTransaction] was provided. If only the [WireTransaction] form was provided
* then this isn't enough to put into the local database, so only the dependencies are checked and inserted. This way
* to use the protocol is helpful when resolving and verifying a finished but partially signed transaction.
* to use the flow is helpful when resolving and verifying a finished but partially signed transaction.
*
* The protocol returns a list of verified [LedgerTransaction] objects, in a depth-first order.
* The flow returns a list of verified [LedgerTransaction] objects, in a depth-first order.
*/
class ResolveTransactionsProtocol(private val txHashes: Set<SecureHash>,
private val otherSide: Party) : ProtocolLogic<List<LedgerTransaction>>() {
class ResolveTransactionsFlow(private val txHashes: Set<SecureHash>,
private val otherSide: Party) : FlowLogic<List<LedgerTransaction>>() {
companion object {
private fun dependencyIDs(wtx: WireTransaction) = wtx.inputs.map { it.txhash }.toSet()
@ -75,7 +75,7 @@ class ResolveTransactionsProtocol(private val txHashes: Set<SecureHash>,
private var wtx: WireTransaction? = null
// TODO: Figure out a more appropriate DOS limit here, 5000 is simply a very bad guess.
/** The maximum number of transactions this protocol will try to download before bailing out. */
/** The maximum number of transactions this flow will try to download before bailing out. */
var transactionCountLimit = 5000
/**
@ -110,7 +110,7 @@ class ResolveTransactionsProtocol(private val txHashes: Set<SecureHash>,
result += ltx
}
// If this protocol is resolving a specific transaction, make sure we have its attachments and then verify
// If this flow is resolving a specific transaction, make sure we have its attachments and then verify
// it as well, but don't insert to the database. Note that when we were given a SignedTransaction (stx != null)
// we *could* insert, because successful verification implies we have everything we need here, and it might
// be a clearer API if we do that. But for consistency with the other c'tor we currently do not.
@ -134,7 +134,7 @@ class ResolveTransactionsProtocol(private val txHashes: Set<SecureHash>,
//
// TODO: This approach has two problems. Analyze and resolve them:
//
// (1) This protocol leaks private data. If you download a transaction and then do NOT request a
// (1) This flow leaks private data. If you download a transaction and then do NOT request a
// dependency, it means you already have it, which in turn means you must have been involved with it before
// somehow, either in the tx itself or in any following spend of it. If there were no following spends, then
// your peer knows for sure that you were involved ... this is bad! The only obvious ways to fix this are
@ -162,7 +162,7 @@ class ResolveTransactionsProtocol(private val txHashes: Set<SecureHash>,
break
// Request the standalone transaction data (which may refer to things we don't yet have).
val downloads: List<SignedTransaction> = subProtocol(FetchTransactionsProtocol(notAlreadyFetched, otherSide)).downloaded
val downloads: List<SignedTransaction> = subFlow(FetchTransactionsFlow(notAlreadyFetched, otherSide)).downloaded
fetchMissingAttachments(downloads.map { it.tx })
@ -192,6 +192,6 @@ class ResolveTransactionsProtocol(private val txHashes: Set<SecureHash>,
wtx.attachments.filter { serviceHub.storageService.attachments.openAttachment(it) == null }
}
if (missingAttachments.isNotEmpty())
subProtocol(FetchAttachmentsProtocol(missingAttachments.toSet(), otherSide))
subFlow(FetchAttachmentsFlow(missingAttachments.toSet(), otherSide))
}
}

View File

@ -1,4 +1,4 @@
package net.corda.protocols
package net.corda.flows
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.messaging.MessagingService

View File

@ -1,14 +1,14 @@
package net.corda.protocols
package net.corda.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.DealState
import net.corda.core.contracts.StateRef
import net.corda.core.crypto.*
import net.corda.core.flows.FlowLogic
import net.corda.core.node.NodeInfo
import net.corda.core.node.recordTransactions
import net.corda.core.node.services.ServiceType
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
@ -21,14 +21,14 @@ import java.security.KeyPair
/**
* Classes for manipulating a two party deal or agreement.
*
* TODO: The subclasses should probably be broken out into individual protocols rather than making this an ever expanding collection of subclasses.
* TODO: The subclasses should probably be broken out into individual flows rather than making this an ever expanding collection of subclasses.
*
* TODO: Also, the term Deal is used here where we might prefer Agreement.
*
* TODO: Consider whether we can merge this with [TwoPartyTradeProtocol]
* TODO: Consider whether we can merge this with [TwoPartyTradeFlow]
*
*/
object TwoPartyDealProtocol {
object TwoPartyDealFlow {
class DealMismatchException(val expectedDeal: ContractState, val actualDeal: ContractState) : Exception() {
override fun toString() = "The submitted deal didn't match the expected: $expectedDeal vs $actualDeal"
@ -38,7 +38,7 @@ object TwoPartyDealProtocol {
override fun toString() = "The submitted deal didn't match the expected: $expectedDeal vs $actualDeal"
}
// This object is serialised to the network and is the first protocol message the seller sends to the buyer.
// This object is serialised to the network and is the first flow message the seller sends to the buyer.
data class Handshake<out T>(val payload: T, val publicKey: CompositeKey)
class SignaturesFromPrimary(val sellerSig: DigitalSignature.WithKey, val notarySig: DigitalSignature.WithKey)
@ -47,15 +47,15 @@ object TwoPartyDealProtocol {
* [Primary] at the end sends the signed tx to all the regulator parties. This a seperate workflow which needs a
* sepearate session with the regulator. This interface is used to do that in [Primary.getCounterpartyMarker].
*/
interface MarkerForBogusRegulatorProtocol
interface MarkerForBogusRegulatorFlow
/**
* Abstracted bilateral deal protocol participant that initiates communication/handshake.
* Abstracted bilateral deal flow participant that initiates communication/handshake.
*
* There's a good chance we can push at least some of this logic down into core protocol logic
* There's a good chance we can push at least some of this logic down into core flow logic
* and helper methods etc.
*/
abstract class Primary(override val progressTracker: ProgressTracker = Primary.tracker()) : ProtocolLogic<SignedTransaction>() {
abstract class Primary(override val progressTracker: ProgressTracker = Primary.tracker()) : FlowLogic<SignedTransaction>() {
companion object {
object AWAITING_PROPOSAL : ProgressTracker.Step("Handshaking and awaiting transaction proposal")
@ -76,7 +76,7 @@ object TwoPartyDealProtocol {
override fun getCounterpartyMarker(party: Party): Class<*> {
return if (serviceHub.networkMapCache.regulators.any { it.legalIdentity == party }) {
MarkerForBogusRegulatorProtocol::class.java
MarkerForBogusRegulatorFlow::class.java
} else {
super.getCounterpartyMarker(party)
}
@ -86,7 +86,7 @@ object TwoPartyDealProtocol {
fun getPartialTransaction(): UntrustworthyData<SignedTransaction> {
progressTracker.currentStep = AWAITING_PROPOSAL
// Make the first message we'll send to kick off the protocol.
// Make the first message we'll send to kick off the flow.
val hello = Handshake(payload, myKeyPair.public.composite)
val maybeSTX = sendAndReceive<SignedTransaction>(otherParty, hello)
@ -117,7 +117,7 @@ object TwoPartyDealProtocol {
// once we implement state pairing.
//
// but the goal of this code is not to be fully secure (yet), but rather, just to find good ways to
// express protocol state machines on top of the messaging layer.
// express flow state machines on top of the messaging layer.
return stx
}
@ -128,7 +128,7 @@ object TwoPartyDealProtocol {
// Download and check all the transactions that this transaction depends on, but do not check this
// transaction itself.
val dependencyTxIDs = stx.tx.inputs.map { it.txhash }.toSet()
subProtocol(ResolveTransactionsProtocol(dependencyTxIDs, otherParty))
subFlow(ResolveTransactionsFlow(dependencyTxIDs, otherParty))
}
@Suspendable
@ -162,7 +162,7 @@ object TwoPartyDealProtocol {
@Suspendable
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.WithKey {
progressTracker.currentStep = NOTARY
return subProtocol(NotaryProtocol.Client(stx))
return subFlow(NotaryFlow.Client(stx))
}
open fun computeOurSignature(partialTX: SignedTransaction): DigitalSignature.WithKey {
@ -185,12 +185,12 @@ object TwoPartyDealProtocol {
/**
* Abstracted bilateral deal protocol participant that is recipient of initial communication.
* Abstracted bilateral deal flow participant that is recipient of initial communication.
*
* There's a good chance we can push at least some of this logic down into core protocol logic
* There's a good chance we can push at least some of this logic down into core flow logic
* and helper methods etc.
*/
abstract class Secondary<U>(override val progressTracker: ProgressTracker = Secondary.tracker()) : ProtocolLogic<SignedTransaction>() {
abstract class Secondary<U>(override val progressTracker: ProgressTracker = Secondary.tracker()) : FlowLogic<SignedTransaction>() {
companion object {
object RECEIVING : ProgressTracker.Step("Waiting for deal info")
@ -267,7 +267,7 @@ object TwoPartyDealProtocol {
/**
* One side of the protocol for inserting a pre-agreed deal.
* One side of the flow for inserting a pre-agreed deal.
*/
open class Instigator(override val otherParty: Party,
override val payload: AutoOffer,
@ -279,7 +279,7 @@ object TwoPartyDealProtocol {
}
/**
* One side of the protocol for inserting a pre-agreed deal.
* One side of the flow for inserting a pre-agreed deal.
*/
open class Acceptor(override val otherParty: Party,
override val progressTracker: ProgressTracker = Secondary.tracker()) : Secondary<AutoOffer>() {

View File

@ -1,4 +1,4 @@
package net.corda.protocols
package net.corda.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.TransactionVerificationException
@ -10,15 +10,15 @@ import net.corda.core.transactions.WireTransaction
import java.security.SignatureException
/**
* A notary commit protocol that makes sure a given transaction is valid before committing it. This does mean that the calling
* A notary commit flow that makes sure a given transaction is valid before committing it. This does mean that the calling
* party has to reveal the whole transaction history; however, we avoid complex conflict resolution logic where a party
* has its input states "blocked" by a transaction from another party, and needs to establish whether that transaction was
* indeed valid.
*/
class ValidatingNotaryProtocol(otherSide: Party,
class ValidatingNotaryFlow(otherSide: Party,
timestampChecker: TimestampChecker,
uniquenessProvider: UniquenessProvider) :
NotaryProtocol.Service(otherSide, timestampChecker, uniquenessProvider) {
NotaryFlow.Service(otherSide, timestampChecker, uniquenessProvider) {
@Suspendable
override fun beforeCommit(stx: SignedTransaction, reqIdentity: Party) {
@ -46,6 +46,6 @@ class ValidatingNotaryProtocol(otherSide: Party,
@Suspendable
private fun resolveTransaction(reqIdentity: Party, wtx: WireTransaction) {
subProtocol(ResolveTransactionsProtocol(wtx, reqIdentity))
subFlow(ResolveTransactionsFlow(wtx, reqIdentity))
}
}

View File

@ -1,4 +1,4 @@
package net.corda.core.protocols;
package net.corda.core.flows;
import org.junit.Test;
@ -7,7 +7,7 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class ProtocolLogicRefFromJavaTest {
public class FlowLogicRefFromJavaTest {
private static class ParamType1 {
final int value;
@ -25,9 +25,9 @@ public class ProtocolLogicRefFromJavaTest {
}
}
private static class JavaProtocolLogic extends ProtocolLogic<Void> {
private static class JavaFlowLogic extends FlowLogic<Void> {
public JavaProtocolLogic(ParamType1 A, ParamType2 b) {
public JavaFlowLogic(ParamType1 A, ParamType2 b) {
}
@Override
@ -36,9 +36,9 @@ public class ProtocolLogicRefFromJavaTest {
}
}
private static class JavaNoArgProtocolLogic extends ProtocolLogic<Void> {
private static class JavaNoArgFlowLogic extends FlowLogic<Void> {
public JavaNoArgProtocolLogic() {
public JavaNoArgFlowLogic() {
}
@Override
@ -53,16 +53,16 @@ public class ProtocolLogicRefFromJavaTest {
Set<String> argsList = new HashSet<>();
argsList.add(ParamType1.class.getName());
argsList.add(ParamType2.class.getName());
whiteList.put(JavaProtocolLogic.class.getName(), argsList);
ProtocolLogicRefFactory factory = new ProtocolLogicRefFactory(whiteList);
factory.create(JavaProtocolLogic.class, new ParamType1(1), new ParamType2("Hello Jack"));
whiteList.put(JavaFlowLogic.class.getName(), argsList);
FlowLogicRefFactory factory = new FlowLogicRefFactory(whiteList);
factory.create(JavaFlowLogic.class, new ParamType1(1), new ParamType2("Hello Jack"));
}
@Test
public void testNoArg() {
Map<String, Set<String>> whiteList = new HashMap<>();
whiteList.put(JavaNoArgProtocolLogic.class.getName(), new HashSet<>());
ProtocolLogicRefFactory factory = new ProtocolLogicRefFactory(whiteList);
factory.create(JavaNoArgProtocolLogic.class);
whiteList.put(JavaNoArgFlowLogic.class.getName(), new HashSet<>());
FlowLogicRefFactory factory = new FlowLogicRefFactory(whiteList);
factory.create(JavaNoArgFlowLogic.class);
}
}

View File

@ -1,4 +1,4 @@
package net.corda.core.protocols
package net.corda.core.flows
import com.esotericsoftware.kryo.io.Input
import com.pholser.junit.quickcheck.From
@ -10,12 +10,12 @@ import com.pholser.junit.quickcheck.runner.JUnitQuickcheck
import net.corda.contracts.testing.SignedTransactionGenerator
import net.corda.core.serialization.createKryo
import net.corda.core.serialization.serialize
import net.corda.protocols.BroadcastTransactionProtocol.NotifyTxRequest
import net.corda.flows.BroadcastTransactionFlow.NotifyTxRequest
import org.junit.runner.RunWith
import kotlin.test.assertEquals
@RunWith(JUnitQuickcheck::class)
class BroadcastTransactionProtocolTest {
class BroadcastTransactionFlowTest {
class NotifyTxRequestMessageGenerator : Generator<NotifyTxRequest>(NotifyTxRequest::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus): NotifyTxRequest {

View File

@ -1,17 +1,17 @@
package net.corda.core.protocols
package net.corda.core.flows
import net.corda.core.days
import org.junit.Before
import org.junit.Test
import java.time.Duration
class ProtocolLogicRefTest {
class FlowLogicRefTest {
data class ParamType1(val value: Int)
data class ParamType2(val value: String)
@Suppress("UNUSED_PARAMETER", "unused") // Things are used via reflection.
class KotlinProtocolLogic(A: ParamType1, b: ParamType2) : ProtocolLogic<Unit>() {
class KotlinFlowLogic(A: ParamType1, b: ParamType2) : FlowLogic<Unit>() {
constructor() : this(ParamType1(1), ParamType2("2"))
constructor(C: ParamType2) : this(ParamType1(1), C)
@ -25,71 +25,71 @@ class ProtocolLogicRefTest {
override fun call() = Unit
}
class KotlinNoArgProtocolLogic : ProtocolLogic<Unit>() {
class KotlinNoArgFlowLogic : FlowLogic<Unit>() {
override fun call() = Unit
}
@Suppress("UNUSED_PARAMETER") // We will never use A or b
class NotWhiteListedKotlinProtocolLogic(A: Int, b: String) : ProtocolLogic<Unit>() {
class NotWhiteListedKotlinFlowLogic(A: Int, b: String) : FlowLogic<Unit>() {
override fun call() = Unit
}
lateinit var factory: ProtocolLogicRefFactory
lateinit var factory: FlowLogicRefFactory
@Before
fun setup() {
// We have to allow Java boxed primitives but Kotlin warns we shouldn't be using them
factory = ProtocolLogicRefFactory(mapOf(Pair(KotlinProtocolLogic::class.java.name, setOf(ParamType1::class.java.name, ParamType2::class.java.name)),
Pair(KotlinNoArgProtocolLogic::class.java.name, setOf())))
factory = FlowLogicRefFactory(mapOf(Pair(KotlinFlowLogic::class.java.name, setOf(ParamType1::class.java.name, ParamType2::class.java.name)),
Pair(KotlinNoArgFlowLogic::class.java.name, setOf())))
}
@Test
fun testCreateKotlinNoArg() {
factory.create(KotlinNoArgProtocolLogic::class.java)
factory.create(KotlinNoArgFlowLogic::class.java)
}
@Test
fun testCreateKotlin() {
val args = mapOf(Pair("A", ParamType1(1)), Pair("b", ParamType2("Hello Jack")))
factory.createKotlin(KotlinProtocolLogic::class.java, args)
factory.createKotlin(KotlinFlowLogic::class.java, args)
}
@Test
fun testCreatePrimary() {
factory.create(KotlinProtocolLogic::class.java, ParamType1(1), ParamType2("Hello Jack"))
factory.create(KotlinFlowLogic::class.java, ParamType1(1), ParamType2("Hello Jack"))
}
@Test(expected = IllegalArgumentException::class)
fun testCreateNotWhiteListed() {
factory.create(NotWhiteListedKotlinProtocolLogic::class.java, ParamType1(1), ParamType2("Hello Jack"))
factory.create(NotWhiteListedKotlinFlowLogic::class.java, ParamType1(1), ParamType2("Hello Jack"))
}
@Test
fun testCreateKotlinVoid() {
factory.createKotlin(KotlinProtocolLogic::class.java, emptyMap())
factory.createKotlin(KotlinFlowLogic::class.java, emptyMap())
}
@Test
fun testCreateKotlinNonPrimary() {
val args = mapOf(Pair("C", ParamType2("Hello Jack")))
factory.createKotlin(KotlinProtocolLogic::class.java, args)
factory.createKotlin(KotlinFlowLogic::class.java, args)
}
@Test(expected = IllegalArgumentException::class)
fun testCreateArgNotWhiteListed() {
val args = mapOf(Pair("illegal", 1.days))
factory.createKotlin(KotlinProtocolLogic::class.java, args)
factory.createKotlin(KotlinFlowLogic::class.java, args)
}
@Test
fun testCreateJavaPrimitiveNoRegistrationRequired() {
val args = mapOf(Pair("primitive", "A string"))
factory.createKotlin(KotlinProtocolLogic::class.java, args)
factory.createKotlin(KotlinFlowLogic::class.java, args)
}
@Test
fun testCreateKotlinPrimitiveNoRegistrationRequired() {
val args = mapOf(Pair("kotlinType", 3))
factory.createKotlin(KotlinProtocolLogic::class.java, args)
factory.createKotlin(KotlinFlowLogic::class.java, args)
}
}

View File

@ -1,4 +1,4 @@
package net.corda.core.protocols
package net.corda.core.flows
import net.corda.core.contracts.DummyContract
import net.corda.core.crypto.NullSignature
@ -8,8 +8,8 @@ import net.corda.core.node.recordTransactions
import net.corda.core.serialization.opaque
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.flows.ResolveTransactionsFlow
import net.corda.node.utilities.databaseTransaction
import net.corda.protocols.ResolveTransactionsProtocol
import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.MINI_CORP_PUBKEY
@ -24,7 +24,7 @@ import kotlin.test.assertFailsWith
import kotlin.test.assertNotNull
import kotlin.test.assertNull
class ResolveTransactionsProtocolTest {
class ResolveTransactionsFlowTest {
lateinit var net: MockNetwork
lateinit var a: MockNetwork.MockNode
lateinit var b: MockNetwork.MockNode
@ -48,8 +48,8 @@ class ResolveTransactionsProtocolTest {
@Test
fun `resolve from two hashes`() {
val (stx1, stx2) = makeTransactions()
val p = ResolveTransactionsProtocol(setOf(stx2.id), a.info.legalIdentity)
val future = b.services.startProtocol(p).resultFuture
val p = ResolveTransactionsFlow(setOf(stx2.id), a.info.legalIdentity)
val future = b.services.startFlow(p).resultFuture
net.runNetwork()
val results = future.get()
assertEquals(listOf(stx1.id, stx2.id), results.map { it.id })
@ -62,8 +62,8 @@ class ResolveTransactionsProtocolTest {
@Test
fun `dependency with an error`() {
val stx = makeTransactions(signFirstTX = false).second
val p = ResolveTransactionsProtocol(setOf(stx.id), a.info.legalIdentity)
val future = b.services.startProtocol(p).resultFuture
val p = ResolveTransactionsFlow(setOf(stx.id), a.info.legalIdentity)
val future = b.services.startFlow(p).resultFuture
net.runNetwork()
assertFailsWith(SignatureException::class) {
rootCauseExceptions { future.get() }
@ -73,8 +73,8 @@ class ResolveTransactionsProtocolTest {
@Test
fun `resolve from a signed transaction`() {
val (stx1, stx2) = makeTransactions()
val p = ResolveTransactionsProtocol(stx2, a.info.legalIdentity)
val future = b.services.startProtocol(p).resultFuture
val p = ResolveTransactionsFlow(stx2, a.info.legalIdentity)
val future = b.services.startFlow(p).resultFuture
net.runNetwork()
future.get()
databaseTransaction(b.database) {
@ -99,11 +99,11 @@ class ResolveTransactionsProtocolTest {
}
cursor = stx
}
val p = ResolveTransactionsProtocol(setOf(cursor.id), a.info.legalIdentity)
val p = ResolveTransactionsFlow(setOf(cursor.id), a.info.legalIdentity)
p.transactionCountLimit = 40
val future = b.services.startProtocol(p).resultFuture
val future = b.services.startFlow(p).resultFuture
net.runNetwork()
assertFailsWith<ResolveTransactionsProtocol.ExcessivelyLargeTransactionGraph> {
assertFailsWith<ResolveTransactionsFlow.ExcessivelyLargeTransactionGraph> {
rootCauseExceptions { future.get() }
}
}
@ -128,8 +128,8 @@ class ResolveTransactionsProtocolTest {
a.services.recordTransactions(stx2, stx3)
}
val p = ResolveTransactionsProtocol(setOf(stx3.id), a.info.legalIdentity)
val future = b.services.startProtocol(p).resultFuture
val p = ResolveTransactionsFlow(setOf(stx3.id), a.info.legalIdentity)
val future = b.services.startFlow(p).resultFuture
net.runNetwork()
future.get()
}
@ -138,8 +138,8 @@ class ResolveTransactionsProtocolTest {
fun attachment() {
val id = a.services.storageService.attachments.importAttachment("Some test file".toByteArray().opaque().open())
val stx2 = makeTransactions(withAttachment = id).second
val p = ResolveTransactionsProtocol(stx2, a.info.legalIdentity)
val future = b.services.startProtocol(p).resultFuture
val p = ResolveTransactionsFlow(stx2, a.info.legalIdentity)
val future = b.services.startFlow(p).resultFuture
net.runNetwork()
future.get()
assertNotNull(b.services.storageService.attachments.openAttachment(id))

View File

@ -12,16 +12,16 @@ import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.services.ServiceInfo
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.SignedTransaction
import net.corda.flows.CashCommand
import net.corda.flows.CashFlow
import net.corda.node.driver.driver
import net.corda.node.services.User
import net.corda.node.services.config.FullNodeConfiguration
import net.corda.node.services.config.NodeSSLConfiguration
import net.corda.node.services.messaging.CordaRPCOps
import net.corda.node.services.messaging.startProtocol
import net.corda.node.services.startProtocolPermission
import net.corda.node.services.messaging.startFlow
import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.protocols.CashCommand
import net.corda.protocols.CashProtocol
import org.graphstream.graph.Edge
import org.graphstream.graph.Node
import org.graphstream.graph.implementations.MultiGraph
@ -47,7 +47,7 @@ fun main(args: Array<String>) {
val printOrVisualise = PrintOrVisualise.valueOf(args[0])
val baseDirectory = Paths.get("build/rpc-api-tutorial")
val user = User("user", "password", permissions = setOf(startProtocolPermission<CashProtocol>()))
val user = User("user", "password", permissions = setOf(startFlowPermission<CashFlow>()))
driver(driverDirectory = baseDirectory) {
startNode("Notary", advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type)))
@ -125,14 +125,14 @@ fun generateTransactions(proxy: CordaRPCOps) {
val n = random.nextDouble()
if (ownedQuantity > 10000 && n > 0.8) {
val quantity = Math.abs(random.nextLong()) % 2000
proxy.startProtocol(::CashProtocol, CashCommand.ExitCash(Amount(quantity, USD), issueRef))
proxy.startFlow(::CashFlow, CashCommand.ExitCash(Amount(quantity, USD), issueRef))
ownedQuantity -= quantity
} else if (ownedQuantity > 1000 && n < 0.7) {
val quantity = Math.abs(random.nextLong() % Math.min(ownedQuantity, 2000))
proxy.startProtocol(::CashProtocol, CashCommand.PayCash(Amount(quantity, Issued(meAndRef, USD)), me))
proxy.startFlow(::CashFlow, CashCommand.PayCash(Amount(quantity, Issued(meAndRef, USD)), me))
} else {
val quantity = Math.abs(random.nextLong() % 1000)
proxy.startProtocol(::CashProtocol, CashCommand.IssueCash(Amount(quantity, USD), issueRef, me, notary))
proxy.startFlow(::CashFlow, CashCommand.IssueCash(Amount(quantity, USD), issueRef, me, notary))
ownedQuantity += quantity
}
}

View File

@ -1,4 +1,4 @@
package net.corda.protocols
package net.corda.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.asset.Cash
@ -6,8 +6,8 @@ import net.corda.core.contracts.*
import net.corda.core.crypto.Party
import net.corda.core.crypto.keys
import net.corda.core.crypto.toStringShort
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.protocols.StateMachineRunId
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StateMachineRunId
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
@ -15,14 +15,14 @@ import java.security.KeyPair
import java.util.*
/**
* Initiates a protocol that produces an Issue/Move or Exit Cash transaction.
* Initiates a flow that produces an Issue/Move or Exit Cash transaction.
*
* @param command Indicates what Cash transaction to create with what parameters.
*/
class CashProtocol(val command: CashCommand): ProtocolLogic<CashProtocolResult>() {
class CashFlow(val command: CashCommand) : FlowLogic<CashFlowResult>() {
@Suspendable
override fun call(): CashProtocolResult {
override fun call(): CashFlowResult {
return when (command) {
is CashCommand.IssueCash -> issueCash(command)
is CashCommand.PayCash -> initiatePayment(command)
@ -32,7 +32,7 @@ class CashProtocol(val command: CashCommand): ProtocolLogic<CashProtocolResult>(
// TODO check with the recipient if they want to accept the cash.
@Suspendable
private fun initiatePayment(req: CashCommand.PayCash): CashProtocolResult {
private fun initiatePayment(req: CashCommand.PayCash): CashFlowResult {
val builder: TransactionBuilder = TransactionType.General.Builder(null)
// TODO: Have some way of restricting this to states the caller controls
try {
@ -45,20 +45,20 @@ class CashProtocol(val command: CashCommand): ProtocolLogic<CashProtocolResult>(
}
val tx = spendTX.toSignedTransaction(checkSufficientSignatures = false)
val protocol = FinalityProtocol(tx, setOf(req.recipient))
subProtocol(protocol)
return CashProtocolResult.Success(
psm.id,
val flow = FinalityFlow(tx, setOf(req.recipient))
subFlow(flow)
return CashFlowResult.Success(
fsm.id,
tx,
"Cash payment transaction generated"
)
} catch(ex: InsufficientBalanceException) {
return CashProtocolResult.Failed(ex.message ?: "Insufficient balance")
return CashFlowResult.Failed(ex.message ?: "Insufficient balance")
}
}
@Suspendable
private fun exitCash(req: CashCommand.ExitCash): CashProtocolResult {
private fun exitCash(req: CashCommand.ExitCash): CashFlowResult {
val builder: TransactionBuilder = TransactionType.General.Builder(null)
try {
val issuer = PartyAndReference(serviceHub.myInfo.legalIdentity, req.issueRef)
@ -81,19 +81,19 @@ class CashProtocol(val command: CashCommand): ProtocolLogic<CashProtocolResult>(
// Commit the transaction
val tx = builder.toSignedTransaction(checkSufficientSignatures = false)
subProtocol(FinalityProtocol(tx, participants))
return CashProtocolResult.Success(
psm.id,
subFlow(FinalityFlow(tx, participants))
return CashFlowResult.Success(
fsm.id,
tx,
"Cash destruction transaction generated"
)
} catch (ex: InsufficientBalanceException) {
return CashProtocolResult.Failed(ex.message ?: "Insufficient balance")
return CashFlowResult.Failed(ex.message ?: "Insufficient balance")
}
}
@Suspendable
private fun issueCash(req: CashCommand.IssueCash): CashProtocolResult {
private fun issueCash(req: CashCommand.IssueCash): CashFlowResult {
val builder: TransactionBuilder = TransactionType.General.Builder(notary = null)
val issuer = PartyAndReference(serviceHub.myInfo.legalIdentity, req.issueRef)
Cash().generateIssue(builder, req.amount.issuedBy(issuer), req.recipient.owningKey, req.notary)
@ -101,9 +101,9 @@ class CashProtocol(val command: CashCommand): ProtocolLogic<CashProtocolResult>(
builder.signWith(myKey)
val tx = builder.toSignedTransaction(checkSufficientSignatures = true)
// Issuance transactions do not need to be notarised, so we can skip directly to broadcasting it
subProtocol(BroadcastTransactionProtocol(tx, setOf(req.recipient)))
return CashProtocolResult.Success(
psm.id,
subFlow(BroadcastTransactionFlow(tx, setOf(req.recipient)))
return CashFlowResult.Success(
fsm.id,
tx,
"Cash issuance completed"
)
@ -113,7 +113,7 @@ class CashProtocol(val command: CashCommand): ProtocolLogic<CashProtocolResult>(
}
/**
* A command to initiate the Cash protocol with.
* A command to initiate the Cash flow with.
*/
sealed class CashCommand {
/**
@ -147,11 +147,11 @@ sealed class CashCommand {
class ExitCash(val amount: Amount<Currency>, val issueRef: OpaqueBytes) : CashCommand()
}
sealed class CashProtocolResult {
sealed class CashFlowResult {
/**
* @param transaction the transaction created as a result, in the case where the protocol completed successfully.
* @param transaction the transaction created as a result, in the case where the flow completed successfully.
*/
class Success(val id: StateMachineRunId, val transaction: SignedTransaction?, val message: String?) : CashProtocolResult() {
class Success(val id: StateMachineRunId, val transaction: SignedTransaction?, val message: String?) : CashFlowResult() {
override fun toString() = "Success($message)"
}
@ -159,7 +159,7 @@ sealed class CashProtocolResult {
* State indicating the action undertaken failed, either directly (it is not something which requires a
* state machine), or before a state machine was started.
*/
class Failed(val message: String?) : CashProtocolResult() {
class Failed(val message: String?) : CashFlowResult() {
override fun toString() = "Failed($message)"
}
}

View File

@ -1,11 +1,11 @@
package net.corda.protocols
package net.corda.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.asset.sumCashBy
import net.corda.core.contracts.*
import net.corda.core.crypto.*
import net.corda.core.flows.FlowLogic
import net.corda.core.node.NodeInfo
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
@ -16,7 +16,7 @@ import java.security.KeyPair
import java.util.*
/**
* This asset trading protocol implements a "delivery vs payment" type swap. It has two parties (B and S for buyer
* This asset trading flow implements a "delivery vs payment" type swap. It has two parties (B and S for buyer
* and seller) and the following steps:
*
* 1. S sends the [StateAndRef] pointing to what they want to sell to B, along with info about the price they require
@ -26,28 +26,28 @@ import java.util.*
* it lacks a signature from S authorising movement of the asset.
* 3. S signs it and hands the now finalised SignedWireTransaction back to B.
*
* Assuming no malicious termination, they both end the protocol being in posession of a valid, signed transaction
* Assuming no malicious termination, they both end the flow being in posession of a valid, signed transaction
* that represents an atomic asset swap.
*
* Note that it's the *seller* who initiates contact with the buyer, not vice-versa as you might imagine.
*
* To initiate the protocol, use either the [runBuyer] or [runSeller] methods, depending on which side of the trade
* To initiate the flow, use either the [runBuyer] or [runSeller] methods, depending on which side of the trade
* your node is taking. These methods return a future which will complete once the trade is over and a fully signed
* transaction is available: you can either block your thread waiting for the protocol to complete by using
* transaction is available: you can either block your thread waiting for the flow to complete by using
* [ListenableFuture.get] or more usefully, register a callback that will be invoked when the time comes.
*
* To see an example of how to use this class, look at the unit tests.
*/
// TODO: Common elements in multi-party transaction consensus and signing should be refactored into a superclass of this
// and [AbstractStateReplacementProtocol].
object TwoPartyTradeProtocol {
// and [AbstractStateReplacementFlow].
object TwoPartyTradeFlow {
class UnacceptablePriceException(val givenPrice: Amount<Currency>) : Exception("Unacceptable price: $givenPrice")
class AssetMismatchException(val expectedTypeName: String, val typeName: String) : Exception() {
override fun toString() = "The submitted asset didn't match the expected type: $expectedTypeName vs $typeName"
}
// This object is serialised to the network and is the first protocol message the seller sends to the buyer.
// This object is serialised to the network and is the first flow message the seller sends to the buyer.
data class SellerTradeInfo(
val assetForSale: StateAndRef<OwnableState>,
val price: Amount<Currency>,
@ -62,7 +62,7 @@ object TwoPartyTradeProtocol {
val assetToSell: StateAndRef<OwnableState>,
val price: Amount<Currency>,
val myKeyPair: KeyPair,
override val progressTracker: ProgressTracker = Seller.tracker()) : ProtocolLogic<SignedTransaction>() {
override val progressTracker: ProgressTracker = Seller.tracker()) : FlowLogic<SignedTransaction>() {
companion object {
object AWAITING_PROPOSAL : ProgressTracker.Step("Awaiting transaction proposal")
@ -92,7 +92,7 @@ object TwoPartyTradeProtocol {
@Suspendable
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.WithKey {
progressTracker.currentStep = NOTARY
return subProtocol(NotaryProtocol.Client(stx))
return subFlow(NotaryFlow.Client(stx))
}
@Suspendable
@ -100,7 +100,7 @@ object TwoPartyTradeProtocol {
progressTracker.currentStep = AWAITING_PROPOSAL
val myPublicKey = myKeyPair.public.composite
// Make the first message we'll send to kick off the protocol.
// Make the first message we'll send to kick off the flow.
val hello = SellerTradeInfo(assetToSell, price, myPublicKey)
val maybeSTX = sendAndReceive<SignedTransaction>(otherParty, hello)
@ -116,7 +116,7 @@ object TwoPartyTradeProtocol {
// Download and check all the things that this transaction depends on and verify it is contract-valid,
// even though it is missing signatures.
subProtocol(ResolveTransactionsProtocol(wtx, otherParty))
subFlow(ResolveTransactionsFlow(wtx, otherParty))
if (wtx.outputs.map { it.data }.sumCashBy(myPublicKey).withoutIssuer() != price)
throw IllegalArgumentException("Transaction is not sending us the right amount of cash")
@ -129,7 +129,7 @@ object TwoPartyTradeProtocol {
// once we implement state pairing.
//
// but the goal of this code is not to be fully secure (yet), but rather, just to find good ways to
// express protocol state machines on top of the messaging layer.
// express flow state machines on top of the messaging layer.
return it
}
@ -156,7 +156,7 @@ object TwoPartyTradeProtocol {
open class Buyer(val otherParty: Party,
val notary: Party,
val acceptablePrice: Amount<Currency>,
val typeToBuy: Class<out OwnableState>) : ProtocolLogic<SignedTransaction>() {
val typeToBuy: Class<out OwnableState>) : FlowLogic<SignedTransaction>() {
object RECEIVING : ProgressTracker.Step("Waiting for seller trading info")
@ -207,7 +207,7 @@ object TwoPartyTradeProtocol {
// Check the transaction that contains the state which is being resolved.
// We only have a hash here, so if we don't know it already, we have to ask for it.
subProtocol(ResolveTransactionsProtocol(setOf(it.assetForSale.ref.txhash), otherParty))
subFlow(ResolveTransactionsFlow(setOf(it.assetForSale.ref.txhash), otherParty))
return it
}

View File

@ -14,6 +14,9 @@ import net.corda.core.node.services.ServiceInfo
import net.corda.core.random63BitValue
import net.corda.core.serialization.serialize
import net.corda.core.utilities.LogHelper
import net.corda.flows.NotaryError
import net.corda.flows.NotaryException
import net.corda.flows.NotaryFlow
import net.corda.node.internal.AbstractNode
import net.corda.node.internal.Node
import net.corda.node.services.config.ConfigHelper
@ -21,9 +24,6 @@ import net.corda.node.services.config.FullNodeConfiguration
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.node.utilities.databaseTransaction
import net.corda.protocols.NotaryError
import net.corda.protocols.NotaryException
import net.corda.protocols.NotaryProtocol
import net.corda.testing.freeLocalHostAndPort
import org.junit.After
import org.junit.Before
@ -76,12 +76,12 @@ class DistributedNotaryTests {
tx.toSignedTransaction(false)
}
val buildProtocol = { NotaryProtocol.Client(stx) }
val buildFlow = { NotaryFlow.Client(stx) }
val firstSpend = alice.services.startProtocol(buildProtocol())
val firstSpend = alice.services.startFlow(buildFlow())
firstSpend.resultFuture.get()
val secondSpend = alice.services.startProtocol(buildProtocol())
val secondSpend = alice.services.startFlow(buildFlow())
val ex = assertFailsWith(ExecutionException::class) { secondSpend.resultFuture.get() }
val error = (ex.cause as NotaryException).error as NotaryError.Conflict

View File

@ -91,31 +91,31 @@ interface APIServer {
fun commitTransaction(tx: SerializedBytes<WireTransaction>, signatures: List<DigitalSignature.WithKey>): SecureHash
/**
* This method would not return until the protocol is finished (hence the "Sync").
* This method would not return until the flow is finished (hence the "Sync").
*
* Longer term we'd add an Async version that returns some kind of ProtocolInvocationRef that could be queried and
* Longer term we'd add an Async version that returns some kind of FlowInvocationRef that could be queried and
* would appear on some kind of event message that is broadcast informing of progress.
*
* Will throw exception if protocol fails.
* Will throw exception if flow fails.
*/
fun invokeProtocolSync(type: ProtocolRef, args: Map<String, Any?>): Any?
fun invokeFlowSync(type: FlowRef, args: Map<String, Any?>): Any?
// fun invokeProtocolAsync(type: ProtocolRef, args: Map<String, Any?>): ProtocolInstanceRef
// fun invokeFlowAsync(type: FlowRef, args: Map<String, Any?>): FlowInstanceRef
/**
* Fetch protocols that require a response to some prompt/question by a human (on the "bank" side).
* Fetch flows that require a response to some prompt/question by a human (on the "bank" side).
*/
fun fetchProtocolsRequiringAttention(query: StatesQuery): Map<StateRef, ProtocolRequiringAttention>
fun fetchFlowsRequiringAttention(query: StatesQuery): Map<StateRef, FlowRequiringAttention>
/**
* Provide the response that a protocol is waiting for.
* Provide the response that a flow is waiting for.
*
* @param protocol Should refer to a previously supplied ProtocolRequiringAttention.
* @param stepId Which step of the protocol are we referring too.
* @param choice Should be one of the choices presented in the ProtocolRequiringAttention.
* @param flow Should refer to a previously supplied FlowRequiringAttention.
* @param stepId Which step of the flow are we referring too.
* @param choice Should be one of the choices presented in the FlowRequiringAttention.
* @param args Any arguments required.
*/
fun provideProtocolResponse(protocol: ProtocolInstanceRef, choice: SecureHash, args: Map<String, Any?>)
fun provideFlowResponse(flow: FlowInstanceRef, choice: SecureHash, args: Map<String, Any?>)
}
@ -131,20 +131,20 @@ data class ContractLedgerRef(val hash: SecureHash) : ContractDefRef
/**
* Encapsulates the protocol to be instantiated. e.g. TwoPartyTradeProtocol.Buyer.
* Encapsulates the flow to be instantiated. e.g. TwoPartyTradeFlow.Buyer.
*/
interface ProtocolRef {
interface FlowRef {
}
data class ProtocolClassRef(val className: String) : ProtocolRef
data class FlowClassRef(val className: String) : FlowRef
data class ProtocolInstanceRef(val protocolInstance: SecureHash, val protocolClass: ProtocolClassRef, val protocolStepId: String)
data class FlowInstanceRef(val flowInstance: SecureHash, val flowClass: FlowClassRef, val flowStepId: String)
/**
* Thinking that Instant is OK for short lived protocol deadlines.
* Thinking that Instant is OK for short lived flow deadlines.
*/
data class ProtocolRequiringAttention(val ref: ProtocolInstanceRef, val prompt: String, val choiceIdsToMessages: Map<SecureHash, String>, val dueBy: Instant)
data class FlowRequiringAttention(val ref: FlowInstanceRef, val prompt: String, val choiceIdsToMessages: Map<SecureHash, String>, val dueBy: Instant)
/**

View File

@ -1,7 +1,10 @@
package net.corda.node.internal
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.contracts.*
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.DealState
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.node.services.linearHeadsOfType
@ -64,25 +67,25 @@ class APIServerImpl(val node: AbstractNode) : APIServer {
throw UnsupportedOperationException()
}
override fun invokeProtocolSync(type: ProtocolRef, args: Map<String, Any?>): Any? {
return invokeProtocolAsync(type, args).get()
override fun invokeFlowSync(type: FlowRef, args: Map<String, Any?>): Any? {
return invokeFlowAsync(type, args).get()
}
private fun invokeProtocolAsync(type: ProtocolRef, args: Map<String, Any?>): ListenableFuture<out Any?> {
if (type is ProtocolClassRef) {
val protocolLogicRef = node.services.protocolLogicRefFactory.createKotlin(type.className, args)
val protocolInstance = node.services.protocolLogicRefFactory.toProtocolLogic(protocolLogicRef)
return node.services.startProtocol(protocolInstance).resultFuture
private fun invokeFlowAsync(type: FlowRef, args: Map<String, Any?>): ListenableFuture<out Any?> {
if (type is FlowClassRef) {
val flowLogicRef = node.services.flowLogicRefFactory.createKotlin(type.className, args)
val flowInstance = node.services.flowLogicRefFactory.toFlowLogic(flowLogicRef)
return node.services.startFlow(flowInstance).resultFuture
} else {
throw UnsupportedOperationException("Unsupported ProtocolRef type: $type")
throw UnsupportedOperationException("Unsupported FlowRef type: $type")
}
}
override fun fetchProtocolsRequiringAttention(query: StatesQuery): Map<StateRef, ProtocolRequiringAttention> {
override fun fetchFlowsRequiringAttention(query: StatesQuery): Map<StateRef, FlowRequiringAttention> {
throw UnsupportedOperationException()
}
override fun provideProtocolResponse(protocol: ProtocolInstanceRef, choice: SecureHash, args: Map<String, Any?>) {
override fun provideFlowResponse(flow: FlowInstanceRef, choice: SecureHash, args: Map<String, Any?>) {
throw UnsupportedOperationException()
}

View File

@ -8,17 +8,20 @@ import com.google.common.util.concurrent.SettableFuture
import net.corda.core.*
import net.corda.core.crypto.Party
import net.corda.core.crypto.X509Utilities
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.flows.FlowStateMachine
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.*
import net.corda.core.node.services.*
import net.corda.core.node.services.NetworkMapCache.MapChangeType
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.protocols.ProtocolLogicRefFactory
import net.corda.core.protocols.ProtocolStateMachine
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.transactions.SignedTransaction
import net.corda.flows.CashCommand
import net.corda.flows.CashFlow
import net.corda.flows.sendRequest
import net.corda.node.api.APIServer
import net.corda.node.services.api.*
import net.corda.node.services.config.NodeConfiguration
@ -30,7 +33,7 @@ import net.corda.node.services.keys.PersistentKeyManagementService
import net.corda.node.services.messaging.RPCOps
import net.corda.node.services.network.InMemoryNetworkMapCache
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.network.NetworkMapService.Companion.REGISTER_PROTOCOL_TOPIC
import net.corda.node.services.network.NetworkMapService.Companion.REGISTER_FLOW_TOPIC
import net.corda.node.services.network.NetworkMapService.RegistrationResponse
import net.corda.node.services.network.NodeRegistration
import net.corda.node.services.network.PersistentNetworkMapService
@ -45,9 +48,6 @@ import net.corda.node.utilities.AddOrRemove
import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.configureDatabase
import net.corda.node.utilities.databaseTransaction
import net.corda.protocols.CashCommand
import net.corda.protocols.CashProtocol
import net.corda.protocols.sendRequest
import org.jetbrains.exposed.sql.Database
import org.slf4j.Logger
import java.nio.file.FileAlreadyExistsException
@ -66,7 +66,7 @@ import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair
* I/O), or a mock implementation suitable for unit test environments.
*
* Marked as SingletonSerializeAsToken to prevent the invisible reference to AbstractNode in the ServiceHub accidentally
* sweeping up the Node into the Kryo checkpoint serialization via any protocols holding a reference to ServiceHub.
* sweeping up the Node into the Kryo checkpoint serialization via any flows holding a reference to ServiceHub.
*/
// TODO: Where this node is the initial network map service, currently no networkMapService is provided.
// In theory the NodeInfo for the node should be passed in, instead, however currently this is constructed by the
@ -95,7 +95,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val netwo
protected val _servicesThatAcceptUploads = ArrayList<AcceptsFileUpload>()
val servicesThatAcceptUploads: List<AcceptsFileUpload> = _servicesThatAcceptUploads
private val protocolFactories = ConcurrentHashMap<Class<*>, (Party) -> ProtocolLogic<*>>()
private val flowFactories = ConcurrentHashMap<Class<*>, (Party) -> FlowLogic<*>>()
protected val partyKeys = mutableSetOf<KeyPair>()
val services = object : ServiceHubInternal() {
@ -112,18 +112,18 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val netwo
// Internal only
override val monitoringService: MonitoringService = MonitoringService(MetricRegistry())
override val protocolLogicRefFactory: ProtocolLogicRefFactory get() = protocolLogicFactory
override val flowLogicRefFactory: FlowLogicRefFactory get() = flowLogicFactory
override fun <T> startProtocol(logic: ProtocolLogic<T>): ProtocolStateMachine<T> = smm.add(logic)
override fun <T> startFlow(logic: FlowLogic<T>): FlowStateMachine<T> = smm.add(logic)
override fun registerProtocolInitiator(markerClass: KClass<*>, protocolFactory: (Party) -> ProtocolLogic<*>) {
require(markerClass !in protocolFactories) { "${markerClass.java.name} has already been used to register a protocol" }
log.info("Registering protocol ${markerClass.java.name}")
protocolFactories[markerClass.java] = protocolFactory
override fun registerFlowInitiator(markerClass: KClass<*>, flowFactory: (Party) -> FlowLogic<*>) {
require(markerClass !in flowFactories) { "${markerClass.java.name} has already been used to register a flow" }
log.info("Registering flow ${markerClass.java.name}")
flowFactories[markerClass.java] = flowFactory
}
override fun getProtocolFactory(markerClass: Class<*>): ((Party) -> ProtocolLogic<*>)? {
return protocolFactories[markerClass]
override fun getFlowFactory(markerClass: Class<*>): ((Party) -> FlowLogic<*>)? {
return flowFactories[markerClass]
}
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
@ -149,7 +149,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val netwo
lateinit var netMapCache: NetworkMapCache
lateinit var api: APIServer
lateinit var scheduler: NodeSchedulerService
lateinit var protocolLogicFactory: ProtocolLogicRefFactory
lateinit var flowLogicFactory: FlowLogicRefFactory
lateinit var schemas: SchemaService
val customServices: ArrayList<Any> = ArrayList()
protected val runOnStop: ArrayList<Runnable> = ArrayList()
@ -204,8 +204,8 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val netwo
// the identity key. But the infrastructure to make that easy isn't here yet.
keyManagement = makeKeyManagementService()
api = APIServerImpl(this@AbstractNode)
protocolLogicFactory = initialiseProtocolLogicFactory()
scheduler = NodeSchedulerService(database, services, protocolLogicFactory)
flowLogicFactory = initialiseFlowLogicFactory()
scheduler = NodeSchedulerService(database, services, flowLogicFactory)
val tokenizableServices = mutableListOf(storage, net, vault, keyManagement, identity, platformClock, scheduler)
@ -309,31 +309,32 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val netwo
}
}
private val defaultProtocolWhiteList: Map<Class<out ProtocolLogic<*>>, Set<Class<*>>> = mapOf(
CashProtocol::class.java to setOf(
private val defaultFlowWhiteList: Map<Class<out FlowLogic<*>>, Set<Class<*>>> = mapOf(
CashFlow::class.java to setOf(
CashCommand.IssueCash::class.java,
CashCommand.PayCash::class.java,
CashCommand.ExitCash::class.java
)
)
private fun initialiseProtocolLogicFactory(): ProtocolLogicRefFactory {
val protocolWhitelist = HashMap<String, Set<String>>()
for ((protocolClass, extraArgumentTypes) in defaultProtocolWhiteList) {
private fun initialiseFlowLogicFactory(): FlowLogicRefFactory {
val flowWhitelist = HashMap<String, Set<String>>()
for ((flowClass, extraArgumentTypes) in defaultFlowWhiteList) {
val argumentWhitelistClassNames = HashSet(extraArgumentTypes.map { it.name })
protocolClass.constructors.forEach {
flowClass.constructors.forEach {
it.parameters.mapTo(argumentWhitelistClassNames) { it.type.name }
}
protocolWhitelist.merge(protocolClass.name, argumentWhitelistClassNames, { x, y -> x + y })
flowWhitelist.merge(flowClass.name, argumentWhitelistClassNames, { x, y -> x + y })
}
for (plugin in pluginRegistries) {
for ((className, classWhitelist) in plugin.requiredProtocols) {
protocolWhitelist.merge(className, classWhitelist, { x, y -> x + y })
for ((className, classWhitelist) in plugin.requiredFlows) {
flowWhitelist.merge(className, classWhitelist, { x, y -> x + y })
}
}
return ProtocolLogicRefFactory(protocolWhitelist)
return FlowLogicRefFactory(flowWhitelist)
}
private fun buildPluginServices(tokenizableServices: MutableList<Any>): List<Any> {
@ -406,7 +407,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val netwo
val reg = NodeRegistration(info, instant.toEpochMilli(), type, expires)
val legalIdentityKey = obtainLegalIdentityKey()
val request = NetworkMapService.RegistrationRequest(reg.toWire(legalIdentityKey.private), net.myAddress)
return net.sendRequest(REGISTER_PROTOCOL_TOPIC, request, networkMapAddr)
return net.sendRequest(REGISTER_FLOW_TOPIC, request, networkMapAddr)
}
protected open fun makeKeyManagementService(): KeyManagementService = PersistentKeyManagementService(partyKeys)

View File

@ -3,20 +3,18 @@ package net.corda.node.internal
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.keys
import net.corda.core.crypto.toStringShort
import net.corda.core.flows.FlowLogic
import net.corda.core.node.NodeInfo
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.StateMachineTransactionMapping
import net.corda.core.node.services.Vault
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.toObservable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker
import net.corda.node.services.messaging.*
import net.corda.node.services.startProtocolPermission
import net.corda.node.services.statemachine.ProtocolStateMachineImpl
import net.corda.node.services.startFlowPermission
import net.corda.node.services.statemachine.FlowStateMachineImpl
import net.corda.node.services.statemachine.StateMachineManager
import net.corda.node.utilities.databaseTransaction
import org.jetbrains.exposed.sql.Database
@ -52,7 +50,7 @@ class CordaRPCOpsImpl(
override fun stateMachinesAndUpdates(): Pair<List<StateMachineInfo>, Observable<StateMachineUpdate>> {
val (allStateMachines, changes) = smm.track()
return Pair(
allStateMachines.map { StateMachineInfo.fromProtocolStateMachineImpl(it) },
allStateMachines.map { StateMachineInfo.fromFlowStateMachineImpl(it) },
changes.map { StateMachineUpdate.fromStateMachineChange(it) }
)
}
@ -79,11 +77,11 @@ class CordaRPCOpsImpl(
}
}
// TODO: Check that this protocol is annotated as being intended for RPC invocation
override fun <T: Any> startProtocolDynamic(logicType: Class<out ProtocolLogic<T>>, vararg args: Any?): ProtocolHandle<T> {
requirePermission(startProtocolPermission(logicType))
val stateMachine = services.invokeProtocolAsync(logicType, *args) as ProtocolStateMachineImpl<T>
return ProtocolHandle(
// TODO: Check that this flow is annotated as being intended for RPC invocation
override fun <T : Any> startFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowHandle<T> {
requirePermission(startFlowPermission(logicType))
val stateMachine = services.invokeFlowAsync(logicType, *args) as FlowStateMachineImpl<T>
return FlowHandle(
id = stateMachine.id,
progress = stateMachine.logic.progressTracker?.changes ?: Observable.empty<ProgressTracker.Change>(),
returnValue = stateMachine.resultFuture.toObservable()

View File

@ -61,7 +61,7 @@ class ConfigurationException(message: String) : Exception(message)
* network map service, while bootstrapping a network.
* @param advertisedServices The services this node advertises. This must be a subset of the services it runs,
* but nodes are not required to advertise services they run (hence subset).
* @param clock The clock used within the node and by all protocols etc.
* @param clock The clock used within the node and by all flows etc.
*/
class Node(override val configuration: FullNodeConfiguration, networkMapAddress: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, clock: Clock = NodeClock()) : AbstractNode(configuration, networkMapAddress, advertisedServices, clock) {
@ -102,7 +102,7 @@ class Node(override val configuration: FullNodeConfiguration, networkMapAddress:
// layer, which can then react to the backpressure. Artemis MQ in particular knows how to do flow control by paging
// messages to disk rather than letting us run out of RAM.
//
// The primary work done by the server thread is execution of protocol logics, and related
// The primary work done by the server thread is execution of flow logics, and related
// serialisation/deserialisation work.
override val serverThread = AffinityExecutor.ServiceAffinityExecutor("Node thread", 1)

View File

@ -3,8 +3,7 @@ package net.corda.node.services
import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.PluginServiceHub
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.node.services.api.ServiceHubInternal
import net.corda.protocols.NotaryChangeProtocol
import net.corda.flows.NotaryChangeFlow
object NotaryChange {
class Plugin : CordaPluginRegistry() {
@ -13,11 +12,11 @@ object NotaryChange {
/**
* A service that monitors the network for requests for changing the notary of a state,
* and immediately runs the [NotaryChangeProtocol] if the auto-accept criteria are met.
* and immediately runs the [NotaryChangeFlow] if the auto-accept criteria are met.
*/
class Service(services: PluginServiceHub) : SingletonSerializeAsToken() {
init {
services.registerProtocolInitiator(NotaryChangeProtocol.Instigator::class) { NotaryChangeProtocol.Acceptor(it) }
services.registerFlowInitiator(NotaryChangeFlow.Instigator::class) { NotaryChangeFlow.Acceptor(it) }
}
}
}

View File

@ -1,7 +1,7 @@
package net.corda.node.services
import com.typesafe.config.Config
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.flows.FlowLogic
import net.corda.node.services.config.getListOrElse
/**
@ -41,5 +41,5 @@ data class User(val username: String, val password: String, val permissions: Set
override fun toString(): String = "${javaClass.simpleName}($username, permissions=$permissions)"
}
fun <P : ProtocolLogic<*>> startProtocolPermission(clazz: Class<P>) = "StartProtocol.${clazz.name}"
inline fun <reified P : ProtocolLogic<*>> startProtocolPermission(): String = startProtocolPermission(P::class.java)
fun <P : FlowLogic<*>> startFlowPermission(clazz: Class<P>) = "StartFlow.${clazz.name}"
inline fun <reified P : FlowLogic<*>> startFlowPermission(): String = startFlowPermission(P::class.java)

View File

@ -7,7 +7,7 @@ import net.corda.core.node.services.DEFAULT_SESSION_ID
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.protocols.ServiceRequestMessage
import net.corda.flows.ServiceRequestMessage
import javax.annotation.concurrent.ThreadSafe
/**

View File

@ -2,7 +2,7 @@ package net.corda.node.services.api
import net.corda.core.crypto.SecureHash
import net.corda.core.serialization.SerializedBytes
import net.corda.node.services.statemachine.ProtocolStateMachineImpl
import net.corda.node.services.statemachine.FlowStateMachineImpl
/**
* Thread-safe storage of fiber checkpoints.
@ -30,7 +30,7 @@ interface CheckpointStorage {
}
// This class will be serialised, so everything it points to transitively must also be serialisable (with Kryo).
class Checkpoint(val serializedFiber: SerializedBytes<ProtocolStateMachineImpl<*>>) {
class Checkpoint(val serializedFiber: SerializedBytes<FlowStateMachineImpl<*>>) {
val id: SecureHash get() = serializedFiber.hash

View File

@ -1,14 +1,14 @@
package net.corda.node.services.api
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.flows.FlowStateMachine
import net.corda.core.messaging.MessagingService
import net.corda.core.node.PluginServiceHub
import net.corda.core.node.services.TxWritableStorageService
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.protocols.ProtocolLogicRefFactory
import net.corda.core.protocols.ProtocolStateMachine
import net.corda.core.transactions.SignedTransaction
import net.corda.node.services.statemachine.ProtocolStateMachineImpl
import net.corda.node.services.statemachine.FlowStateMachineImpl
import org.slf4j.LoggerFactory
interface MessagingServiceInternal : MessagingService {
@ -38,7 +38,7 @@ private val log = LoggerFactory.getLogger(ServiceHubInternal::class.java)
abstract class ServiceHubInternal : PluginServiceHub {
abstract val monitoringService: MonitoringService
abstract val protocolLogicRefFactory: ProtocolLogicRefFactory
abstract val flowLogicRefFactory: FlowLogicRefFactory
abstract val schemaService: SchemaService
abstract override val networkService: MessagingServiceInternal
@ -51,7 +51,7 @@ abstract class ServiceHubInternal : PluginServiceHub {
* @param txs The transactions to record.
*/
internal fun recordTransactionsInternal(writableStorageService: TxWritableStorageService, txs: Iterable<SignedTransaction>) {
val stateMachineRunId = ProtocolStateMachineImpl.currentStateMachine()?.id
val stateMachineRunId = FlowStateMachineImpl.currentStateMachine()?.id
if (stateMachineRunId != null) {
txs.forEach {
storageService.stateMachineRecordedTransactionMapping.addMapping(stateMachineRunId, it.id)
@ -68,12 +68,12 @@ abstract class ServiceHubInternal : PluginServiceHub {
* between SMM and the scheduler. That particular problem should also be resolved by the service manager work
* itself, at which point this method would not be needed (by the scheduler).
*/
abstract fun <T> startProtocol(logic: ProtocolLogic<T>): ProtocolStateMachine<T>
abstract fun <T> startFlow(logic: FlowLogic<T>): FlowStateMachine<T>
override fun <T : Any> invokeProtocolAsync(logicType: Class<out ProtocolLogic<T>>, vararg args: Any?): ProtocolStateMachine<T> {
val logicRef = protocolLogicRefFactory.create(logicType, *args)
override fun <T : Any> invokeFlowAsync(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowStateMachine<T> {
val logicRef = flowLogicRefFactory.create(logicType, *args)
@Suppress("UNCHECKED_CAST")
val logic = protocolLogicRefFactory.toProtocolLogic(logicRef) as ProtocolLogic<T>
return startProtocol(logic)
val logic = flowLogicRefFactory.toFlowLogic(logicRef) as FlowLogic<T>
return startFlow(logic)
}
}

View File

@ -2,21 +2,21 @@ package net.corda.node.services.events
import co.paralleluniverse.fibers.Suspendable
import com.google.common.util.concurrent.SettableFuture
import kotlinx.support.jdk8.collections.compute
import net.corda.core.ThreadBox
import net.corda.core.contracts.SchedulableState
import net.corda.core.contracts.ScheduledActivity
import net.corda.core.contracts.ScheduledStateRef
import net.corda.core.contracts.StateRef
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.node.services.SchedulerService
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.protocols.ProtocolLogicRefFactory
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.trace
import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.utilities.*
import kotlinx.support.jdk8.collections.compute
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.statements.InsertStatement
@ -38,14 +38,14 @@ import javax.annotation.concurrent.ThreadSafe
* but that starts to sound a lot like off-ledger state.
*
* @param services Core node services.
* @param protocolLogicRefFactory Factory for restoring [ProtocolLogic] instances from references.
* @param flowLogicRefFactory Factory for restoring [FlowLogic] instances from references.
* @param schedulerTimerExecutor The executor the scheduler blocks on waiting for the clock to advance to the next
* activity. Only replace this for unit testing purposes. This is not the executor the [ProtocolLogic] is launched on.
* activity. Only replace this for unit testing purposes. This is not the executor the [FlowLogic] is launched on.
*/
@ThreadSafe
class NodeSchedulerService(private val database: Database,
private val services: ServiceHubInternal,
private val protocolLogicRefFactory: ProtocolLogicRefFactory,
private val flowLogicRefFactory: FlowLogicRefFactory,
private val schedulerTimerExecutor: Executor = Executors.newSingleThreadExecutor())
: SchedulerService, SingletonSerializeAsToken() {
@ -87,7 +87,7 @@ class NodeSchedulerService(private val database: Database,
private val mutex = ThreadBox(InnerState())
// We need the [StateMachineManager] to be constructed before this is called in case it schedules a protocol.
// We need the [StateMachineManager] to be constructed before this is called in case it schedules a flow.
fun start() {
mutex.locked {
recomputeEarliest()
@ -152,10 +152,10 @@ class NodeSchedulerService(private val database: Database,
}
private fun onTimeReached(scheduledState: ScheduledStateRef) {
services.startProtocol(RunScheduled(scheduledState, this@NodeSchedulerService))
services.startFlow(RunScheduled(scheduledState, this@NodeSchedulerService))
}
class RunScheduled(val scheduledState: ScheduledStateRef, val scheduler: NodeSchedulerService) : ProtocolLogic<Unit>() {
class RunScheduled(val scheduledState: ScheduledStateRef, val scheduler: NodeSchedulerService) : FlowLogic<Unit>() {
companion object {
object RUNNING : ProgressTracker.Step("Running scheduled...")
@ -169,9 +169,9 @@ class NodeSchedulerService(private val database: Database,
progressTracker.currentStep = RUNNING
// Ensure we are still scheduled.
val scheduledLogic: ProtocolLogic<*>? = getScheduledLogic()
val scheduledLogic: FlowLogic<*>? = getScheduledLogic()
if(scheduledLogic != null) {
subProtocol(scheduledLogic)
subFlow(scheduledLogic)
}
}
@ -180,16 +180,16 @@ class NodeSchedulerService(private val database: Database,
val state = txState.data as SchedulableState
return try {
// This can throw as running contract code.
state.nextScheduledActivity(scheduledState.ref, scheduler.protocolLogicRefFactory)
state.nextScheduledActivity(scheduledState.ref, scheduler.flowLogicRefFactory)
} catch(e: Exception) {
logger.error("Attempt to run scheduled state $scheduledState resulted in error.", e)
null
}
}
private fun getScheduledLogic(): ProtocolLogic<*>? {
private fun getScheduledLogic(): FlowLogic<*>? {
val scheduledActivity = getScheduledaActivity()
var scheduledLogic: ProtocolLogic<*>? = null
var scheduledLogic: FlowLogic<*>? = null
scheduler.mutex.locked {
// need to remove us from those scheduled, but only if we are still next
scheduledStates.compute(scheduledState.ref) { ref, value ->
@ -201,11 +201,11 @@ class NodeSchedulerService(private val database: Database,
logger.info("Scheduled state $scheduledState has rescheduled to ${scheduledActivity.scheduledAt}.")
ScheduledStateRef(scheduledState.ref, scheduledActivity.scheduledAt)
} else {
// TODO: ProtocolLogicRefFactory needs to sort out the class loader etc
val logic = scheduler.protocolLogicRefFactory.toProtocolLogic(scheduledActivity.logicRef)
logger.trace { "Scheduler starting ProtocolLogic $logic" }
// ProtocolLogic will be checkpointed by the time this returns.
//scheduler.services.startProtocolAndForget(logic)
// TODO: FlowLogicRefFactory needs to sort out the class loader etc
val logic = scheduler.flowLogicRefFactory.toFlowLogic(scheduledActivity.logicRef)
logger.trace { "Scheduler starting FlowLogic $logic" }
// FlowLogic will be checkpointed by the time this returns.
//scheduler.services.startFlowAndForget(logic)
scheduledLogic = logic
null
}

View File

@ -4,7 +4,7 @@ import net.corda.core.contracts.ContractState
import net.corda.core.contracts.SchedulableState
import net.corda.core.contracts.ScheduledStateRef
import net.corda.core.contracts.StateAndRef
import net.corda.core.protocols.ProtocolLogicRefFactory
import net.corda.core.flows.FlowLogicRefFactory
import net.corda.node.services.api.ServiceHubInternal
/**
@ -15,14 +15,14 @@ class ScheduledActivityObserver(val services: ServiceHubInternal) {
init {
services.vaultService.updates.subscribe { update ->
update.consumed.forEach { services.schedulerService.unscheduleStateActivity(it) }
update.produced.forEach { scheduleStateActivity(it, services.protocolLogicRefFactory) }
update.produced.forEach { scheduleStateActivity(it, services.flowLogicRefFactory) }
}
}
private fun scheduleStateActivity(produced: StateAndRef<ContractState>, protocolLogicRefFactory: ProtocolLogicRefFactory) {
private fun scheduleStateActivity(produced: StateAndRef<ContractState>, flowLogicRefFactory: FlowLogicRefFactory) {
val producedState = produced.state.data
if (producedState is SchedulableState) {
val scheduledAt = sandbox { producedState.nextScheduledActivity(produced.ref, protocolLogicRefFactory)?.scheduledAt } ?: return
val scheduledAt = sandbox { producedState.nextScheduledActivity(produced.ref, flowLogicRefFactory)?.scheduledAt } ?: return
services.schedulerService.scheduleStateActivity(ScheduledStateRef(produced.ref, scheduledAt))
}
}

View File

@ -15,7 +15,7 @@ import javax.annotation.concurrent.ThreadSafe
*
* - Probably be accessed via the network layer as an internal node service i.e. via a message queue, so it can run
* on a separate/firewalled service.
* - Use the protocol framework so requests to fetch keys can be suspended whilst a human signs off on the request.
* - Use the flow framework so requests to fetch keys can be suspended whilst a human signs off on the request.
* - Use deterministic key derivation.
* - Possibly have some sort of TREZOR-like two-factor authentication ability.
*

View File

@ -158,7 +158,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
log.error("Queue created for a peer that we don't know from the network map: $queueName")
}
} catch (e: AddressFormatException) {
log.error("Protocol violation: Could not parse queue name as Base 58: $queueName")
log.error("Flow violation: Could not parse queue name as Base 58: $queueName")
}
}
}

View File

@ -3,29 +3,29 @@ package net.corda.node.services.messaging
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StateMachineRunId
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.StateMachineTransactionMapping
import net.corda.core.node.services.Vault
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.protocols.StateMachineRunId
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker
import net.corda.node.services.statemachine.ProtocolStateMachineImpl
import net.corda.node.services.statemachine.FlowStateMachineImpl
import net.corda.node.services.statemachine.StateMachineManager
import net.corda.node.utilities.AddOrRemove
import rx.Observable
data class StateMachineInfo(
val id: StateMachineRunId,
val protocolLogicClassName: String,
val flowLogicClassName: String,
val progressTrackerStepAndUpdates: Pair<String, Observable<String>>?
) {
companion object {
fun fromProtocolStateMachineImpl(psm: ProtocolStateMachineImpl<*>): StateMachineInfo {
fun fromFlowStateMachineImpl(psm: FlowStateMachineImpl<*>): StateMachineInfo {
return StateMachineInfo(
id = psm.id,
protocolLogicClassName = psm.logic.javaClass.simpleName,
flowLogicClassName = psm.logic.javaClass.simpleName,
progressTrackerStepAndUpdates = psm.logic.track()
)
}
@ -42,7 +42,7 @@ sealed class StateMachineUpdate(val id: StateMachineRunId) {
AddOrRemove.ADD -> {
val stateMachineInfo = StateMachineInfo(
id = change.id,
protocolLogicClassName = change.logic.javaClass.simpleName,
flowLogicClassName = change.logic.javaClass.simpleName,
progressTrackerStepAndUpdates = change.logic.track()
)
StateMachineUpdate.Added(stateMachineInfo)
@ -92,11 +92,11 @@ interface CordaRPCOps : RPCOps {
fun networkMapUpdates(): Pair<List<NodeInfo>, Observable<NetworkMapCache.MapChange>>
/**
* Start the given protocol with the given arguments, returning an [Observable] with a single observation of the
* result of running the protocol.
* Start the given flow with the given arguments, returning an [Observable] with a single observation of the
* result of running the flow.
*/
@RPCReturnsObservables
fun <T: Any> startProtocolDynamic(logicType: Class<out ProtocolLogic<T>>, vararg args: Any?): ProtocolHandle<T>
fun <T : Any> startFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?): FlowHandle<T>
/**
* Returns Node's identity, assuming this will not change while the node is running.
@ -115,46 +115,50 @@ interface CordaRPCOps : RPCOps {
}
/**
* These allow type safe invocations of protocols from Kotlin, e.g.:
* These allow type safe invocations of flows from Kotlin, e.g.:
*
* val rpc: CordaRPCOps = (..)
* rpc.startProtocol(::ResolveTransactionsProtocol, setOf<SecureHash>(), aliceIdentity)
* rpc.startFlow(::ResolveTransactionsFlow, setOf<SecureHash>(), aliceIdentity)
*
* Note that the passed in constructor function is only used for unification of other type parameters and reification of
* the Class instance of the protocol. This could be changed to use the constructor function directly.
* the Class instance of the flow. This could be changed to use the constructor function directly.
*/
inline fun <T : Any, reified R : ProtocolLogic<T>> CordaRPCOps.startProtocol(
inline fun <T : Any, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
@Suppress("UNUSED_PARAMETER")
protocolConstructor: () -> R
) = startProtocolDynamic(R::class.java)
inline fun <T : Any, A, reified R : ProtocolLogic<T>> CordaRPCOps.startProtocol(
flowConstructor: () -> R
) = startFlowDynamic(R::class.java)
inline fun <T : Any, A, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
@Suppress("UNUSED_PARAMETER")
protocolConstructor: (A) -> R,
flowConstructor: (A) -> R,
arg0: A
) = startProtocolDynamic(R::class.java, arg0)
inline fun <T : Any, A, B, reified R : ProtocolLogic<T>> CordaRPCOps.startProtocol(
) = startFlowDynamic(R::class.java, arg0)
inline fun <T : Any, A, B, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
@Suppress("UNUSED_PARAMETER")
protocolConstructor: (A, B) -> R,
flowConstructor: (A, B) -> R,
arg0: A,
arg1: B
) = startProtocolDynamic(R::class.java, arg0, arg1)
inline fun <T : Any, A, B, C, reified R: ProtocolLogic<T>> CordaRPCOps.startProtocol(
) = startFlowDynamic(R::class.java, arg0, arg1)
inline fun <T : Any, A, B, C, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
@Suppress("UNUSED_PARAMETER")
protocolConstructor: (A, B, C) -> R,
flowConstructor: (A, B, C) -> R,
arg0: A,
arg1: B,
arg2: C
) = startProtocolDynamic(R::class.java, arg0, arg1, arg2)
inline fun <T : Any, A, B, C, D, reified R : ProtocolLogic<T>> CordaRPCOps.startProtocol(
) = startFlowDynamic(R::class.java, arg0, arg1, arg2)
inline fun <T : Any, A, B, C, D, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
@Suppress("UNUSED_PARAMETER")
protocolConstructor: (A, B, C, D) -> R,
flowConstructor: (A, B, C, D) -> R,
arg0: A,
arg1: B,
arg2: C,
arg3: D
) = startProtocolDynamic(R::class.java, arg0, arg1, arg2, arg3)
) = startFlowDynamic(R::class.java, arg0, arg1, arg2, arg3)
data class ProtocolHandle<A>(
data class FlowHandle<A>(
val id: StateMachineRunId,
val progress: Observable<ProgressTracker.Change>,
val returnValue: Observable<A>

View File

@ -19,14 +19,14 @@ import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.StateMachineRunId
import net.corda.core.node.*
import net.corda.core.node.services.*
import net.corda.core.protocols.StateMachineRunId
import net.corda.core.serialization.*
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.flows.CashFlowResult
import net.corda.node.services.User
import net.corda.protocols.CashProtocolResult
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.apache.activemq.artemis.api.core.SimpleString
@ -181,8 +181,8 @@ private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null)
register(Cash.Clauses.ConserveAmount::class.java)
register(listOf(Unit).javaClass) // SingletonList
register(setOf(Unit).javaClass) // SingletonSet
register(CashProtocolResult.Success::class.java)
register(CashProtocolResult.Failed::class.java)
register(CashFlowResult.Success::class.java)
register(CashFlowResult.Failed::class.java)
register(ServiceEntry::class.java)
register(NodeInfo::class.java)
register(PhysicalLocation::class.java)
@ -215,7 +215,7 @@ private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null)
register(Array<StackTraceElement>::class.java, read = { kryo, input -> emptyArray() }, write = { kryo, output, o -> })
register(Collections.unmodifiableList(emptyList<String>()).javaClass)
register(PermissionException::class.java)
register(ProtocolHandle::class.java)
register(FlowHandle::class.java)
register(KryoException::class.java)
register(StringBuffer::class.java)
pluginRegistries.forEach { it.registerRPCKryoTypes(this) }

View File

@ -22,12 +22,12 @@ import net.corda.core.randomOrNull
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.node.services.network.NetworkMapService.Companion.FETCH_PROTOCOL_TOPIC
import net.corda.node.services.network.NetworkMapService.Companion.SUBSCRIPTION_PROTOCOL_TOPIC
import net.corda.flows.sendRequest
import net.corda.node.services.network.NetworkMapService.Companion.FETCH_FLOW_TOPIC
import net.corda.node.services.network.NetworkMapService.Companion.SUBSCRIPTION_FLOW_TOPIC
import net.corda.node.services.network.NetworkMapService.FetchMapResponse
import net.corda.node.services.network.NetworkMapService.SubscribeResponse
import net.corda.node.utilities.AddOrRemove
import net.corda.protocols.sendRequest
import rx.Observable
import rx.subjects.PublishSubject
import java.security.SignatureException
@ -106,10 +106,10 @@ open class InMemoryNetworkMapCache : SingletonSerializeAsToken(), NetworkMapCach
ifChangedSinceVer: Int?): ListenableFuture<Unit> {
if (subscribe && !registeredForPush) {
// Add handler to the network, for updates received from the remote network map service.
net.addMessageHandler(NetworkMapService.PUSH_PROTOCOL_TOPIC, DEFAULT_SESSION_ID) { message, r ->
net.addMessageHandler(NetworkMapService.PUSH_FLOW_TOPIC, DEFAULT_SESSION_ID) { message, r ->
try {
val req = message.data.deserialize<NetworkMapService.Update>()
val ackMessage = net.createMessage(NetworkMapService.PUSH_ACK_PROTOCOL_TOPIC, DEFAULT_SESSION_ID,
val ackMessage = net.createMessage(NetworkMapService.PUSH_ACK_FLOW_TOPIC, DEFAULT_SESSION_ID,
NetworkMapService.UpdateAcknowledge(req.mapVersion, net.myAddress).serialize().bytes)
net.send(ackMessage, req.replyTo)
processUpdatePush(req)
@ -124,7 +124,7 @@ open class InMemoryNetworkMapCache : SingletonSerializeAsToken(), NetworkMapCach
// Fetch the network map and register for updates at the same time
val req = NetworkMapService.FetchMapRequest(subscribe, ifChangedSinceVer, net.myAddress)
val future = net.sendRequest<FetchMapResponse>(FETCH_PROTOCOL_TOPIC, req, networkMapAddress).map { resp ->
val future = net.sendRequest<FetchMapResponse>(FETCH_FLOW_TOPIC, req, networkMapAddress).map { resp ->
// We may not receive any nodes back, if the map hasn't changed since the version specified
resp.nodes?.forEach { processRegistration(it) }
Unit
@ -161,7 +161,7 @@ open class InMemoryNetworkMapCache : SingletonSerializeAsToken(), NetworkMapCach
override fun deregisterForUpdates(net: MessagingService, service: NodeInfo): ListenableFuture<Unit> {
// Fetch the network map and register for updates at the same time
val req = NetworkMapService.SubscribeRequest(false, net.myAddress)
val future = net.sendRequest<SubscribeResponse>(SUBSCRIPTION_PROTOCOL_TOPIC, req, service.address).map {
val future = net.sendRequest<SubscribeResponse>(SUBSCRIPTION_FLOW_TOPIC, req, service.address).map {
if (it.confirmed) Unit else throw NetworkCacheError.DeregistrationFailed()
}
_registrationFuture.setFuture(future)

View File

@ -20,10 +20,10 @@ import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.utilities.loggerFor
import net.corda.flows.ServiceRequestMessage
import net.corda.node.services.api.AbstractNodeService
import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.utilities.AddOrRemove
import net.corda.protocols.ServiceRequestMessage
import java.security.PrivateKey
import java.security.SignatureException
import java.time.Instant
@ -51,15 +51,15 @@ interface NetworkMapService {
companion object {
val DEFAULT_EXPIRATION_PERIOD = Period.ofWeeks(4)
val FETCH_PROTOCOL_TOPIC = "platform.network_map.fetch"
val QUERY_PROTOCOL_TOPIC = "platform.network_map.query"
val REGISTER_PROTOCOL_TOPIC = "platform.network_map.register"
val SUBSCRIPTION_PROTOCOL_TOPIC = "platform.network_map.subscribe"
val FETCH_FLOW_TOPIC = "platform.network_map.fetch"
val QUERY_FLOW_TOPIC = "platform.network_map.query"
val REGISTER_FLOW_TOPIC = "platform.network_map.register"
val SUBSCRIPTION_FLOW_TOPIC = "platform.network_map.subscribe"
// Base topic used when pushing out updates to the network map. Consumed, for example, by the map cache.
// When subscribing to these updates, remember they must be acknowledged
val PUSH_PROTOCOL_TOPIC = "platform.network_map.push"
val PUSH_FLOW_TOPIC = "platform.network_map.push"
// Base topic for messages acknowledging pushed updates
val PUSH_ACK_PROTOCOL_TOPIC = "platform.network_map.push_ack"
val PUSH_ACK_FLOW_TOPIC = "platform.network_map.push_ack"
val logger = loggerFor<NetworkMapService>()
@ -142,19 +142,19 @@ abstract class AbstractNetworkMapService
protected fun setup() {
// Register message handlers
handlers += addMessageHandler(NetworkMapService.FETCH_PROTOCOL_TOPIC,
handlers += addMessageHandler(NetworkMapService.FETCH_FLOW_TOPIC,
{ req: NetworkMapService.FetchMapRequest -> processFetchAllRequest(req) }
)
handlers += addMessageHandler(NetworkMapService.QUERY_PROTOCOL_TOPIC,
handlers += addMessageHandler(NetworkMapService.QUERY_FLOW_TOPIC,
{ req: NetworkMapService.QueryIdentityRequest -> processQueryRequest(req) }
)
handlers += addMessageHandler(NetworkMapService.REGISTER_PROTOCOL_TOPIC,
handlers += addMessageHandler(NetworkMapService.REGISTER_FLOW_TOPIC,
{ req: NetworkMapService.RegistrationRequest -> processRegistrationChangeRequest(req) }
)
handlers += addMessageHandler(NetworkMapService.SUBSCRIPTION_PROTOCOL_TOPIC,
handlers += addMessageHandler(NetworkMapService.SUBSCRIPTION_FLOW_TOPIC,
{ req: NetworkMapService.SubscribeRequest -> processSubscriptionRequest(req) }
)
handlers += net.addMessageHandler(NetworkMapService.PUSH_ACK_PROTOCOL_TOPIC, DEFAULT_SESSION_ID) { message, r ->
handlers += net.addMessageHandler(NetworkMapService.PUSH_ACK_FLOW_TOPIC, DEFAULT_SESSION_ID) { message, r ->
val req = message.data.deserialize<NetworkMapService.UpdateAcknowledge>()
processAcknowledge(req)
}
@ -200,7 +200,7 @@ abstract class AbstractNetworkMapService
// to a MessageRecipientGroup that nodes join/leave, rather than the network map
// service itself managing the group
val update = NetworkMapService.Update(wireReg, mapVersion, net.myAddress).serialize().bytes
val message = net.createMessage(NetworkMapService.PUSH_PROTOCOL_TOPIC, DEFAULT_SESSION_ID, update)
val message = net.createMessage(NetworkMapService.PUSH_FLOW_TOPIC, DEFAULT_SESSION_ID, update)
subscribers.locked {
val toRemove = mutableListOf<SingleMessageRecipient>()

View File

@ -3,9 +3,9 @@ package net.corda.node.services.persistence
import net.corda.core.ThreadBox
import net.corda.core.bufferUntilSubscribed
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.StateMachineRunId
import net.corda.core.node.services.StateMachineRecordedTransactionMappingStorage
import net.corda.core.node.services.StateMachineTransactionMapping
import net.corda.core.protocols.StateMachineRunId
import net.corda.node.utilities.*
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.statements.InsertStatement
@ -17,7 +17,7 @@ import javax.annotation.concurrent.ThreadSafe
* Database storage of a txhash -> state machine id mapping.
*
* Mappings are added as transactions are persisted by [ServiceHub.recordTransaction], and never deleted. Used in the
* RPC API to correlate transaction creation with protocols.
* RPC API to correlate transaction creation with flows.
*
*/
@ThreadSafe

View File

@ -2,14 +2,13 @@ package net.corda.node.services.persistence
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic
import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.PluginServiceHub
import net.corda.core.node.recordTransactions
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.loggerFor
import net.corda.node.services.api.ServiceHubInternal
import net.corda.protocols.*
import net.corda.flows.*
import java.io.InputStream
import javax.annotation.concurrent.ThreadSafe
@ -41,16 +40,16 @@ object DataVending {
class TransactionRejectedError(msg: String) : Exception(msg)
init {
services.registerProtocolInitiator(FetchTransactionsProtocol::class, ::FetchTransactionsHandler)
services.registerProtocolInitiator(FetchAttachmentsProtocol::class, ::FetchAttachmentsHandler)
services.registerProtocolInitiator(BroadcastTransactionProtocol::class, ::NotifyTransactionHandler)
services.registerFlowInitiator(FetchTransactionsFlow::class, ::FetchTransactionsHandler)
services.registerFlowInitiator(FetchAttachmentsFlow::class, ::FetchAttachmentsHandler)
services.registerFlowInitiator(BroadcastTransactionFlow::class, ::NotifyTransactionHandler)
}
private class FetchTransactionsHandler(val otherParty: Party) : ProtocolLogic<Unit>() {
private class FetchTransactionsHandler(val otherParty: Party) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val request = receive<FetchDataProtocol.Request>(otherParty).unwrap {
val request = receive<FetchDataFlow.Request>(otherParty).unwrap {
require(it.hashes.isNotEmpty())
it
}
@ -66,10 +65,10 @@ object DataVending {
// TODO: Use Artemis message streaming support here, called "large messages". This avoids the need to buffer.
private class FetchAttachmentsHandler(val otherParty: Party) : ProtocolLogic<Unit>() {
private class FetchAttachmentsHandler(val otherParty: Party) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val request = receive<FetchDataProtocol.Request>(otherParty).unwrap {
val request = receive<FetchDataFlow.Request>(otherParty).unwrap {
require(it.hashes.isNotEmpty())
it
}
@ -91,11 +90,11 @@ object DataVending {
// includes us in any outside that list. Potentially just if it includes any outside that list at all.
// TODO: Do we want to be able to reject specific transactions on more complex rules, for example reject incoming
// cash without from unknown parties?
class NotifyTransactionHandler(val otherParty: Party) : ProtocolLogic<Unit>() {
class NotifyTransactionHandler(val otherParty: Party) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val request = receive<BroadcastTransactionProtocol.NotifyTxRequest>(otherParty).unwrap { it }
subProtocol(ResolveTransactionsProtocol(request.tx, otherParty), shareParentSessions = true)
val request = receive<BroadcastTransactionFlow.NotifyTxRequest>(otherParty).unwrap { it }
subFlow(ResolveTransactionsFlow(request.tx, otherParty), shareParentSessions = true)
serviceHub.recordTransactions(request.tx)
}
}

View File

@ -5,7 +5,7 @@ import net.corda.core.bufferUntilSubscribed
import net.corda.core.crypto.SecureHash
import net.corda.core.node.services.StateMachineRecordedTransactionMappingStorage
import net.corda.core.node.services.StateMachineTransactionMapping
import net.corda.core.protocols.StateMachineRunId
import net.corda.core.flows.StateMachineRunId
import rx.Observable
import rx.subjects.PublishSubject
import java.util.*

View File

@ -7,10 +7,10 @@ import co.paralleluniverse.strands.Strand
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.SettableFuture
import net.corda.core.crypto.Party
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.protocols.ProtocolSessionException
import net.corda.core.protocols.ProtocolStateMachine
import net.corda.core.protocols.StateMachineRunId
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSessionException
import net.corda.core.flows.FlowStateMachine
import net.corda.core.flows.StateMachineRunId
import net.corda.core.random63BitValue
import net.corda.core.utilities.UntrustworthyData
import net.corda.core.utilities.trace
@ -28,9 +28,9 @@ import java.sql.SQLException
import java.util.*
import java.util.concurrent.ExecutionException
class ProtocolStateMachineImpl<R>(override val id: StateMachineRunId,
val logic: ProtocolLogic<R>,
scheduler: FiberScheduler) : Fiber<R>("protocol", scheduler), ProtocolStateMachine<R> {
class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
val logic: FlowLogic<R>,
scheduler: FiberScheduler) : Fiber<R>("flow", scheduler), FlowStateMachine<R> {
companion object {
// Used to work around a small limitation in Quasar.
@ -41,14 +41,14 @@ class ProtocolStateMachineImpl<R>(override val id: StateMachineRunId,
}
/**
* Return the current [ProtocolStateMachineImpl] or null if executing outside of one.
* Return the current [FlowStateMachineImpl] or null if executing outside of one.
*/
fun currentStateMachine(): ProtocolStateMachineImpl<*>? = Strand.currentStrand() as? ProtocolStateMachineImpl<*>
fun currentStateMachine(): FlowStateMachineImpl<*>? = Strand.currentStrand() as? FlowStateMachineImpl<*>
}
// These fields shouldn't be serialised, so they are marked @Transient.
@Transient lateinit override var serviceHub: ServiceHubInternal
@Transient internal lateinit var actionOnSuspend: (ProtocolIORequest) -> Unit
@Transient internal lateinit var actionOnSuspend: (FlowIORequest) -> Unit
@Transient internal lateinit var actionOnEnd: () -> Unit
@Transient internal lateinit var database: Database
@Transient internal var fromCheckpoint: Boolean = false
@ -73,10 +73,10 @@ class ProtocolStateMachineImpl<R>(override val id: StateMachineRunId,
}
}
internal val openSessions = HashMap<Pair<ProtocolLogic<*>, Party>, ProtocolSession>()
internal val openSessions = HashMap<Pair<FlowLogic<*>, Party>, FlowSession>()
init {
logic.psm = this
logic.fsm = this
name = id.toString()
}
@ -122,8 +122,8 @@ class ProtocolStateMachineImpl<R>(override val id: StateMachineRunId,
override fun <T : Any> sendAndReceive(otherParty: Party,
payload: Any,
receiveType: Class<T>,
sessionProtocol: ProtocolLogic<*>): UntrustworthyData<T> {
val (session, new) = getSession(otherParty, sessionProtocol, payload)
sessionFlow: FlowLogic<*>): UntrustworthyData<T> {
val (session, new) = getSession(otherParty, sessionFlow, payload)
val receivedSessionData = if (new) {
// Only do a receive here as the session init has carried the payload
receiveInternal<SessionData>(session)
@ -137,48 +137,48 @@ class ProtocolStateMachineImpl<R>(override val id: StateMachineRunId,
@Suspendable
override fun <T : Any> receive(otherParty: Party,
receiveType: Class<T>,
sessionProtocol: ProtocolLogic<*>): UntrustworthyData<T> {
val session = getSession(otherParty, sessionProtocol, null).first
sessionFlow: FlowLogic<*>): UntrustworthyData<T> {
val session = getSession(otherParty, sessionFlow, null).first
val receivedSessionData = receiveInternal<SessionData>(session)
return UntrustworthyData(receiveType.cast(receivedSessionData.payload))
}
@Suspendable
override fun send(otherParty: Party, payload: Any, sessionProtocol: ProtocolLogic<*>) {
val (session, new) = getSession(otherParty, sessionProtocol, payload)
override fun send(otherParty: Party, payload: Any, sessionFlow: FlowLogic<*>) {
val (session, new) = getSession(otherParty, sessionFlow, payload)
if (!new) {
// Don't send the payload again if it was already piggy-backed on a session init
sendInternal(session, createSessionData(session, payload))
}
}
private fun createSessionData(session: ProtocolSession, payload: Any): SessionData {
private fun createSessionData(session: FlowSession, payload: Any): SessionData {
val otherPartySessionId = session.otherPartySessionId
?: throw IllegalStateException("We've somehow held onto an unconfirmed session: $session")
return SessionData(otherPartySessionId, payload)
}
@Suspendable
private fun sendInternal(session: ProtocolSession, message: SessionMessage) {
private fun sendInternal(session: FlowSession, message: SessionMessage) {
suspend(SendOnly(session, message))
}
@Suspendable
private inline fun <reified M : SessionMessage> receiveInternal(session: ProtocolSession): M {
private inline fun <reified M : SessionMessage> receiveInternal(session: FlowSession): M {
return suspendAndExpectReceive(ReceiveOnly(session, M::class.java))
}
private inline fun <reified M : SessionMessage> sendAndReceiveInternal(session: ProtocolSession, message: SessionMessage): M {
private inline fun <reified M : SessionMessage> sendAndReceiveInternal(session: FlowSession, message: SessionMessage): M {
return suspendAndExpectReceive(SendAndReceive(session, message, M::class.java))
}
@Suspendable
private fun getSession(otherParty: Party, sessionProtocol: ProtocolLogic<*>, firstPayload: Any?): Pair<ProtocolSession, Boolean> {
val session = openSessions[Pair(sessionProtocol, otherParty)]
private fun getSession(otherParty: Party, sessionFlow: FlowLogic<*>, firstPayload: Any?): Pair<FlowSession, Boolean> {
val session = openSessions[Pair(sessionFlow, otherParty)]
return if (session != null) {
Pair(session, false)
} else {
Pair(startNewSession(otherParty, sessionProtocol, firstPayload), true)
Pair(startNewSession(otherParty, sessionFlow, firstPayload), true)
}
}
@ -189,21 +189,21 @@ class ProtocolStateMachineImpl<R>(override val id: StateMachineRunId,
* multiple public keys, but we **don't support multiple nodes advertising the same legal identity**.
*/
@Suspendable
private fun startNewSession(otherParty: Party, sessionProtocol: ProtocolLogic<*>, firstPayload: Any?) : ProtocolSession {
private fun startNewSession(otherParty: Party, sessionFlow: FlowLogic<*>, firstPayload: Any?): FlowSession {
val node = serviceHub.networkMapCache.getRepresentativeNode(otherParty) ?: throw IllegalArgumentException("Don't know about party $otherParty")
val nodeIdentity = node.legalIdentity
logger.trace { "Initiating a new session with $nodeIdentity (representative of $otherParty)" }
val session = ProtocolSession(sessionProtocol, nodeIdentity, random63BitValue(), null)
openSessions[Pair(sessionProtocol, nodeIdentity)] = session
val counterpartyProtocol = sessionProtocol.getCounterpartyMarker(nodeIdentity).name
val sessionInit = SessionInit(session.ourSessionId, serviceHub.myInfo.legalIdentity, counterpartyProtocol, firstPayload)
val session = FlowSession(sessionFlow, nodeIdentity, random63BitValue(), null)
openSessions[Pair(sessionFlow, nodeIdentity)] = session
val counterpartyFlow = sessionFlow.getCounterpartyMarker(nodeIdentity).name
val sessionInit = SessionInit(session.ourSessionId, serviceHub.myInfo.legalIdentity, counterpartyFlow, firstPayload)
val sessionInitResponse = sendAndReceiveInternal<SessionInitResponse>(session, sessionInit)
if (sessionInitResponse is SessionConfirm) {
session.otherPartySessionId = sessionInitResponse.initiatedSessionId
return session
} else {
sessionInitResponse as SessionReject
throw ProtocolSessionException("Party $nodeIdentity rejected session attempt: ${sessionInitResponse.errorMessage}")
throw FlowSessionException("Party $nodeIdentity rejected session attempt: ${sessionInitResponse.errorMessage}")
}
}
@ -227,7 +227,7 @@ class ProtocolStateMachineImpl<R>(override val id: StateMachineRunId,
if (receivedMessage is SessionEnd) {
openSessions.values.remove(receiveRequest.session)
throw ProtocolSessionException("Counterparty on ${receiveRequest.session.otherParty} has prematurely ended on $receiveRequest")
throw FlowSessionException("Counterparty on ${receiveRequest.session.otherParty} has prematurely ended on $receiveRequest")
} else if (receiveRequest.receiveType.isInstance(receivedMessage)) {
return receiveRequest.receiveType.cast(receivedMessage)
} else {
@ -236,7 +236,7 @@ class ProtocolStateMachineImpl<R>(override val id: StateMachineRunId,
}
@Suspendable
private fun suspend(ioRequest: ProtocolIORequest) {
private fun suspend(ioRequest: FlowIORequest) {
// we have to pass the Thread local Transaction across via a transient field as the Fiber Park swaps them out.
txTrampoline = TransactionManager.currentOrNull()
StrandLocalTransactionManager.setThreadLocalTx(null)

View File

@ -1,38 +1,38 @@
package net.corda.node.services.statemachine
import net.corda.node.services.statemachine.StateMachineManager.ProtocolSession
import net.corda.node.services.statemachine.StateMachineManager.FlowSession
import net.corda.node.services.statemachine.StateMachineManager.SessionMessage
// TODO revisit when Kotlin 1.1 is released and data classes can extend other classes
interface ProtocolIORequest {
interface FlowIORequest {
// This is used to identify where we suspended, in case of message mismatch errors and other things where we
// don't have the original stack trace because it's in a suspended fiber.
val stackTraceInCaseOfProblems: StackSnapshot
val session: ProtocolSession
val session: FlowSession
}
interface SendRequest : ProtocolIORequest {
interface SendRequest : FlowIORequest {
val message: SessionMessage
}
interface ReceiveRequest<T : SessionMessage> : ProtocolIORequest {
interface ReceiveRequest<T : SessionMessage> : FlowIORequest {
val receiveType: Class<T>
}
data class SendAndReceive<T : SessionMessage>(override val session: ProtocolSession,
data class SendAndReceive<T : SessionMessage>(override val session: FlowSession,
override val message: SessionMessage,
override val receiveType: Class<T>) : SendRequest, ReceiveRequest<T> {
@Transient
override val stackTraceInCaseOfProblems: StackSnapshot = StackSnapshot()
}
data class ReceiveOnly<T : SessionMessage>(override val session: ProtocolSession,
data class ReceiveOnly<T : SessionMessage>(override val session: FlowSession,
override val receiveType: Class<T>) : ReceiveRequest<T> {
@Transient
override val stackTraceInCaseOfProblems: StackSnapshot = StackSnapshot()
}
data class SendOnly(override val session: ProtocolSession, override val message: SessionMessage) : SendRequest {
data class SendOnly(override val session: FlowSession, override val message: SessionMessage) : SendRequest {
@Transient
override val stackTraceInCaseOfProblems: StackSnapshot = StackSnapshot()
}

View File

@ -11,11 +11,11 @@ import kotlinx.support.jdk8.collections.removeIf
import net.corda.core.ThreadBox
import net.corda.core.abbreviate
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowStateMachine
import net.corda.core.flows.StateMachineRunId
import net.corda.core.messaging.TopicSession
import net.corda.core.messaging.send
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.protocols.ProtocolStateMachine
import net.corda.core.protocols.StateMachineRunId
import net.corda.core.random63BitValue
import net.corda.core.serialization.*
import net.corda.core.then
@ -41,8 +41,8 @@ import java.util.concurrent.ExecutionException
import javax.annotation.concurrent.ThreadSafe
/**
* A StateMachineManager is responsible for coordination and persistence of multiple [ProtocolStateMachine] objects.
* Each such object represents an instantiation of a (two-party) protocol that has reached a particular point.
* A StateMachineManager is responsible for coordination and persistence of multiple [FlowStateMachine] objects.
* Each such object represents an instantiation of a (two-party) flow that has reached a particular point.
*
* An implementation of this class will persist state machines to long term storage so they can survive process restarts
* and, if run with a single-threaded executor, will ensure no two state machines run concurrently with each other
@ -51,7 +51,7 @@ import javax.annotation.concurrent.ThreadSafe
* A "state machine" is a class with a single call method. The call method and any others it invokes are rewritten by
* a bytecode rewriting engine called Quasar, to ensure the code can be suspended and resumed at any point.
*
* The SMM will always invoke the protocol fibers on the given [AffinityExecutor], regardless of which thread actually
* The SMM will always invoke the flow fibers on the given [AffinityExecutor], regardless of which thread actually
* starts them via [add].
*
* TODO: Consider the issue of continuation identity more deeply: is it a safe assumption that a serialised
@ -79,7 +79,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
val scheduler = FiberScheduler()
data class Change(
val logic: ProtocolLogic<*>,
val logic: FlowLogic<*>,
val addOrRemove: AddOrRemove,
val id: StateMachineRunId
)
@ -88,10 +88,10 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
// property.
private val mutex = ThreadBox(object {
var started = false
val stateMachines = LinkedHashMap<ProtocolStateMachineImpl<*>, Checkpoint>()
val stateMachines = LinkedHashMap<FlowStateMachineImpl<*>, Checkpoint>()
val changesPublisher = PublishSubject.create<Change>()
fun notifyChangeObservers(psm: ProtocolStateMachineImpl<*>, addOrRemove: AddOrRemove) {
fun notifyChangeObservers(psm: FlowStateMachineImpl<*>, addOrRemove: AddOrRemove) {
changesPublisher.onNext(Change(psm.logic, addOrRemove, psm.id))
}
})
@ -105,35 +105,35 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
private val metrics = serviceHub.monitoringService.metrics
init {
metrics.register("Protocols.InFlight", Gauge<Int> { mutex.content.stateMachines.size })
metrics.register("Flows.InFlight", Gauge<Int> { mutex.content.stateMachines.size })
}
private val checkpointingMeter = metrics.meter("Protocols.Checkpointing Rate")
private val totalStartedProtocols = metrics.counter("Protocols.Started")
private val totalFinishedProtocols = metrics.counter("Protocols.Finished")
private val checkpointingMeter = metrics.meter("Flows.Checkpointing Rate")
private val totalStartedFlows = metrics.counter("Flows.Started")
private val totalFinishedFlows = metrics.counter("Flows.Finished")
private val openSessions = ConcurrentHashMap<Long, ProtocolSession>()
private val openSessions = ConcurrentHashMap<Long, FlowSession>()
private val recentlyClosedSessions = ConcurrentHashMap<Long, Party>()
// Context for tokenized services in checkpoints
private val serializationContext = SerializeAsTokenContext(tokenizableServices, quasarKryo())
/** Returns a list of all state machines executing the given protocol logic at the top level (subprotocols do not count) */
fun <P : ProtocolLogic<T>, T> findStateMachines(protocolClass: Class<P>): List<Pair<P, ListenableFuture<T>>> {
/** Returns a list of all state machines executing the given flow logic at the top level (subflows do not count) */
fun <P : FlowLogic<T>, T> findStateMachines(flowClass: Class<P>): List<Pair<P, ListenableFuture<T>>> {
@Suppress("UNCHECKED_CAST")
return mutex.locked {
stateMachines.keys
.map { it.logic }
.filterIsInstance(protocolClass)
.map { it to (it.psm as ProtocolStateMachineImpl<T>).resultFuture }
.filterIsInstance(flowClass)
.map { it to (it.fsm as FlowStateMachineImpl<T>).resultFuture }
}
}
val allStateMachines: List<ProtocolLogic<*>>
val allStateMachines: List<FlowLogic<*>>
get() = mutex.locked { stateMachines.keys.map { it.logic } }
/**
* An observable that emits triples of the changing protocol, the type of change, and a process-specific ID number
* An observable that emits triples of the changing flow, the type of change, and a process-specific ID number
* which may change across restarts.
*/
val changes: Observable<Change>
@ -141,7 +141,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
init {
Fiber.setDefaultUncaughtExceptionHandler { fiber, throwable ->
(fiber as ProtocolStateMachineImpl<*>).logger.error("Caught exception from protocol", throwable)
(fiber as FlowStateMachineImpl<*>).logger.error("Caught exception from flow", throwable)
}
}
@ -179,7 +179,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
* Atomic get snapshot + subscribe. This is needed so we don't miss updates between subscriptions to [changes] and
* calls to [allStateMachines]
*/
fun track(): Pair<List<ProtocolStateMachineImpl<*>>, Observable<Change>> {
fun track(): Pair<List<FlowStateMachineImpl<*>>, Observable<Change>> {
return mutex.locked {
val bufferedChanges = UnicastSubject.create<Change>()
changesPublisher.subscribe(bufferedChanges)
@ -190,7 +190,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
private fun restoreFibersFromCheckpoints() {
mutex.locked {
checkpointStorage.forEach {
// If a protocol is added before start() then don't attempt to restore it
// If a flow is added before start() then don't attempt to restore it
if (!stateMachines.containsValue(it)) {
val fiber = deserializeFiber(it.serializedFiber)
initFiber(fiber)
@ -216,7 +216,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
}
}
private fun resumeRestoredFiber(fiber: ProtocolStateMachineImpl<*>) {
private fun resumeRestoredFiber(fiber: FlowStateMachineImpl<*>) {
fiber.openSessions.values.forEach { openSessions[it.ourSessionId] = it }
if (fiber.openSessions.values.any { it.waitingForResponse }) {
fiber.logger.info("Restored, pending on receive")
@ -260,23 +260,23 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
val otherParty = sessionInit.initiatorParty
val otherPartySessionId = sessionInit.initiatorSessionId
try {
val markerClass = Class.forName(sessionInit.protocolName)
val protocolFactory = serviceHub.getProtocolFactory(markerClass)
if (protocolFactory != null) {
val protocol = protocolFactory(otherParty)
val psm = createFiber(protocol)
val session = ProtocolSession(protocol, otherParty, random63BitValue(), otherPartySessionId)
val markerClass = Class.forName(sessionInit.flowName)
val flowFactory = serviceHub.getFlowFactory(markerClass)
if (flowFactory != null) {
val flow = flowFactory(otherParty)
val psm = createFiber(flow)
val session = FlowSession(flow, otherParty, random63BitValue(), otherPartySessionId)
if (sessionInit.firstPayload != null) {
session.receivedMessages += SessionData(session.ourSessionId, sessionInit.firstPayload)
}
openSessions[session.ourSessionId] = session
psm.openSessions[Pair(protocol, otherParty)] = session
psm.openSessions[Pair(flow, otherParty)] = session
updateCheckpoint(psm)
sendSessionMessage(otherParty, SessionConfirm(otherPartySessionId, session.ourSessionId), psm)
psm.logger.debug { "Initiated from $sessionInit on $session" }
startFiber(psm)
} else {
logger.warn("Unknown protocol marker class in $sessionInit")
logger.warn("Unknown flow marker class in $sessionInit")
sendSessionMessage(otherParty, SessionReject(otherPartySessionId, "Don't know ${markerClass.name}"), null)
}
} catch (e: Exception) {
@ -285,14 +285,14 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
}
}
private fun serializeFiber(fiber: ProtocolStateMachineImpl<*>): SerializedBytes<ProtocolStateMachineImpl<*>> {
private fun serializeFiber(fiber: FlowStateMachineImpl<*>): SerializedBytes<FlowStateMachineImpl<*>> {
val kryo = quasarKryo()
// add the map of tokens -> tokenizedServices to the kyro context
SerializeAsTokenSerializer.setContext(kryo, serializationContext)
return fiber.serialize(kryo)
}
private fun deserializeFiber(serialisedFiber: SerializedBytes<ProtocolStateMachineImpl<*>>): ProtocolStateMachineImpl<*> {
private fun deserializeFiber(serialisedFiber: SerializedBytes<FlowStateMachineImpl<*>>): FlowStateMachineImpl<*> {
val kryo = quasarKryo()
// put the map of token -> tokenized into the kryo context
SerializeAsTokenSerializer.setContext(kryo, serializationContext)
@ -304,12 +304,12 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
return createKryo(serializer.kryo)
}
private fun <T> createFiber(logic: ProtocolLogic<T>): ProtocolStateMachineImpl<T> {
private fun <T> createFiber(logic: FlowLogic<T>): FlowStateMachineImpl<T> {
val id = StateMachineRunId.createRandom()
return ProtocolStateMachineImpl(id, logic, scheduler).apply { initFiber(this) }
return FlowStateMachineImpl(id, logic, scheduler).apply { initFiber(this) }
}
private fun initFiber(psm: ProtocolStateMachineImpl<*>) {
private fun initFiber(psm: FlowStateMachineImpl<*>) {
psm.database = database
psm.serviceHub = serviceHub
psm.actionOnSuspend = { ioRequest ->
@ -325,7 +325,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
psm.logic.progressTracker?.currentStep = ProgressTracker.DONE
mutex.locked {
stateMachines.remove(psm)?.let { checkpointStorage.removeCheckpoint(it) }
totalFinishedProtocols.inc()
totalFinishedFlows.inc()
notifyChangeObservers(psm, AddOrRemove.REMOVE)
}
endAllFiberSessions(psm)
@ -334,12 +334,12 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
}
}
mutex.locked {
totalStartedProtocols.inc()
totalStartedFlows.inc()
notifyChangeObservers(psm, AddOrRemove.ADD)
}
}
private fun endAllFiberSessions(psm: ProtocolStateMachineImpl<*>) {
private fun endAllFiberSessions(psm: FlowStateMachineImpl<*>) {
openSessions.values.removeIf { session ->
if (session.psm == psm) {
val otherPartySessionId = session.otherPartySessionId
@ -354,17 +354,17 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
}
}
private fun startFiber(fiber: ProtocolStateMachineImpl<*>) {
private fun startFiber(fiber: FlowStateMachineImpl<*>) {
try {
resumeFiber(fiber)
} catch (e: ExecutionException) {
// There are two ways we can take exceptions in this method:
//
// 1) A bug in the SMM code itself whilst setting up the new protocol. In that case the exception will
// 1) A bug in the SMM code itself whilst setting up the new flow. In that case the exception will
// propagate out of this method as it would for any method.
//
// 2) An exception in the first part of the fiber after it's been invoked for the first time via
// fiber.start(). In this case the exception will be caught and stashed in the protocol logic future,
// fiber.start(). In this case the exception will be caught and stashed in the flow logic future,
// then sent to the unhandled exception handler above which logs it, and is then rethrown wrapped
// in an ExecutionException or RuntimeException+EE so we can just catch it here and ignore it.
} catch (e: RuntimeException) {
@ -378,10 +378,10 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
* The state machine will be persisted when it suspends, with automated restart if the StateMachineManager is
* restarted with checkpointed state machines in the storage service.
*/
fun <T> add(logic: ProtocolLogic<T>): ProtocolStateMachine<T> {
fun <T> add(logic: FlowLogic<T>): FlowStateMachine<T> {
val fiber = createFiber(logic)
// We swap out the parent transaction context as using this frequently leads to a deadlock as we wait
// on the protocol completion future inside that context. The problem is that any progress checkpoints are
// on the flow completion future inside that context. The problem is that any progress checkpoints are
// unable to acquire the table lock and move forward till the calling transaction finishes.
// Committing in line here on a fresh context ensure we can progress.
isolatedTransaction(database) {
@ -396,7 +396,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
return fiber
}
private fun updateCheckpoint(psm: ProtocolStateMachineImpl<*>) {
private fun updateCheckpoint(psm: FlowStateMachineImpl<*>) {
check(psm.state != Strand.State.RUNNING) { "Fiber cannot be running when checkpointing" }
val newCheckpoint = Checkpoint(serializeFiber(psm))
val previousCheckpoint = mutex.locked { stateMachines.put(psm, newCheckpoint) }
@ -407,7 +407,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
checkpointingMeter.mark()
}
private fun resumeFiber(psm: ProtocolStateMachineImpl<*>) {
private fun resumeFiber(psm: FlowStateMachineImpl<*>) {
// Avoid race condition when setting stopping to true and then checking liveFibers
incrementLiveFibers()
if (!stopping) executor.executeASAP {
@ -418,7 +418,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
}
}
private fun processIORequest(ioRequest: ProtocolIORequest) {
private fun processIORequest(ioRequest: FlowIORequest) {
if (ioRequest is SendRequest) {
if (ioRequest.message is SessionInit) {
openSessions[ioRequest.session.ourSessionId] = ioRequest.session
@ -431,7 +431,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
}
}
private fun sendSessionMessage(party: Party, message: SessionMessage, psm: ProtocolStateMachineImpl<*>?) {
private fun sendSessionMessage(party: Party, message: SessionMessage, psm: FlowStateMachineImpl<*>?) {
val node = serviceHub.networkMapCache.getNodeByCompositeKey(party.owningKey)
?: throw IllegalArgumentException("Don't know about party $party")
val logger = psm?.logger ?: logger
@ -448,7 +448,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
data class SessionInit(val initiatorSessionId: Long,
val initiatorParty: Party,
val protocolName: String,
val flowName: String,
val firstPayload: Any?) : SessionMessage
interface SessionInitResponse : ExistingSessionMessage
@ -470,14 +470,14 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
data class SessionEnd(override val recipientSessionId: Long) : ExistingSessionMessage
data class ProtocolSession(val protocol: ProtocolLogic<*>,
data class FlowSession(val flow: FlowLogic<*>,
val otherParty: Party,
val ourSessionId: Long,
var otherPartySessionId: Long?,
@Volatile var waitingForResponse: Boolean = false) {
val receivedMessages = ConcurrentLinkedQueue<ExistingSessionMessage>()
val psm: ProtocolStateMachineImpl<*> get() = protocol.psm as ProtocolStateMachineImpl<*>
val psm: FlowStateMachineImpl<*> get() = flow.fsm as FlowStateMachineImpl<*>
}

View File

@ -1,11 +1,9 @@
package net.corda.node.services.transactions
import net.corda.core.crypto.Party
import net.corda.core.node.services.ServiceType
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.flows.NotaryFlow
import net.corda.node.services.api.ServiceHubInternal
import net.corda.protocols.NotaryProtocol
import kotlin.reflect.KClass
/**
* A Notary service acts as the final signer of a transaction ensuring two things:
@ -14,15 +12,15 @@ import kotlin.reflect.KClass
*O
* A transaction has to be signed by a Notary to be considered valid (except for output-only transactions without a timestamp).
*
* This is the base implementation that can be customised with specific Notary transaction commit protocol.
* This is the base implementation that can be customised with specific Notary transaction commit flow.
*/
abstract class NotaryService(services: ServiceHubInternal) : SingletonSerializeAsToken() {
init {
services.registerProtocolInitiator(NotaryProtocol.Client::class) { createProtocol(it) }
services.registerFlowInitiator(NotaryFlow.Client::class) { createFlow(it) }
}
/** Implement a factory that specifies the transaction commit protocol for the notary service to use */
abstract fun createProtocol(otherParty: Party): NotaryProtocol.Service
/** Implement a factory that specifies the transaction commit flow for the notary service to use */
abstract fun createFlow(otherParty: Party): NotaryFlow.Service
}

View File

@ -2,8 +2,8 @@ package net.corda.node.services.transactions
import net.corda.core.crypto.Party
import net.corda.core.node.services.TimestampChecker
import net.corda.flows.ValidatingNotaryFlow
import net.corda.node.services.api.ServiceHubInternal
import net.corda.protocols.ValidatingNotaryProtocol
/** A validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
class RaftValidatingNotaryService(services: ServiceHubInternal,
@ -13,7 +13,7 @@ class RaftValidatingNotaryService(services: ServiceHubInternal,
val type = ValidatingNotaryService.type.getSubType("raft")
}
override fun createProtocol(otherParty: Party): ValidatingNotaryProtocol {
return ValidatingNotaryProtocol(otherParty, timestampChecker, uniquenessProvider)
override fun createFlow(otherParty: Party): ValidatingNotaryFlow {
return ValidatingNotaryFlow(otherParty, timestampChecker, uniquenessProvider)
}
}

View File

@ -4,9 +4,8 @@ import net.corda.core.crypto.Party
import net.corda.core.node.services.ServiceType
import net.corda.core.node.services.TimestampChecker
import net.corda.core.node.services.UniquenessProvider
import net.corda.core.utilities.loggerFor
import net.corda.flows.NotaryFlow
import net.corda.node.services.api.ServiceHubInternal
import net.corda.protocols.NotaryProtocol
/** A simple Notary service that does not perform transaction validation */
class SimpleNotaryService(services: ServiceHubInternal,
@ -16,7 +15,7 @@ class SimpleNotaryService(services: ServiceHubInternal,
val type = ServiceType.notary.getSubType("simple")
}
override fun createProtocol(otherParty: Party): NotaryProtocol.Service {
return NotaryProtocol.Service(otherParty, timestampChecker, uniquenessProvider)
override fun createFlow(otherParty: Party): NotaryFlow.Service {
return NotaryFlow.Service(otherParty, timestampChecker, uniquenessProvider)
}
}

View File

@ -4,8 +4,8 @@ import net.corda.core.crypto.Party
import net.corda.core.node.services.ServiceType
import net.corda.core.node.services.TimestampChecker
import net.corda.core.node.services.UniquenessProvider
import net.corda.flows.ValidatingNotaryFlow
import net.corda.node.services.api.ServiceHubInternal
import net.corda.protocols.ValidatingNotaryProtocol
/** A Notary service that validates the transaction chain of he submitted transaction before committing it */
class ValidatingNotaryService(services: ServiceHubInternal,
@ -15,7 +15,7 @@ class ValidatingNotaryService(services: ServiceHubInternal,
val type = ServiceType.notary.getSubType("validating")
}
override fun createProtocol(otherParty: Party): ValidatingNotaryProtocol {
return ValidatingNotaryProtocol(otherParty, timestampChecker, uniquenessProvider)
override fun createFlow(otherParty: Party): ValidatingNotaryFlow {
return ValidatingNotaryFlow(otherParty, timestampChecker, uniquenessProvider)
}
}

View File

@ -1,28 +1,28 @@
package net.corda.node.utilities
import net.corda.core.ThreadBox
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.flows.FlowLogic
import net.corda.core.utilities.ProgressTracker
import net.corda.node.services.statemachine.StateMachineManager
import java.util.*
/**
* This observes the [StateMachineManager] and follows the progress of [ProtocolLogic]s until they complete in the order
* This observes the [StateMachineManager] and follows the progress of [FlowLogic]s until they complete in the order
* they are added to the [StateMachineManager].
*/
class ANSIProgressObserver(val smm: StateMachineManager) {
init {
smm.changes.subscribe { change ->
when (change.addOrRemove) {
AddOrRemove.ADD -> addProtocolLogic(change.logic)
AddOrRemove.REMOVE -> removeProtocolLogic(change.logic)
AddOrRemove.ADD -> addFlowLogic(change.logic)
AddOrRemove.REMOVE -> removeFlowLogic(change.logic)
}
}
}
private class Content {
var currentlyRendering: ProtocolLogic<*>? = null
val pending = ArrayDeque<ProtocolLogic<*>>()
var currentlyRendering: FlowLogic<*>? = null
val pending = ArrayDeque<FlowLogic<*>>()
}
private val state = ThreadBox(Content())
@ -39,18 +39,18 @@ class ANSIProgressObserver(val smm: StateMachineManager) {
}
}
private fun removeProtocolLogic(protocolLogic: ProtocolLogic<*>) {
private fun removeFlowLogic(flowLogic: FlowLogic<*>) {
state.locked {
protocolLogic.progressTracker?.currentStep = ProgressTracker.DONE
if (currentlyRendering == protocolLogic) {
flowLogic.progressTracker?.currentStep = ProgressTracker.DONE
if (currentlyRendering == flowLogic) {
wireUpProgressRendering()
}
}
}
private fun addProtocolLogic(protocolLogic: ProtocolLogic<*>) {
private fun addFlowLogic(flowLogic: FlowLogic<*>) {
state.locked {
pending.add(protocolLogic)
pending.add(flowLogic)
if ((currentlyRendering?.progressTracker?.currentStep ?: ProgressTracker.DONE) == ProgressTracker.DONE) {
wireUpProgressRendering()
}

View File

@ -27,9 +27,9 @@ import kotlin.concurrent.withLock
* or testing.
*
* Currently this is intended for use within a node as a simplified way for Oracles to implement subscriptions for changing
* data by running a protocol internally to implement the request handler (see [NodeInterestRates.Oracle]), which can then
* data by running a flow internally to implement the request handler (see [NodeInterestRates.Oracle]), which can then
* effectively relinquish control until the data becomes available. This isn't the most scalable design and is intended
* to be temporary. In addition, it's enitrely possible to envisage a time when we want public [ProtocolLogic]
* to be temporary. In addition, it's enitrely possible to envisage a time when we want public [FlowLogic]
* implementations to be able to wait for some condition to become true outside of message send/receive. At that point
* we may revisit this implementation and indeed the whole model for this, when we understand that requirement more fully.
*

View File

@ -2,23 +2,23 @@ package net.corda.node
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.*
import net.corda.core.flows.StateMachineRunId
import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.Vault
import net.corda.core.protocols.StateMachineRunId
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.SignedTransaction
import net.corda.flows.CashCommand
import net.corda.flows.CashFlow
import net.corda.node.internal.CordaRPCOpsImpl
import net.corda.node.services.User
import net.corda.node.services.messaging.CURRENT_RPC_USER
import net.corda.node.services.messaging.PermissionException
import net.corda.node.services.messaging.StateMachineUpdate
import net.corda.node.services.messaging.startProtocol
import net.corda.node.services.messaging.startFlow
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.startProtocolPermission
import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.node.utilities.databaseTransaction
import net.corda.protocols.CashCommand
import net.corda.protocols.CashProtocol
import net.corda.testing.expect
import net.corda.testing.expectEvents
import net.corda.testing.node.MockNetwork
@ -48,7 +48,7 @@ class CordaRPCOpsImplTest {
aliceNode = network.createNode(networkMapAddress = networkMap.info.address)
notaryNode = network.createNode(advertisedServices = ServiceInfo(SimpleNotaryService.type), networkMapAddress = networkMap.info.address)
rpc = CordaRPCOpsImpl(aliceNode.services, aliceNode.smm, aliceNode.database)
CURRENT_RPC_USER.set(User("user", "pwd", permissions = setOf(startProtocolPermission<CashProtocol>())))
CURRENT_RPC_USER.set(User("user", "pwd", permissions = setOf(startFlowPermission<CashFlow>())))
stateMachineUpdates = rpc.stateMachinesAndUpdates().second
transactions = rpc.verifiedTransactions().second
@ -68,7 +68,7 @@ class CordaRPCOpsImplTest {
// Tell the monitoring service node to issue some cash
val recipient = aliceNode.info.legalIdentity
val outEvent = CashCommand.IssueCash(Amount(quantity, GBP), ref, recipient, notaryNode.info.notaryIdentity)
rpc.startProtocol(::CashProtocol, outEvent)
rpc.startFlow(::CashFlow, outEvent)
network.runNetwork()
val expectedState = Cash.State(Amount(quantity,
@ -105,7 +105,7 @@ class CordaRPCOpsImplTest {
@Test
fun `issue and move`() {
rpc.startProtocol(::CashProtocol, CashCommand.IssueCash(
rpc.startFlow(::CashFlow, CashCommand.IssueCash(
amount = Amount(100, USD),
issueRef = OpaqueBytes(ByteArray(1, { 1 })),
recipient = aliceNode.info.legalIdentity,
@ -114,7 +114,7 @@ class CordaRPCOpsImplTest {
network.runNetwork()
rpc.startProtocol(::CashProtocol, CashCommand.PayCash(
rpc.startFlow(::CashFlow, CashCommand.PayCash(
amount = Amount(100, Issued(PartyAndReference(aliceNode.info.legalIdentity, OpaqueBytes(ByteArray(1, { 1 }))), USD)),
recipient = aliceNode.info.legalIdentity
))
@ -186,7 +186,7 @@ class CordaRPCOpsImplTest {
fun `cash command by user not permissioned for cash`() {
CURRENT_RPC_USER.set(User("user", "pwd", permissions = emptySet()))
assertThatExceptionOfType(PermissionException::class.java).isThrownBy {
rpc.startProtocol(::CashProtocol, CashCommand.IssueCash(
rpc.startFlow(::CashFlow, CashCommand.IssueCash(
amount = Amount(100, USD),
issueRef = OpaqueBytes(ByteArray(1, { 1 })),
recipient = aliceNode.info.legalIdentity,

View File

@ -6,12 +6,12 @@ import net.corda.core.crypto.sha256
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.services.ServiceInfo
import net.corda.core.write
import net.corda.flows.FetchAttachmentsFlow
import net.corda.flows.FetchDataFlow
import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.protocols.FetchAttachmentsProtocol
import net.corda.protocols.FetchDataProtocol
import net.corda.testing.node.MockNetwork
import net.corda.testing.rootCauseExceptions
import org.junit.Before
@ -49,9 +49,9 @@ class AttachmentTests {
// Insert an attachment into node zero's store directly.
val id = n0.storage.attachments.importAttachment(ByteArrayInputStream(fakeAttachment()))
// Get node one to run a protocol to fetch it and insert it.
// Get node one to run a flow to fetch it and insert it.
network.runNetwork()
val f1 = n1.services.startProtocol(FetchAttachmentsProtocol(setOf(id), n0.info.legalIdentity))
val f1 = n1.services.startFlow(FetchAttachmentsFlow(setOf(id), n0.info.legalIdentity))
network.runNetwork()
assertEquals(0, f1.resultFuture.get().fromDisk.size)
@ -62,7 +62,7 @@ class AttachmentTests {
// Shut down node zero and ensure node one can still resolve the attachment.
n0.stop()
val response: FetchDataProtocol.Result<Attachment> = n1.services.startProtocol(FetchAttachmentsProtocol(setOf(id), n0.info.legalIdentity)).resultFuture.get()
val response: FetchDataFlow.Result<Attachment> = n1.services.startFlow(FetchAttachmentsFlow(setOf(id), n0.info.legalIdentity)).resultFuture.get()
assertEquals(attachment, response.fromDisk[0])
}
@ -73,9 +73,9 @@ class AttachmentTests {
// Get node one to fetch a non-existent attachment.
val hash = SecureHash.randomSHA256()
network.runNetwork()
val f1 = n1.services.startProtocol(FetchAttachmentsProtocol(setOf(hash), n0.info.legalIdentity))
val f1 = n1.services.startFlow(FetchAttachmentsFlow(setOf(hash), n0.info.legalIdentity))
network.runNetwork()
val e = assertFailsWith<FetchDataProtocol.HashNotFound> { rootCauseExceptions { f1.resultFuture.get() } }
val e = assertFailsWith<FetchDataFlow.HashNotFound> { rootCauseExceptions { f1.resultFuture.get() } }
assertEquals(hash, e.requested)
}
@ -104,9 +104,9 @@ class AttachmentTests {
// Get n1 to fetch the attachment. Should receive corrupted bytes.
network.runNetwork()
val f1 = n1.services.startProtocol(FetchAttachmentsProtocol(setOf(id), n0.info.legalIdentity))
val f1 = n1.services.startFlow(FetchAttachmentsFlow(setOf(id), n0.info.legalIdentity))
network.runNetwork()
assertFailsWith<FetchDataProtocol.DownloadedVsRequestedDataMismatch> {
assertFailsWith<FetchDataFlow.DownloadedVsRequestedDataMismatch> {
rootCauseExceptions { f1.resultFuture.get() }
}
}

View File

@ -9,11 +9,11 @@ import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.composite
import net.corda.core.days
import net.corda.core.flows.FlowStateMachine
import net.corda.core.flows.StateMachineRunId
import net.corda.core.map
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.services.*
import net.corda.core.protocols.ProtocolStateMachine
import net.corda.core.protocols.StateMachineRunId
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction
@ -21,6 +21,8 @@ import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.core.utilities.LogHelper
import net.corda.core.utilities.TEST_TX_TIME
import net.corda.flows.TwoPartyTradeFlow.Buyer
import net.corda.flows.TwoPartyTradeFlow.Seller
import net.corda.node.internal.AbstractNode
import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.persistence.DBTransactionStorage
@ -28,8 +30,6 @@ import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.node.services.persistence.StorageServiceImpl
import net.corda.node.services.persistence.checkpoints
import net.corda.node.utilities.databaseTransaction
import net.corda.protocols.TwoPartyTradeProtocol.Buyer
import net.corda.protocols.TwoPartyTradeProtocol.Seller
import net.corda.testing.*
import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.MockNetwork
@ -58,7 +58,7 @@ import kotlin.test.assertTrue
*
* We assume that Alice and Bob already found each other via some market, and have agreed the details already.
*/
class TwoPartyTradeProtocolTests {
class TwoPartyTradeFlowTests {
lateinit var net: MockNetwork
lateinit var notaryNode: MockNetwork.MockNode
@ -146,7 +146,7 @@ class TwoPartyTradeProtocolTests {
insertFakeTransactions(alicesFakePaper, aliceNode, aliceKey, notaryKey)
val aliceFuture = runBuyerAndSeller("alice's paper".outputStateAndRef()).sellerResult
// Everything is on this thread so we can now step through the protocol one step at a time.
// Everything is on this thread so we can now step through the flow one step at a time.
// Seller Alice already sent a message to Buyer Bob. Pump once:
bobNode.pumpReceive()
@ -408,18 +408,18 @@ class TwoPartyTradeProtocolTests {
private data class RunResult(
// The buyer is not created immediately, only when the seller starts running
val buyer: Future<ProtocolStateMachine<*>>,
val buyer: Future<FlowStateMachine<*>>,
val sellerResult: Future<SignedTransaction>,
val sellerId: StateMachineRunId
)
private fun runBuyerAndSeller(assetToSell: StateAndRef<OwnableState>) : RunResult {
val buyerFuture = bobNode.initiateSingleShotProtocol(Seller::class) { otherParty ->
val buyerFuture = bobNode.initiateSingleShotFlow(Seller::class) { otherParty ->
Buyer(otherParty, notaryNode.info.notaryIdentity, 1000.DOLLARS, CommercialPaper.State::class.java)
}.map { it.psm }
}.map { it.fsm }
val seller = Seller(bobNode.info.legalIdentity, notaryNode.info, assetToSell, 1000.DOLLARS, ALICE_KEY)
val sellerResultFuture = aliceNode.smm.add(seller).resultFuture
return RunResult(buyerFuture, sellerResultFuture, seller.psm.id)
return RunResult(buyerFuture, sellerResultFuture, seller.fsm.id)
}
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.runWithError(

View File

@ -152,7 +152,7 @@ class ArtemisMessagingTests {
val message = messagingClient.createMessage(topic, DEFAULT_SESSION_ID, "first msg".toByteArray())
messagingClient.send(message, messagingClient.myAddress)
val networkMapMessage = messagingClient.createMessage(NetworkMapService.FETCH_PROTOCOL_TOPIC, DEFAULT_SESSION_ID, "second msg".toByteArray())
val networkMapMessage = messagingClient.createMessage(NetworkMapService.FETCH_FLOW_TOPIC, DEFAULT_SESSION_ID, "second msg".toByteArray())
messagingClient.send(networkMapMessage, messagingClient.myAddress)
val actual: Message = receivedMessages.take()
@ -179,7 +179,7 @@ class ArtemisMessagingTests {
messagingClient.send(message, messagingClient.myAddress)
}
val networkMapMessage = messagingClient.createMessage(NetworkMapService.FETCH_PROTOCOL_TOPIC, DEFAULT_SESSION_ID, "second msg".toByteArray())
val networkMapMessage = messagingClient.createMessage(NetworkMapService.FETCH_FLOW_TOPIC, DEFAULT_SESSION_ID, "second msg".toByteArray())
messagingClient.send(networkMapMessage, messagingClient.myAddress)
val actual: Message = receivedMessages.take()
@ -212,7 +212,7 @@ class ArtemisMessagingTests {
messagingClient.addMessageHandler(topic) { message, r ->
receivedMessages.add(message)
}
messagingClient.addMessageHandler(NetworkMapService.FETCH_PROTOCOL_TOPIC) { message, r ->
messagingClient.addMessageHandler(NetworkMapService.FETCH_FLOW_TOPIC) { message, r ->
receivedMessages.add(message)
}
// Run after the handlers are added, otherwise (some of) the messages get delivered and discarded / dead-lettered.

View File

@ -4,17 +4,17 @@ import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.map
import net.corda.core.messaging.send
import net.corda.core.node.services.DEFAULT_SESSION_ID
import net.corda.flows.sendRequest
import net.corda.node.services.network.AbstractNetworkMapService
import net.corda.node.services.network.InMemoryNetworkMapService
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.network.NetworkMapService.*
import net.corda.node.services.network.NetworkMapService.Companion.FETCH_PROTOCOL_TOPIC
import net.corda.node.services.network.NetworkMapService.Companion.PUSH_ACK_PROTOCOL_TOPIC
import net.corda.node.services.network.NetworkMapService.Companion.REGISTER_PROTOCOL_TOPIC
import net.corda.node.services.network.NetworkMapService.Companion.SUBSCRIPTION_PROTOCOL_TOPIC
import net.corda.node.services.network.NetworkMapService.Companion.FETCH_FLOW_TOPIC
import net.corda.node.services.network.NetworkMapService.Companion.PUSH_ACK_FLOW_TOPIC
import net.corda.node.services.network.NetworkMapService.Companion.REGISTER_FLOW_TOPIC
import net.corda.node.services.network.NetworkMapService.Companion.SUBSCRIPTION_FLOW_TOPIC
import net.corda.node.services.network.NodeRegistration
import net.corda.node.utilities.AddOrRemove
import net.corda.protocols.sendRequest
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetwork.MockNode
import org.junit.Before
@ -165,23 +165,23 @@ abstract class AbstractNetworkMapServiceTest {
private fun MockNode.registration(mapServiceNode: MockNode, reg: NodeRegistration, privateKey: PrivateKey): ListenableFuture<RegistrationResponse> {
val req = RegistrationRequest(reg.toWire(privateKey), services.networkService.myAddress)
return services.networkService.sendRequest(REGISTER_PROTOCOL_TOPIC, req, mapServiceNode.info.address)
return services.networkService.sendRequest(REGISTER_FLOW_TOPIC, req, mapServiceNode.info.address)
}
private fun MockNode.subscribe(mapServiceNode: MockNode, subscribe: Boolean): ListenableFuture<SubscribeResponse> {
val req = SubscribeRequest(subscribe, services.networkService.myAddress)
return services.networkService.sendRequest(SUBSCRIPTION_PROTOCOL_TOPIC, req, mapServiceNode.info.address)
return services.networkService.sendRequest(SUBSCRIPTION_FLOW_TOPIC, req, mapServiceNode.info.address)
}
private fun MockNode.updateAcknowlege(mapServiceNode: MockNode, mapVersion: Int) {
val req = UpdateAcknowledge(mapVersion, services.networkService.myAddress)
services.networkService.send(PUSH_ACK_PROTOCOL_TOPIC, DEFAULT_SESSION_ID, req, mapServiceNode.info.address)
services.networkService.send(PUSH_ACK_FLOW_TOPIC, DEFAULT_SESSION_ID, req, mapServiceNode.info.address)
}
private fun MockNode.fetchMap(mapServiceNode: MockNode, subscribe: Boolean, ifChangedSinceVersion: Int? = null): Future<Collection<NodeRegistration>?> {
val net = services.networkService
val req = FetchMapRequest(subscribe, ifChangedSinceVersion, net.myAddress)
return net.sendRequest<FetchMapResponse>(FETCH_PROTOCOL_TOPIC, req, mapServiceNode.info.address).map { it.nodes }
return net.sendRequest<FetchMapResponse>(FETCH_FLOW_TOPIC, req, mapServiceNode.info.address).map { it.nodes }
}
}

View File

@ -2,11 +2,11 @@ package net.corda.node.services
import com.codahale.metrics.MetricRegistry
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.flows.FlowStateMachine
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.*
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.protocols.ProtocolLogicRefFactory
import net.corda.core.protocols.ProtocolStateMachine
import net.corda.core.transactions.SignedTransaction
import net.corda.node.serialization.NodeClock
import net.corda.node.services.api.MessagingServiceInternal
@ -34,7 +34,7 @@ open class MockServiceHubInternal(
val mapCache: NetworkMapCache? = MockNetworkMapCache(),
val scheduler: SchedulerService? = null,
val overrideClock: Clock? = NodeClock(),
val protocolFactory: ProtocolLogicRefFactory? = ProtocolLogicRefFactory(),
val flowFactory: FlowLogicRefFactory? = FlowLogicRefFactory(),
val schemas: SchemaService? = NodeSchemaService()
) : ServiceHubInternal() {
override val vaultService: VaultService = customVault ?: NodeVaultService(this)
@ -56,8 +56,8 @@ open class MockServiceHubInternal(
get() = throw UnsupportedOperationException()
override val monitoringService: MonitoringService = MonitoringService(MetricRegistry())
override val protocolLogicRefFactory: ProtocolLogicRefFactory
get() = protocolFactory ?: throw UnsupportedOperationException()
override val flowLogicRefFactory: FlowLogicRefFactory
get() = flowFactory ?: throw UnsupportedOperationException()
override val schemaService: SchemaService
get() = schemas ?: throw UnsupportedOperationException()
@ -65,7 +65,7 @@ open class MockServiceHubInternal(
private val txStorageService: TxWritableStorageService
get() = storage ?: throw UnsupportedOperationException()
private val protocolFactories = ConcurrentHashMap<Class<*>, (Party) -> ProtocolLogic<*>>()
private val flowFactories = ConcurrentHashMap<Class<*>, (Party) -> FlowLogic<*>>()
lateinit var smm: StateMachineManager
@ -79,13 +79,13 @@ open class MockServiceHubInternal(
override fun recordTransactions(txs: Iterable<SignedTransaction>) = recordTransactionsInternal(txStorageService, txs)
override fun <T> startProtocol(logic: ProtocolLogic<T>): ProtocolStateMachine<T> = smm.add(logic)
override fun <T> startFlow(logic: FlowLogic<T>): FlowStateMachine<T> = smm.add(logic)
override fun registerProtocolInitiator(markerClass: KClass<*>, protocolFactory: (Party) -> ProtocolLogic<*>) {
protocolFactories[markerClass.java] = protocolFactory
override fun registerFlowInitiator(markerClass: KClass<*>, flowFactory: (Party) -> FlowLogic<*>) {
flowFactories[markerClass.java] = flowFactory
}
override fun getProtocolFactory(markerClass: Class<*>): ((Party) -> ProtocolLogic<*>)? {
return protocolFactories[markerClass]
override fun getFlowFactory(markerClass: Class<*>): ((Party) -> FlowLogic<*>)? {
return flowFactories[markerClass]
}
}

View File

@ -4,11 +4,11 @@ import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.composite
import net.corda.core.days
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowLogicRef
import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.node.ServiceHub
import net.corda.core.node.recordTransactions
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.protocols.ProtocolLogicRef
import net.corda.core.protocols.ProtocolLogicRefFactory
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.node.services.events.NodeSchedulerService
@ -47,7 +47,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
// We have to allow Java boxed primitives but Kotlin warns we shouldn't be using them
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
val factory = ProtocolLogicRefFactory(mapOf(Pair(TestProtocolLogic::class.java.name, setOf(NodeSchedulerServiceTest::class.java.name, Integer::class.java.name))))
val factory = FlowLogicRefFactory(mapOf(Pair(TestFlowLogic::class.java.name, setOf(NodeSchedulerServiceTest::class.java.name, Integer::class.java.name))))
lateinit var services: MockServiceHubInternal
@ -56,12 +56,12 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
lateinit var dataSource: Closeable
lateinit var database: Database
lateinit var countDown: CountDownLatch
lateinit var smmHasRemovedAllProtocols: CountDownLatch
lateinit var smmHasRemovedAllFlows: CountDownLatch
var calls: Int = 0
/**
* Have a reference to this test added to [ServiceHub] so that when the [ProtocolLogic] runs it can access the test instance.
* Have a reference to this test added to [ServiceHub] so that when the [FlowLogic] runs it can access the test instance.
* The [TestState] is serialized and deserialized so attempting to use a transient field won't work, as it just
* results in NPE.
*/
@ -73,7 +73,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
@Before
fun setup() {
countDown = CountDownLatch(1)
smmHasRemovedAllProtocols = CountDownLatch(1)
smmHasRemovedAllFlows = CountDownLatch(1)
calls = 0
val dataSourceAndDatabase = configureDatabase(makeTestDataSourceProperties())
dataSource = dataSourceAndDatabase.first
@ -90,7 +90,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
val mockSMM = StateMachineManager(services, listOf(services, scheduler), DBCheckpointStorage(), smmExecutor, database)
mockSMM.changes.subscribe { change ->
if (change.addOrRemove == AddOrRemove.REMOVE && mockSMM.allStateMachines.isEmpty()) {
smmHasRemovedAllProtocols.countDown()
smmHasRemovedAllFlows.countDown()
}
}
mockSMM.start()
@ -103,14 +103,14 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
fun tearDown() {
// We need to make sure the StateMachineManager is done before shutting down executors.
if (services.smm.allStateMachines.isNotEmpty()) {
smmHasRemovedAllProtocols.await()
smmHasRemovedAllFlows.await()
}
smmExecutor.shutdown()
smmExecutor.awaitTermination(60, TimeUnit.SECONDS)
dataSource.close()
}
class TestState(val protocolLogicRef: ProtocolLogicRef, val instant: Instant) : LinearState, SchedulableState {
class TestState(val flowLogicRef: FlowLogicRef, val instant: Instant) : LinearState, SchedulableState {
override val participants: List<CompositeKey>
get() = throw UnsupportedOperationException()
@ -118,13 +118,13 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean = true
override fun nextScheduledActivity(thisStateRef: StateRef, protocolLogicRefFactory: ProtocolLogicRefFactory): ScheduledActivity? = ScheduledActivity(protocolLogicRef, instant)
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? = ScheduledActivity(flowLogicRef, instant)
override val contract: Contract
get() = throw UnsupportedOperationException()
}
class TestProtocolLogic(val increment: Int = 1) : ProtocolLogic<Unit>() {
class TestFlowLogic(val increment: Int = 1) : FlowLogic<Unit>() {
override fun call() {
(serviceHub as TestReference).testReference.calls += increment
(serviceHub as TestReference).testReference.countDown.countDown()
@ -267,7 +267,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
databaseTransaction(database) {
apply {
val freshKey = services.keyManagementService.freshKey()
val state = TestState(factory.create(TestProtocolLogic::class.java, increment), instant)
val state = TestState(factory.create(TestFlowLogic::class.java, increment), instant)
val usefulTX = TransactionType.General.Builder(null).apply {
addOutputState(state, DUMMY_NOTARY)
addCommand(Command(), freshKey.public.composite)

View File

@ -7,12 +7,12 @@ import net.corda.core.node.services.ServiceInfo
import net.corda.core.seconds
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.flows.NotaryChangeFlow.Instigator
import net.corda.flows.StateReplacementException
import net.corda.flows.StateReplacementRefused
import net.corda.node.internal.AbstractNode
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.protocols.NotaryChangeProtocol.Instigator
import net.corda.protocols.StateReplacementException
import net.corda.protocols.StateReplacementRefused
import net.corda.testing.node.MockNetwork
import org.junit.Before
import org.junit.Test
@ -48,8 +48,8 @@ class NotaryChangeTests {
fun `should change notary for a state with single participant`() {
val state = issueState(clientNodeA, oldNotaryNode)
val newNotary = newNotaryNode.info.notaryIdentity
val protocol = Instigator(state, newNotary)
val future = clientNodeA.services.startProtocol(protocol)
val flow = Instigator(state, newNotary)
val future = clientNodeA.services.startFlow(flow)
net.runNetwork()
@ -61,8 +61,8 @@ class NotaryChangeTests {
fun `should change notary for a state with multiple participants`() {
val state = issueMultiPartyState(clientNodeA, clientNodeB, oldNotaryNode)
val newNotary = newNotaryNode.info.notaryIdentity
val protocol = Instigator(state, newNotary)
val future = clientNodeA.services.startProtocol(protocol)
val flow = Instigator(state, newNotary)
val future = clientNodeA.services.startFlow(flow)
net.runNetwork()
@ -77,8 +77,8 @@ class NotaryChangeTests {
fun `should throw when a participant refuses to change Notary`() {
val state = issueMultiPartyState(clientNodeA, clientNodeB, oldNotaryNode)
val newEvilNotary = Party("Evil Notary", generateKeyPair().public)
val protocol = Instigator(state, newEvilNotary)
val future = clientNodeA.services.startProtocol(protocol)
val flow = Instigator(state, newEvilNotary)
val future = clientNodeA.services.startFlow(flow)
net.runNetwork()
@ -87,7 +87,7 @@ class NotaryChangeTests {
assertTrue(error is StateReplacementRefused)
}
// TODO: Add more test cases once we have a general protocol/service exception handling mechanism:
// TODO: Add more test cases once we have a general flow/service exception handling mechanism:
// - A participant is offline/can't be found on the network
// - The requesting party is not a participant
// - The requesting party wants to change additional state fields

View File

@ -11,12 +11,12 @@ import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.flows.NotaryError
import net.corda.flows.NotaryException
import net.corda.flows.NotaryFlow
import net.corda.node.internal.AbstractNode
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.protocols.NotaryError
import net.corda.protocols.NotaryException
import net.corda.protocols.NotaryProtocol
import net.corda.testing.MINI_CORP_KEY
import net.corda.testing.node.MockNetwork
import org.junit.Before
@ -94,10 +94,10 @@ class NotaryServiceTests {
tx.toSignedTransaction(false)
}
val firstSpend = NotaryProtocol.Client(stx)
val secondSpend = NotaryProtocol.Client(stx)
clientNode.services.startProtocol(firstSpend)
val future = clientNode.services.startProtocol(secondSpend)
val firstSpend = NotaryFlow.Client(stx)
val secondSpend = NotaryFlow.Client(stx)
clientNode.services.startFlow(firstSpend)
val future = clientNode.services.startFlow(secondSpend)
net.runNetwork()
@ -109,8 +109,8 @@ class NotaryServiceTests {
private fun runNotaryClient(stx: SignedTransaction): ListenableFuture<DigitalSignature.WithKey> {
val protocol = NotaryProtocol.Client(stx)
val future = clientNode.services.startProtocol(protocol).resultFuture
val flow = NotaryFlow.Client(stx)
val future = clientNode.services.startFlow(flow).resultFuture
net.runNetwork()
return future
}

View File

@ -8,12 +8,12 @@ import net.corda.core.node.services.ServiceInfo
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.flows.NotaryError
import net.corda.flows.NotaryException
import net.corda.flows.NotaryFlow
import net.corda.node.internal.AbstractNode
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.protocols.NotaryError
import net.corda.protocols.NotaryException
import net.corda.protocols.NotaryProtocol
import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.MINI_CORP_KEY
import net.corda.testing.node.MockNetwork
@ -79,8 +79,8 @@ class ValidatingNotaryServiceTests {
}
private fun runClient(stx: SignedTransaction): ListenableFuture<DigitalSignature.WithKey> {
val protocol = NotaryProtocol.Client(stx)
val future = clientNode.services.startProtocol(protocol).resultFuture
val flow = NotaryFlow.Client(stx)
val future = clientNode.services.startFlow(flow).resultFuture
net.runNetwork()
return future
}

View File

@ -7,12 +7,12 @@ import net.corda.core.contracts.Issued
import net.corda.core.contracts.TransactionType
import net.corda.core.contracts.USD
import net.corda.core.crypto.Party
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.flows.FlowLogic
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.flows.BroadcastTransactionFlow.NotifyTxRequest
import net.corda.node.services.persistence.DataVending.Service.NotifyTransactionHandler
import net.corda.node.utilities.databaseTransaction
import net.corda.protocols.BroadcastTransactionProtocol.NotifyTxRequest
import net.corda.testing.MEGA_CORP
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetwork.MockNode
@ -88,13 +88,13 @@ class DataVendingServiceTests {
}
private fun MockNode.sendNotifyTx(tx: SignedTransaction, walletServiceNode: MockNode) {
walletServiceNode.services.registerProtocolInitiator(NotifyTxProtocol::class, ::NotifyTransactionHandler)
services.startProtocol(NotifyTxProtocol(walletServiceNode.info.legalIdentity, tx))
walletServiceNode.services.registerFlowInitiator(NotifyTxFlow::class, ::NotifyTransactionHandler)
services.startFlow(NotifyTxFlow(walletServiceNode.info.legalIdentity, tx))
network.runNetwork()
}
private class NotifyTxProtocol(val otherParty: Party, val stx: SignedTransaction) : ProtocolLogic<Unit>() {
private class NotifyTxFlow(val otherParty: Party, val stx: SignedTransaction) : FlowLogic<Unit>() {
@Suspendable
override fun call() = send(otherParty, NotifyTxRequest(stx))
}

View File

@ -4,14 +4,14 @@ import co.paralleluniverse.fibers.Fiber
import co.paralleluniverse.fibers.Suspendable
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.crypto.Party
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.protocols.ProtocolSessionException
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSessionException
import net.corda.core.random63BitValue
import net.corda.core.serialization.deserialize
import net.corda.node.services.persistence.checkpoints
import net.corda.node.services.statemachine.StateMachineManager.*
import net.corda.node.utilities.databaseTransaction
import net.corda.testing.initiateSingleShotProtocol
import net.corda.testing.initiateSingleShotFlow
import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.InMemoryMessagingNetwork.MessageTransfer
import net.corda.testing.node.MockNetwork
@ -49,29 +49,29 @@ class StateMachineManagerTests {
}
@Test
fun `newly added protocol is preserved on restart`() {
node1.smm.add(NoOpProtocol(nonTerminating = true))
fun `newly added flow is preserved on restart`() {
node1.smm.add(NoOpFlow(nonTerminating = true))
node1.acceptableLiveFiberCountOnStop = 1
val restoredProtocol = node1.restartAndGetRestoredProtocol<NoOpProtocol>()
assertThat(restoredProtocol.protocolStarted).isTrue()
val restoredFlow = node1.restartAndGetRestoredFlow<NoOpFlow>()
assertThat(restoredFlow.flowStarted).isTrue()
}
@Test
fun `protocol can lazily use the serviceHub in its constructor`() {
val protocol = object : ProtocolLogic<Unit>() {
fun `flow can lazily use the serviceHub in its constructor`() {
val flow = object : FlowLogic<Unit>() {
val lazyTime by lazy { serviceHub.clock.instant() }
@Suspendable
override fun call() = Unit
}
node1.smm.add(protocol)
assertThat(protocol.lazyTime).isNotNull()
node1.smm.add(flow)
assertThat(flow.lazyTime).isNotNull()
}
@Test
fun `protocol restarted just after receiving payload`() {
node2.services.registerProtocolInitiator(SendProtocol::class) { ReceiveThenSuspendProtocol(it) }
fun `flow restarted just after receiving payload`() {
node2.services.registerFlowInitiator(SendFlow::class) { ReceiveThenSuspendFlow(it) }
val payload = random63BitValue()
node1.smm.add(SendProtocol(payload, node2.info.legalIdentity))
node1.smm.add(SendFlow(payload, node2.info.legalIdentity))
// We push through just enough messages to get only the payload sent
node2.pumpReceive()
@ -79,60 +79,60 @@ class StateMachineManagerTests {
node2.acceptableLiveFiberCountOnStop = 1
node2.stop()
net.runNetwork()
val restoredProtocol = node2.restartAndGetRestoredProtocol<ReceiveThenSuspendProtocol>(node1)
assertThat(restoredProtocol.receivedPayloads[0]).isEqualTo(payload)
val restoredFlow = node2.restartAndGetRestoredFlow<ReceiveThenSuspendFlow>(node1)
assertThat(restoredFlow.receivedPayloads[0]).isEqualTo(payload)
}
@Test
fun `protocol added before network map does run after init`() {
fun `flow added before network map does run after init`() {
val node3 = net.createNode(node1.info.address) //create vanilla node
val protocol = NoOpProtocol()
node3.smm.add(protocol)
assertEquals(false, protocol.protocolStarted) // Not started yet as no network activity has been allowed yet
val flow = NoOpFlow()
node3.smm.add(flow)
assertEquals(false, flow.flowStarted) // Not started yet as no network activity has been allowed yet
net.runNetwork() // Allow network map messages to flow
assertEquals(true, protocol.protocolStarted) // Now we should have run the protocol
assertEquals(true, flow.flowStarted) // Now we should have run the flow
}
@Test
fun `protocol added before network map will be init checkpointed`() {
fun `flow added before network map will be init checkpointed`() {
var node3 = net.createNode(node1.info.address) //create vanilla node
val protocol = NoOpProtocol()
node3.smm.add(protocol)
assertEquals(false, protocol.protocolStarted) // Not started yet as no network activity has been allowed yet
val flow = NoOpFlow()
node3.smm.add(flow)
assertEquals(false, flow.flowStarted) // Not started yet as no network activity has been allowed yet
node3.disableDBCloseOnStop()
node3.stop()
node3 = net.createNode(node1.info.address, forcedID = node3.id)
val restoredProtocol = node3.getSingleProtocol<NoOpProtocol>().first
assertEquals(false, restoredProtocol.protocolStarted) // Not started yet as no network activity has been allowed yet
val restoredFlow = node3.getSingleFlow<NoOpFlow>().first
assertEquals(false, restoredFlow.flowStarted) // Not started yet as no network activity has been allowed yet
net.runNetwork() // Allow network map messages to flow
node3.smm.executor.flush()
assertEquals(true, restoredProtocol.protocolStarted) // Now we should have run the protocol and hopefully cleared the init checkpoint
assertEquals(true, restoredFlow.flowStarted) // Now we should have run the flow and hopefully cleared the init checkpoint
node3.disableDBCloseOnStop()
node3.stop()
// Now it is completed the protocol should leave no Checkpoint.
// Now it is completed the flow should leave no Checkpoint.
node3 = net.createNode(node1.info.address, forcedID = node3.id)
net.runNetwork() // Allow network map messages to flow
node3.smm.executor.flush()
assertTrue(node3.smm.findStateMachines(NoOpProtocol::class.java).isEmpty())
assertTrue(node3.smm.findStateMachines(NoOpFlow::class.java).isEmpty())
}
@Test
fun `protocol loaded from checkpoint will respond to messages from before start`() {
fun `flow loaded from checkpoint will respond to messages from before start`() {
val payload = random63BitValue()
node1.services.registerProtocolInitiator(ReceiveThenSuspendProtocol::class) { SendProtocol(payload, it) }
node2.smm.add(ReceiveThenSuspendProtocol(node1.info.legalIdentity)) // Prepare checkpointed receive protocol
node1.services.registerFlowInitiator(ReceiveThenSuspendFlow::class) { SendFlow(payload, it) }
node2.smm.add(ReceiveThenSuspendFlow(node1.info.legalIdentity)) // Prepare checkpointed receive flow
// Make sure the add() has finished initial processing.
node2.smm.executor.flush()
node2.disableDBCloseOnStop()
node2.stop() // kill receiver
val restoredProtocol = node2.restartAndGetRestoredProtocol<ReceiveThenSuspendProtocol>(node1)
assertThat(restoredProtocol.receivedPayloads[0]).isEqualTo(payload)
val restoredFlow = node2.restartAndGetRestoredFlow<ReceiveThenSuspendFlow>(node1)
assertThat(restoredFlow.receivedPayloads[0]).isEqualTo(payload)
}
@Test
fun `protocol with send will resend on interrupted restart`() {
fun `flow with send will resend on interrupted restart`() {
val payload = random63BitValue()
val payload2 = random63BitValue()
@ -140,11 +140,11 @@ class StateMachineManagerTests {
net.messagingNetwork.sentMessages.toSessionTransfers().filter { it.isPayloadTransfer }.forEach { sentCount++ }
val node3 = net.createNode(node1.info.address)
val secondProtocol = node3.initiateSingleShotProtocol(PingPongProtocol::class) { PingPongProtocol(it, payload2) }
val secondFlow = node3.initiateSingleShotFlow(PingPongFlow::class) { PingPongFlow(it, payload2) }
net.runNetwork()
// Kick off first send and receive
node2.smm.add(PingPongProtocol(node3.info.legalIdentity, payload))
node2.smm.add(PingPongFlow(node3.info.legalIdentity, payload))
databaseTransaction(node2.database) {
assertEquals(1, node2.checkpointStorage.checkpoints().size)
}
@ -158,55 +158,55 @@ class StateMachineManagerTests {
}
val node2b = net.createNode(node1.info.address, node2.id, advertisedServices = *node2.advertisedServices.toTypedArray())
node2.manuallyCloseDB()
val (firstAgain, fut1) = node2b.getSingleProtocol<PingPongProtocol>()
// Run the network which will also fire up the second protocol. First message should get deduped. So message data stays in sync.
val (firstAgain, fut1) = node2b.getSingleFlow<PingPongFlow>()
// Run the network which will also fire up the second flow. First message should get deduped. So message data stays in sync.
net.runNetwork()
node2b.smm.executor.flush()
fut1.get()
val receivedCount = sessionTransfers.count { it.isPayloadTransfer }
// Check protocols completed cleanly and didn't get out of phase
assertEquals(4, receivedCount, "Protocol should have exchanged 4 unique messages")// Two messages each way
// Check flows completed cleanly and didn't get out of phase
assertEquals(4, receivedCount, "Flow should have exchanged 4 unique messages")// Two messages each way
// can't give a precise value as every addMessageHandler re-runs the undelivered messages
assertTrue(sentCount > receivedCount, "Node restart should have retransmitted messages")
databaseTransaction(node2b.database) {
assertEquals(0, node2b.checkpointStorage.checkpoints().size, "Checkpoints left after restored protocol should have ended")
assertEquals(0, node2b.checkpointStorage.checkpoints().size, "Checkpoints left after restored flow should have ended")
}
databaseTransaction(node3.database) {
assertEquals(0, node3.checkpointStorage.checkpoints().size, "Checkpoints left after restored protocol should have ended")
assertEquals(0, node3.checkpointStorage.checkpoints().size, "Checkpoints left after restored flow should have ended")
}
assertEquals(payload2, firstAgain.receivedPayload, "Received payload does not match the first value on Node 3")
assertEquals(payload2 + 1, firstAgain.receivedPayload2, "Received payload does not match the expected second value on Node 3")
assertEquals(payload, secondProtocol.get().receivedPayload, "Received payload does not match the (restarted) first value on Node 2")
assertEquals(payload + 1, secondProtocol.get().receivedPayload2, "Received payload does not match the expected second value on Node 2")
assertEquals(payload, secondFlow.get().receivedPayload, "Received payload does not match the (restarted) first value on Node 2")
assertEquals(payload + 1, secondFlow.get().receivedPayload2, "Received payload does not match the expected second value on Node 2")
}
@Test
fun `sending to multiple parties`() {
val node3 = net.createNode(node1.info.address)
net.runNetwork()
node2.services.registerProtocolInitiator(SendProtocol::class) { ReceiveThenSuspendProtocol(it) }
node3.services.registerProtocolInitiator(SendProtocol::class) { ReceiveThenSuspendProtocol(it) }
node2.services.registerFlowInitiator(SendFlow::class) { ReceiveThenSuspendFlow(it) }
node3.services.registerFlowInitiator(SendFlow::class) { ReceiveThenSuspendFlow(it) }
val payload = random63BitValue()
node1.smm.add(SendProtocol(payload, node2.info.legalIdentity, node3.info.legalIdentity))
node1.smm.add(SendFlow(payload, node2.info.legalIdentity, node3.info.legalIdentity))
net.runNetwork()
val node2Protocol = node2.getSingleProtocol<ReceiveThenSuspendProtocol>().first
val node3Protocol = node3.getSingleProtocol<ReceiveThenSuspendProtocol>().first
assertThat(node2Protocol.receivedPayloads[0]).isEqualTo(payload)
assertThat(node3Protocol.receivedPayloads[0]).isEqualTo(payload)
val node2Flow = node2.getSingleFlow<ReceiveThenSuspendFlow>().first
val node3Flow = node3.getSingleFlow<ReceiveThenSuspendFlow>().first
assertThat(node2Flow.receivedPayloads[0]).isEqualTo(payload)
assertThat(node3Flow.receivedPayloads[0]).isEqualTo(payload)
assertSessionTransfers(node2,
node1 sent sessionInit(node1, SendProtocol::class, payload) to node2,
node1 sent sessionInit(node1, SendFlow::class, payload) to node2,
node2 sent sessionConfirm() to node1,
node1 sent sessionEnd() to node2
//There's no session end from the other protocols as they're manually suspended
//There's no session end from the other flows as they're manually suspended
)
assertSessionTransfers(node3,
node1 sent sessionInit(node1, SendProtocol::class, payload) to node3,
node1 sent sessionInit(node1, SendFlow::class, payload) to node3,
node3 sent sessionConfirm() to node1,
node1 sent sessionEnd() to node3
//There's no session end from the other protocols as they're manually suspended
//There's no session end from the other flows as they're manually suspended
)
node2.acceptableLiveFiberCountOnStop = 1
@ -219,24 +219,24 @@ class StateMachineManagerTests {
net.runNetwork()
val node2Payload = random63BitValue()
val node3Payload = random63BitValue()
node2.services.registerProtocolInitiator(ReceiveThenSuspendProtocol::class) { SendProtocol(node2Payload, it) }
node3.services.registerProtocolInitiator(ReceiveThenSuspendProtocol::class) { SendProtocol(node3Payload, it) }
val multiReceiveProtocol = ReceiveThenSuspendProtocol(node2.info.legalIdentity, node3.info.legalIdentity)
node1.smm.add(multiReceiveProtocol)
node2.services.registerFlowInitiator(ReceiveThenSuspendFlow::class) { SendFlow(node2Payload, it) }
node3.services.registerFlowInitiator(ReceiveThenSuspendFlow::class) { SendFlow(node3Payload, it) }
val multiReceiveFlow = ReceiveThenSuspendFlow(node2.info.legalIdentity, node3.info.legalIdentity)
node1.smm.add(multiReceiveFlow)
node1.acceptableLiveFiberCountOnStop = 1
net.runNetwork()
assertThat(multiReceiveProtocol.receivedPayloads[0]).isEqualTo(node2Payload)
assertThat(multiReceiveProtocol.receivedPayloads[1]).isEqualTo(node3Payload)
assertThat(multiReceiveFlow.receivedPayloads[0]).isEqualTo(node2Payload)
assertThat(multiReceiveFlow.receivedPayloads[1]).isEqualTo(node3Payload)
assertSessionTransfers(node2,
node1 sent sessionInit(node1, ReceiveThenSuspendProtocol::class) to node2,
node1 sent sessionInit(node1, ReceiveThenSuspendFlow::class) to node2,
node2 sent sessionConfirm() to node1,
node2 sent sessionData(node2Payload) to node1,
node2 sent sessionEnd() to node1
)
assertSessionTransfers(node3,
node1 sent sessionInit(node1, ReceiveThenSuspendProtocol::class) to node3,
node1 sent sessionInit(node1, ReceiveThenSuspendFlow::class) to node3,
node3 sent sessionConfirm() to node1,
node3 sent sessionData(node3Payload) to node1,
node3 sent sessionEnd() to node1
@ -245,12 +245,12 @@ class StateMachineManagerTests {
@Test
fun `both sides do a send as their first IO request`() {
node2.services.registerProtocolInitiator(PingPongProtocol::class) { PingPongProtocol(it, 20L) }
node1.smm.add(PingPongProtocol(node2.info.legalIdentity, 10L))
node2.services.registerFlowInitiator(PingPongFlow::class) { PingPongFlow(it, 20L) }
node1.smm.add(PingPongFlow(node2.info.legalIdentity, 10L))
net.runNetwork()
assertSessionTransfers(
node1 sent sessionInit(node1, PingPongProtocol::class, 10L) to node2,
node1 sent sessionInit(node1, PingPongFlow::class, 10L) to node2,
node2 sent sessionConfirm() to node1,
node2 sent sessionData(20L) to node1,
node1 sent sessionData(11L) to node2,
@ -261,18 +261,18 @@ class StateMachineManagerTests {
@Test
fun `exception thrown on other side`() {
node2.services.registerProtocolInitiator(ReceiveThenSuspendProtocol::class) { ExceptionProtocol }
val future = node1.smm.add(ReceiveThenSuspendProtocol(node2.info.legalIdentity)).resultFuture
node2.services.registerFlowInitiator(ReceiveThenSuspendFlow::class) { ExceptionFlow }
val future = node1.smm.add(ReceiveThenSuspendFlow(node2.info.legalIdentity)).resultFuture
net.runNetwork()
assertThatThrownBy { future.get() }.hasCauseInstanceOf(ProtocolSessionException::class.java)
assertThatThrownBy { future.get() }.hasCauseInstanceOf(FlowSessionException::class.java)
assertSessionTransfers(
node1 sent sessionInit(node1, ReceiveThenSuspendProtocol::class) to node2,
node1 sent sessionInit(node1, ReceiveThenSuspendFlow::class) to node2,
node2 sent sessionConfirm() to node1,
node2 sent sessionEnd() to node1
)
}
private inline fun <reified P : ProtocolLogic<*>> MockNode.restartAndGetRestoredProtocol(
private inline fun <reified P : FlowLogic<*>> MockNode.restartAndGetRestoredFlow(
networkMapNode: MockNode? = null): P {
disableDBCloseOnStop() //Handover DB to new node copy
stop()
@ -280,15 +280,15 @@ class StateMachineManagerTests {
newNode.acceptableLiveFiberCountOnStop = 1
manuallyCloseDB()
mockNet.runNetwork() // allow NetworkMapService messages to stabilise and thus start the state machine
return newNode.getSingleProtocol<P>().first
return newNode.getSingleFlow<P>().first
}
private inline fun <reified P : ProtocolLogic<*>> MockNode.getSingleProtocol(): Pair<P, ListenableFuture<*>> {
private inline fun <reified P : FlowLogic<*>> MockNode.getSingleFlow(): Pair<P, ListenableFuture<*>> {
return smm.findStateMachines(P::class.java).single()
}
private fun sessionInit(initiatorNode: MockNode, protocolMarker: KClass<*>, payload: Any? = null): SessionInit {
return SessionInit(0, initiatorNode.info.legalIdentity, protocolMarker.java.name, payload)
private fun sessionInit(initiatorNode: MockNode, flowMarker: KClass<*>, payload: Any? = null): SessionInit {
return SessionInit(0, initiatorNode.info.legalIdentity, flowMarker.java.name, payload)
}
private fun sessionConfirm() = SessionConfirm(0, 0)
@ -334,13 +334,13 @@ class StateMachineManagerTests {
private infix fun Pair<Int, SessionMessage>.to(node: MockNode): SessionTransfer = SessionTransfer(first, second, node.id)
private class NoOpProtocol(val nonTerminating: Boolean = false) : ProtocolLogic<Unit>() {
private class NoOpFlow(val nonTerminating: Boolean = false) : FlowLogic<Unit>() {
@Transient var protocolStarted = false
@Transient var flowStarted = false
@Suspendable
override fun call() {
protocolStarted = true
flowStarted = true
if (nonTerminating) {
Fiber.park()
}
@ -348,7 +348,7 @@ class StateMachineManagerTests {
}
private class SendProtocol(val payload: Any, vararg val otherParties: Party) : ProtocolLogic<Unit>() {
private class SendFlow(val payload: Any, vararg val otherParties: Party) : FlowLogic<Unit>() {
init {
require(otherParties.isNotEmpty())
@ -359,7 +359,7 @@ class StateMachineManagerTests {
}
private class ReceiveThenSuspendProtocol(vararg val otherParties: Party) : ProtocolLogic<Unit>() {
private class ReceiveThenSuspendFlow(vararg val otherParties: Party) : FlowLogic<Unit>() {
init {
require(otherParties.isNotEmpty())
@ -375,7 +375,7 @@ class StateMachineManagerTests {
}
}
private class PingPongProtocol(val otherParty: Party, val payload: Long) : ProtocolLogic<Unit>() {
private class PingPongFlow(val otherParty: Party, val payload: Long) : FlowLogic<Unit>() {
@Transient var receivedPayload: Long? = null
@Transient var receivedPayload2: Long? = null
@ -383,13 +383,13 @@ class StateMachineManagerTests {
@Suspendable
override fun call() {
receivedPayload = sendAndReceive<Long>(otherParty, payload).unwrap { it }
println("${psm.id} Received $receivedPayload")
println("${fsm.id} Received $receivedPayload")
receivedPayload2 = sendAndReceive<Long>(otherParty, payload + 1).unwrap { it }
println("${psm.id} Received $receivedPayload2")
println("${fsm.id} Received $receivedPayload2")
}
}
private object ExceptionProtocol : ProtocolLogic<Nothing>() {
private object ExceptionFlow : FlowLogic<Nothing>() {
override fun call(): Nothing = throw Exception()
}

View File

@ -8,7 +8,7 @@ import net.corda.core.success
import net.corda.core.utilities.ApiUtils
import net.corda.core.utilities.Emoji
import net.corda.core.utilities.loggerFor
import net.corda.protocols.FinalityProtocol
import net.corda.flows.FinalityFlow
import net.corda.testing.ALICE_KEY
import java.util.concurrent.CompletableFuture
import javax.ws.rs.*
@ -49,10 +49,10 @@ class AttachmentDemoApi(val services: ServiceHub) {
// Send the transaction to the other recipient
val tx = ptx.toSignedTransaction()
services.invokeProtocolAsync(FinalityProtocol::class.java, tx, setOf(it)).resultFuture.success {
println("Successfully sent attachment with the FinalityProtocol")
services.invokeFlowAsync<Unit>(FinalityFlow::class.java, tx, setOf(it)).resultFuture.success {
println("Successfully sent attachment with the FinalityFlow")
}.failure {
logger.error("Failed to send attachment with the FinalityProtocol", it)
logger.error("Failed to send attachment with the FinalityFlow")
}
Response.accepted().build()
@ -64,10 +64,10 @@ class AttachmentDemoApi(val services: ServiceHub) {
@Consumes(MediaType.APPLICATION_JSON)
fun runRecipient(): Response {
val future = CompletableFuture<Response>()
// Normally we would receive the transaction from a more specific protocol, but in this case we let [FinalityProtocol]
// Normally we would receive the transaction from a more specific flow, but in this case we let [FinalityFlow]
// handle receiving it for us.
services.storageService.validatedTransactions.updates.subscribe { event ->
// When the transaction is received, it's passed through [ResolveTransactionsProtocol], which first fetches any
// When the transaction is received, it's passed through [ResolveTransactionsFlow], which first fetches any
// attachments for us, then verifies the transaction. As such, by the time it hits the validated transaction store,
// we have a copy of the attachment.
val tx = event.tx

View File

@ -3,14 +3,14 @@ package net.corda.attachmentdemo.plugin
import net.corda.attachmentdemo.api.AttachmentDemoApi
import net.corda.core.node.CordaPluginRegistry
import net.corda.core.transactions.SignedTransaction
import net.corda.protocols.FinalityProtocol
import net.corda.flows.FinalityFlow
class AttachmentDemoPlugin : CordaPluginRegistry() {
// A list of classes that expose web APIs.
override val webApis: List<Class<*>> = listOf(AttachmentDemoApi::class.java)
// A list of protocols that are required for this cordapp
override val requiredProtocols: Map<String, Set<String>> = mapOf(
FinalityProtocol::class.java.name to setOf(SignedTransaction::class.java.name, setOf(Unit).javaClass.name, setOf(Unit).javaClass.name)
// A list of flows that are required for this cordapp
override val requiredFlows: Map<String, Set<String>> = mapOf(
FinalityFlow::class.java.name to setOf(SignedTransaction::class.java.name, setOf(Unit).javaClass.name, setOf(Unit).javaClass.name)
)
override val servicePlugins: List<Class<*>> = listOf()
}

View File

@ -4,9 +4,9 @@ import net.corda.core.node.ServiceHub
import net.corda.core.node.services.linearHeadsOfType
import net.corda.core.utilities.loggerFor
import net.corda.irs.contract.InterestRateSwap
import net.corda.irs.protocols.AutoOfferProtocol
import net.corda.irs.protocols.ExitServerProtocol
import net.corda.irs.protocols.UpdateBusinessDayProtocol
import net.corda.irs.flows.AutoOfferFlow
import net.corda.irs.flows.ExitServerFlow
import net.corda.irs.flows.UpdateBusinessDayFlow
import java.net.URI
import java.time.LocalDate
import java.time.LocalDateTime
@ -65,7 +65,7 @@ class InterestRateSwapAPI(val services: ServiceHub) {
@Consumes(MediaType.APPLICATION_JSON)
fun storeDeal(newDeal: InterestRateSwap.State): Response {
try {
services.invokeProtocolAsync(AutoOfferProtocol.Requester::class.java, newDeal).resultFuture.get()
services.invokeFlowAsync(AutoOfferFlow.Requester::class.java, newDeal).resultFuture.get()
return Response.created(URI.create(generateDealLink(newDeal))).build()
} catch (ex: Throwable) {
logger.info("Exception when creating deal: $ex")
@ -92,7 +92,7 @@ class InterestRateSwapAPI(val services: ServiceHub) {
val priorDemoDate = fetchDemoDate()
// Can only move date forwards
if (newDemoDate.isAfter(priorDemoDate)) {
services.invokeProtocolAsync(UpdateBusinessDayProtocol.Broadcast::class.java, newDemoDate).resultFuture.get()
services.invokeFlowAsync(UpdateBusinessDayFlow.Broadcast::class.java, newDemoDate).resultFuture.get()
return Response.ok().build()
}
val msg = "demodate is already $priorDemoDate and can only be updated with a later date"
@ -111,7 +111,7 @@ class InterestRateSwapAPI(val services: ServiceHub) {
@Path("restart")
@Consumes(MediaType.APPLICATION_JSON)
fun exitServer(): Response {
services.invokeProtocolAsync(ExitServerProtocol.Broadcast::class.java, 83).resultFuture.get()
services.invokeFlowAsync(ExitServerFlow.Broadcast::class.java, 83).resultFuture.get()
return Response.ok().build()
}
}

View File

@ -4,18 +4,18 @@ import co.paralleluniverse.fibers.Suspendable
import net.corda.core.RetryableException
import net.corda.core.contracts.*
import net.corda.core.crypto.*
import net.corda.core.flows.FlowLogic
import net.corda.core.math.CubicSplineInterpolator
import net.corda.core.math.Interpolator
import net.corda.core.math.InterpolatorFactory
import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.PluginServiceHub
import net.corda.core.node.services.ServiceType
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.transactions.FilteredTransaction
import net.corda.core.utilities.ProgressTracker
import net.corda.irs.protocols.FixingProtocol
import net.corda.irs.protocols.RatesFixProtocol
import net.corda.irs.flows.FixingFlow
import net.corda.irs.flows.RatesFixFlow
import net.corda.node.services.api.AcceptsFileUpload
import net.corda.node.utilities.AbstractJDBCHashSet
import net.corda.node.utilities.FiberBox
@ -46,10 +46,10 @@ object NodeInterestRates {
val type = ServiceType.corda.getSubType("interest_rates")
/**
* Register the protocol that is used with the Fixing integration tests.
* Register the flow that is used with the Fixing integration tests.
*/
class Plugin : CordaPluginRegistry() {
override val requiredProtocols: Map<String, Set<String>> = mapOf(Pair(FixingProtocol.FixingRoleDecider::class.java.name, setOf(Duration::class.java.name, StateRef::class.java.name)))
override val requiredFlows: Map<String, Set<String>> = mapOf(Pair(FixingFlow.FixingRoleDecider::class.java.name, setOf(Duration::class.java.name, StateRef::class.java.name)))
override val servicePlugins: List<Class<*>> = listOf(Service::class.java)
}
@ -67,20 +67,20 @@ object NodeInterestRates {
init {
// Note access to the singleton oracle property is via the registered SingletonSerializeAsToken Service.
// Otherwise the Kryo serialisation of the call stack in the Quasar Fiber extends to include
// the framework Oracle and the protocol will crash.
services.registerProtocolInitiator(RatesFixProtocol.FixSignProtocol::class) { FixSignHandler(it, this) }
services.registerProtocolInitiator(RatesFixProtocol.FixQueryProtocol::class) { FixQueryHandler(it, this) }
// the framework Oracle and the flow will crash.
services.registerFlowInitiator(RatesFixFlow.FixSignFlow::class) { FixSignHandler(it, this) }
services.registerFlowInitiator(RatesFixFlow.FixQueryFlow::class) { FixQueryHandler(it, this) }
}
private class FixSignHandler(val otherParty: Party, val service: Service) : ProtocolLogic<Unit>() {
private class FixSignHandler(val otherParty: Party, val service: Service) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val request = receive<RatesFixProtocol.SignRequest>(otherParty).unwrap { it }
val request = receive<RatesFixFlow.SignRequest>(otherParty).unwrap { it }
send(otherParty, service.oracle.sign(request.ftx, request.rootHash))
}
}
private class FixQueryHandler(val otherParty: Party, val service: Service) : ProtocolLogic<Unit>() {
private class FixQueryHandler(val otherParty: Party, val service: Service) : FlowLogic<Unit>() {
companion object {
object RECEIVED : ProgressTracker.Step("Received fix request")
@ -95,7 +95,7 @@ object NodeInterestRates {
@Suspendable
override fun call(): Unit {
val request = receive<RatesFixProtocol.QueryRequest>(otherParty).unwrap { it }
val request = receive<RatesFixFlow.QueryRequest>(otherParty).unwrap { it }
val answers = service.oracle.query(request.queries, request.deadline)
progressTracker.currentStep = SENDING
send(otherParty, answers)

View File

@ -5,10 +5,10 @@ import net.corda.core.contracts.clauses.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.node.services.ServiceType
import net.corda.core.protocols.ProtocolLogicRefFactory
import net.corda.core.transactions.TransactionBuilder
import net.corda.irs.protocols.FixingProtocol
import net.corda.irs.flows.FixingFlow
import net.corda.irs.utilities.suggestInterestRateAnnouncementTimeWindow
import org.apache.commons.jexl3.JexlBuilder
import org.apache.commons.jexl3.MapContext
@ -674,12 +674,12 @@ class InterestRateSwap() : Contract {
override val parties: List<Party>
get() = listOf(fixedLeg.fixedRatePayer, floatingLeg.floatingRatePayer)
override fun nextScheduledActivity(thisStateRef: StateRef, protocolLogicRefFactory: ProtocolLogicRefFactory): ScheduledActivity? {
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
val nextFixingOf = nextFixingOf() ?: return null
// This is perhaps not how we should determine the time point in the business day, but instead expect the schedule to detail some of these aspects
val instant = suggestInterestRateAnnouncementTimeWindow(index = nextFixingOf.name, source = floatingLeg.indexSource, date = nextFixingOf.forDay).start
return ScheduledActivity(protocolLogicRefFactory.create(FixingProtocol.FixingRoleDecider::class.java, thisStateRef), instant)
return ScheduledActivity(flowLogicRefFactory.create(FixingFlow.FixingRoleDecider::class.java, thisStateRef), instant)
}
override fun generateAgreement(notary: Party): TransactionBuilder = InterestRateSwap().generateAgreement(floatingLeg, fixedLeg, calculation, common, notary)

View File

@ -1,28 +1,27 @@
package net.corda.irs.protocols
package net.corda.irs.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.DealState
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic
import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.PluginServiceHub
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker
import net.corda.node.services.api.ServiceHubInternal
import net.corda.protocols.TwoPartyDealProtocol
import net.corda.protocols.TwoPartyDealProtocol.Acceptor
import net.corda.protocols.TwoPartyDealProtocol.AutoOffer
import net.corda.protocols.TwoPartyDealProtocol.Instigator
import net.corda.flows.TwoPartyDealFlow
import net.corda.flows.TwoPartyDealFlow.Acceptor
import net.corda.flows.TwoPartyDealFlow.AutoOffer
import net.corda.flows.TwoPartyDealFlow.Instigator
/**
* This whole class is really part of a demo just to initiate the agreement of a deal with a simple
* API call from a single party without bi-directional access to the database of offers etc.
*
* In the "real world", we'd probably have the offers sitting in the platform prior to the agreement step
* or the protocol would have to reach out to external systems (or users) to verify the deals.
* or the flow would have to reach out to external systems (or users) to verify the deals.
*/
object AutoOfferProtocol {
object AutoOfferFlow {
class Plugin : CordaPluginRegistry() {
override val servicePlugins: List<Class<*>> = listOf(Service::class.java)
@ -31,24 +30,24 @@ object AutoOfferProtocol {
class Service(services: PluginServiceHub) : SingletonSerializeAsToken() {
object DEALING : ProgressTracker.Step("Starting the deal protocol") {
override fun childProgressTracker(): ProgressTracker = TwoPartyDealProtocol.Secondary.tracker()
object DEALING : ProgressTracker.Step("Starting the deal flow") {
override fun childProgressTracker(): ProgressTracker = TwoPartyDealFlow.Secondary.tracker()
}
init {
services.registerProtocolInitiator(Instigator::class) { Acceptor(it) }
services.registerFlowInitiator(Instigator::class) { Acceptor(it) }
}
}
class Requester(val dealToBeOffered: DealState) : ProtocolLogic<SignedTransaction>() {
class Requester(val dealToBeOffered: DealState) : FlowLogic<SignedTransaction>() {
companion object {
object RECEIVED : ProgressTracker.Step("Received API call")
object DEALING : ProgressTracker.Step("Starting the deal protocol") {
override fun childProgressTracker(): ProgressTracker = TwoPartyDealProtocol.Primary.tracker()
object DEALING : ProgressTracker.Step("Starting the deal flow") {
override fun childProgressTracker(): ProgressTracker = TwoPartyDealFlow.Primary.tracker()
}
// We vend a progress tracker that already knows there's going to be a TwoPartyTradingProtocol involved at some
// We vend a progress tracker that already knows there's going to be a TwoPartyTradingFlow involved at some
// point: by setting up the tracker in advance, the user can see what's coming in more detail, instead of being
// surprised when it appears as a new set of tasks below the current one.
fun tracker() = ProgressTracker(RECEIVED, DEALING)
@ -74,7 +73,7 @@ object AutoOfferProtocol {
myKey,
progressTracker.getChildProgressTracker(DEALING)!!
)
val stx = subProtocol(instigator)
val stx = subFlow(instigator)
return stx
}

View File

@ -1,23 +1,22 @@
package net.corda.irs.protocols
package net.corda.irs.flows
import co.paralleluniverse.fibers.Suspendable
import co.paralleluniverse.strands.Strand
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic
import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.NodeInfo
import net.corda.core.node.PluginServiceHub
import net.corda.core.protocols.ProtocolLogic
import net.corda.node.services.api.ServiceHubInternal
import net.corda.testing.node.MockNetworkMapCache
import java.util.concurrent.TimeUnit
object ExitServerProtocol {
object ExitServerFlow {
// Will only be enabled if you install the Handler
@Volatile private var enabled = false
// This is not really a HandshakeMessage but needs to be so that the send uses the default session ID. This will
// resolve itself when the protocol session stuff is done.
// resolve itself when the flow session stuff is done.
data class ExitMessage(val exitCode: Int)
class Plugin: CordaPluginRegistry() {
@ -26,13 +25,13 @@ object ExitServerProtocol {
class Service(services: PluginServiceHub) {
init {
services.registerProtocolInitiator(Broadcast::class, ::ExitServerHandler)
services.registerFlowInitiator(Broadcast::class, ::ExitServerHandler)
enabled = true
}
}
private class ExitServerHandler(val otherParty: Party) : ProtocolLogic<Unit>() {
private class ExitServerHandler(val otherParty: Party) : FlowLogic<Unit>() {
override fun call() {
// Just to validate we got the message
if (enabled) {
@ -47,7 +46,7 @@ object ExitServerProtocol {
* This takes a Java Integer rather than Kotlin Int as that is what we end up with in the calling map and currently
* we do not support coercing numeric types in the reflective search for matching constructors.
*/
class Broadcast(val exitCode: Int) : ProtocolLogic<Boolean>() {
class Broadcast(val exitCode: Int) : FlowLogic<Boolean>() {
@Suspendable
override fun call(): Boolean {

View File

@ -1,45 +1,45 @@
package net.corda.irs.protocols
package net.corda.irs.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.TransientProperty
import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic
import net.corda.core.node.NodeInfo
import net.corda.core.node.PluginServiceHub
import net.corda.core.node.services.ServiceType
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.seconds
import net.corda.core.transactions.FilterFuns
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.trace
import net.corda.protocols.TwoPartyDealProtocol
import net.corda.flows.TwoPartyDealFlow
import java.math.BigDecimal
import java.security.KeyPair
object FixingProtocol {
object FixingFlow {
class Service(services: PluginServiceHub) {
init {
services.registerProtocolInitiator(Floater::class) { Fixer(it) }
services.registerFlowInitiator(Floater::class) { Fixer(it) }
}
}
/**
* One side of the fixing protocol for an interest rate swap, but could easily be generalised further.
* One side of the fixing flow for an interest rate swap, but could easily be generalised further.
*
* Do not infer too much from the name of the class. This is just to indicate that it is the "side"
* of the protocol that is run by the party with the fixed leg of swap deal, which is the basis for deciding
* who does what in the protocol.
* of the flow that is run by the party with the fixed leg of swap deal, which is the basis for deciding
* who does what in the flow.
*/
class Fixer(override val otherParty: Party,
override val progressTracker: ProgressTracker = TwoPartyDealProtocol.Secondary.tracker()) : TwoPartyDealProtocol.Secondary<FixingSession>() {
override val progressTracker: ProgressTracker = TwoPartyDealFlow.Secondary.tracker()) : TwoPartyDealFlow.Secondary<FixingSession>() {
private lateinit var txState: TransactionState<*>
private lateinit var deal: FixableDealState
override fun validateHandshake(handshake: TwoPartyDealProtocol.Handshake<FixingSession>): TwoPartyDealProtocol.Handshake<FixingSession> {
override fun validateHandshake(handshake: TwoPartyDealFlow.Handshake<FixingSession>): TwoPartyDealFlow.Handshake<FixingSession> {
logger.trace { "Got fixing request for: ${handshake.payload}" }
txState = serviceHub.loadState(handshake.payload.ref)
@ -55,7 +55,7 @@ object FixingProtocol {
}
@Suspendable
override fun assembleSharedTX(handshake: TwoPartyDealProtocol.Handshake<FixingSession>): Pair<TransactionBuilder, List<CompositeKey>> {
override fun assembleSharedTX(handshake: TwoPartyDealFlow.Handshake<FixingSession>): Pair<TransactionBuilder, List<CompositeKey>> {
@Suppress("UNCHECKED_CAST")
val fixOf = deal.nextFixingOf()!!
@ -70,10 +70,10 @@ object FixingProtocol {
val oracle = serviceHub.networkMapCache.get(handshake.payload.oracleType).first()
val oracleParty = oracle.serviceIdentities(handshake.payload.oracleType).first()
// TODO Could it be solved in better way, move filtering here not in RatesFixProtocol?
// TODO Could it be solved in better way, move filtering here not in RatesFixFlow?
fun filterCommands(c: Command) = oracleParty.owningKey in c.signers && c.value is Fix
val filterFuns = FilterFuns(filterCommands = ::filterCommands)
val addFixing = object : RatesFixProtocol(ptx, filterFuns, oracleParty, fixOf, BigDecimal.ZERO, BigDecimal.ONE) {
val addFixing = object : RatesFixFlow(ptx, filterFuns, oracleParty, fixOf, BigDecimal.ZERO, BigDecimal.ONE) {
@Suspendable
override fun beforeSigning(fix: Fix) {
newDeal.generateFix(ptx, StateAndRef(txState, handshake.payload.ref), fix)
@ -83,21 +83,21 @@ object FixingProtocol {
ptx.setTime(serviceHub.clock.instant(), 30.seconds)
}
}
subProtocol(addFixing)
subFlow(addFixing)
return Pair(ptx, arrayListOf(myOldParty.owningKey))
}
}
/**
* One side of the fixing protocol for an interest rate swap, but could easily be generalised furher.
* One side of the fixing flow for an interest rate swap, but could easily be generalised furher.
*
* As per the [Fixer], do not infer too much from this class name in terms of business roles. This
* is just the "side" of the protocol run by the party with the floating leg as a way of deciding who
* does what in the protocol.
* is just the "side" of the flow run by the party with the floating leg as a way of deciding who
* does what in the flow.
*/
class Floater(override val otherParty: Party,
override val payload: FixingSession,
override val progressTracker: ProgressTracker = TwoPartyDealProtocol.Primary.tracker()) : TwoPartyDealProtocol.Primary() {
override val progressTracker: ProgressTracker = TwoPartyDealFlow.Primary.tracker()) : TwoPartyDealFlow.Primary() {
@Suppress("UNCHECKED_CAST")
internal val dealToFix: StateAndRef<FixableDealState> by TransientProperty {
@ -120,7 +120,7 @@ object FixingProtocol {
data class FixingSession(val ref: StateRef, val oracleType: ServiceType)
/**
* This protocol looks at the deal and decides whether to be the Fixer or Floater role in agreeing a fixing.
* This flow looks at the deal and decides whether to be the Fixer or Floater role in agreeing a fixing.
*
* It is kicked off as an activity on both participant nodes by the scheduler when it's time for a fixing. If the
* Fixer role is chosen, then that will be initiated by the [FixingSession] message sent from the other party and
@ -129,7 +129,7 @@ object FixingProtocol {
* TODO: Replace [FixingSession] and [FixingSessionInitiationHandler] with generic session initiation logic once it exists.
*/
class FixingRoleDecider(val ref: StateRef,
override val progressTracker: ProgressTracker = tracker()) : ProtocolLogic<Unit>() {
override val progressTracker: ProgressTracker = tracker()) : FlowLogic<Unit>() {
companion object {
class LOADING() : ProgressTracker.Step("Loading state to decide fixing role")
@ -147,7 +147,7 @@ object FixingProtocol {
if (sortedParties[0].name == serviceHub.myInfo.legalIdentity.name) {
val fixing = FixingSession(ref, fixableDeal.oracleType)
// Start the Floater which will then kick-off the Fixer
subProtocol(Floater(sortedParties[1], fixing))
subFlow(Floater(sortedParties[1], fixing))
}
}
}

View File

@ -1,17 +1,17 @@
package net.corda.irs.protocols
package net.corda.irs.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.Fix
import net.corda.core.contracts.FixOf
import net.corda.core.contracts.Timestamp
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.flows.FlowLogic
import net.corda.core.transactions.FilterFuns
import net.corda.core.transactions.FilteredTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker
import net.corda.irs.flows.RatesFixFlow.FixOutOfRange
import net.corda.irs.utilities.suggestInterestRateAnnouncementTimeWindow
import java.math.BigDecimal
import java.time.Instant
@ -20,21 +20,21 @@ import java.util.*
// This code is unit tested in NodeInterestRates.kt
/**
* This protocol queries the given oracle for an interest rate fix, and if it is within the given tolerance embeds the
* This flow queries the given oracle for an interest rate fix, and if it is within the given tolerance embeds the
* fix in the transaction and then proceeds to get the oracle to sign it. Although the [call] method combines the query
* and signing step, you can run the steps individually by constructing this object and then using the public methods
* for each step.
*
* @throws FixOutOfRange if the returned fix was further away from the expected rate by the given amount.
*/
open class RatesFixProtocol(protected val tx: TransactionBuilder,
open class RatesFixFlow(protected val tx: TransactionBuilder,
// Filtering functions over transaction, used to build partial transaction presented to oracle.
private val filterFuns: FilterFuns,
private val oracle: Party,
private val fixOf: FixOf,
private val expectedRate: BigDecimal,
private val rateTolerance: BigDecimal,
override val progressTracker: ProgressTracker = RatesFixProtocol.tracker(fixOf.name)) : ProtocolLogic<Unit>() {
override val progressTracker: ProgressTracker = RatesFixFlow.tracker(fixOf.name)) : FlowLogic<Unit>() {
companion object {
class QUERYING(val name: String) : ProgressTracker.Step("Querying oracle for $name interest rate")
@ -52,13 +52,13 @@ open class RatesFixProtocol(protected val tx: TransactionBuilder,
@Suspendable
override fun call() {
progressTracker.currentStep = progressTracker.steps[1]
val fix = subProtocol(FixQueryProtocol(fixOf, oracle))
val fix = subFlow(FixQueryFlow(fixOf, oracle))
progressTracker.currentStep = WORKING
checkFixIsNearExpected(fix)
tx.addCommand(fix, oracle.owningKey)
beforeSigning(fix)
progressTracker.currentStep = SIGNING
val signature = subProtocol(FixSignProtocol(tx, oracle, filterFuns))
val signature = subFlow(FixSignFlow(tx, oracle, filterFuns))
tx.addSignatureUnchecked(signature)
}
@ -79,7 +79,7 @@ open class RatesFixProtocol(protected val tx: TransactionBuilder,
}
class FixQueryProtocol(val fixOf: FixOf, val oracle: Party) : ProtocolLogic<Fix>() {
class FixQueryFlow(val fixOf: FixOf, val oracle: Party) : FlowLogic<Fix>() {
@Suspendable
override fun call(): Fix {
val deadline = suggestInterestRateAnnouncementTimeWindow(fixOf.name, oracle.name, fixOf.forDay).end
@ -95,7 +95,7 @@ open class RatesFixProtocol(protected val tx: TransactionBuilder,
}
}
class FixSignProtocol(val tx: TransactionBuilder, val oracle: Party, val filterFuns: FilterFuns) : ProtocolLogic<DigitalSignature.LegallyIdentifiable>() {
class FixSignFlow(val tx: TransactionBuilder, val oracle: Party, val filterFuns: FilterFuns) : FlowLogic<DigitalSignature.LegallyIdentifiable>() {
@Suspendable
override fun call(): DigitalSignature.LegallyIdentifiable {
val wtx = tx.toWireTransaction()

View File

@ -1,24 +1,23 @@
package net.corda.irs.protocols
package net.corda.irs.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic
import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.NodeInfo
import net.corda.core.node.PluginServiceHub
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.utilities.ProgressTracker
import net.corda.node.utilities.TestClock
import net.corda.node.services.api.ServiceHubInternal
import net.corda.testing.node.MockNetworkMapCache
import java.time.LocalDate
/**
* This is a less temporary, demo-oriented way of initiating processing of temporal events.
*/
object UpdateBusinessDayProtocol {
object UpdateBusinessDayFlow {
// This is not really a HandshakeMessage but needs to be so that the send uses the default session ID. This will
// resolve itself when the protocol session stuff is done.
// resolve itself when the flow session stuff is done.
data class UpdateBusinessDayMessage(val date: LocalDate)
class Plugin: CordaPluginRegistry() {
@ -27,11 +26,11 @@ object UpdateBusinessDayProtocol {
class Service(services: PluginServiceHub) {
init {
services.registerProtocolInitiator(Broadcast::class, ::UpdateBusinessDayHandler)
services.registerFlowInitiator(Broadcast::class, ::UpdateBusinessDayHandler)
}
}
private class UpdateBusinessDayHandler(val otherParty: Party) : ProtocolLogic<Unit>() {
private class UpdateBusinessDayHandler(val otherParty: Party) : FlowLogic<Unit>() {
override fun call() {
val message = receive<UpdateBusinessDayMessage>(otherParty).unwrap { it }
(serviceHub.clock as TestClock).updateDate(message.date)
@ -40,7 +39,7 @@ object UpdateBusinessDayProtocol {
class Broadcast(val date: LocalDate,
override val progressTracker: ProgressTracker = Broadcast.tracker()) : ProtocolLogic<Unit>() {
override val progressTracker: ProgressTracker = Broadcast.tracker()) : FlowLogic<Unit>() {
companion object {
object NOTIFYING : ProgressTracker.Step("Notifying peers")

View File

@ -5,11 +5,10 @@ import net.corda.core.crypto.Party
import net.corda.core.node.CordaPluginRegistry
import net.corda.irs.api.InterestRateSwapAPI
import net.corda.irs.contract.InterestRateSwap
import net.corda.irs.protocols.AutoOfferProtocol
import net.corda.irs.protocols.ExitServerProtocol
import net.corda.irs.protocols.FixingProtocol
import net.corda.irs.protocols.UpdateBusinessDayProtocol
import net.corda.protocols.TwoPartyDealProtocol
import net.corda.irs.flows.AutoOfferFlow
import net.corda.irs.flows.ExitServerFlow
import net.corda.irs.flows.FixingFlow
import net.corda.irs.flows.UpdateBusinessDayFlow
import java.time.Duration
class IRSPlugin : CordaPluginRegistry() {
@ -17,11 +16,11 @@ class IRSPlugin : CordaPluginRegistry() {
override val staticServeDirs: Map<String, String> = mapOf(
"irsdemo" to javaClass.classLoader.getResource("irsweb").toExternalForm()
)
override val servicePlugins: List<Class<*>> = listOf(FixingProtocol.Service::class.java)
override val requiredProtocols: Map<String, Set<String>> = mapOf(
Pair(AutoOfferProtocol.Requester::class.java.name, setOf(InterestRateSwap.State::class.java.name)),
Pair(UpdateBusinessDayProtocol.Broadcast::class.java.name, setOf(java.time.LocalDate::class.java.name)),
Pair(ExitServerProtocol.Broadcast::class.java.name, setOf(kotlin.Int::class.java.name)),
Pair(FixingProtocol.FixingRoleDecider::class.java.name, setOf(StateRef::class.java.name, Duration::class.java.name)),
Pair(FixingProtocol.Floater::class.java.name, setOf(Party::class.java.name, FixingProtocol.FixingSession::class.java.name)))
override val servicePlugins: List<Class<*>> = listOf(FixingFlow.Service::class.java)
override val requiredFlows: Map<String, Set<String>> = mapOf(
Pair(AutoOfferFlow.Requester::class.java.name, setOf(InterestRateSwap.State::class.java.name)),
Pair(UpdateBusinessDayFlow.Broadcast::class.java.name, setOf(java.time.LocalDate::class.java.name)),
Pair(ExitServerFlow.Broadcast::class.java.name, setOf(kotlin.Int::class.java.name)),
Pair(FixingFlow.FixingRoleDecider::class.java.name, setOf(StateRef::class.java.name, Duration::class.java.name)),
Pair(FixingFlow.Floater::class.java.name, setOf(Party::class.java.name, FixingFlow.FixingSession::class.java.name)))
}

View File

@ -9,17 +9,17 @@ import net.corda.core.RunOnCallerThread
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.flatMap
import net.corda.core.flows.FlowStateMachine
import net.corda.core.map
import net.corda.core.node.services.linearHeadsOfType
import net.corda.core.protocols.ProtocolStateMachine
import net.corda.core.success
import net.corda.core.transactions.SignedTransaction
import net.corda.flows.TwoPartyDealFlow.Acceptor
import net.corda.flows.TwoPartyDealFlow.AutoOffer
import net.corda.flows.TwoPartyDealFlow.Instigator
import net.corda.irs.contract.InterestRateSwap
import net.corda.node.utilities.databaseTransaction
import net.corda.protocols.TwoPartyDealProtocol.Acceptor
import net.corda.protocols.TwoPartyDealProtocol.AutoOffer
import net.corda.protocols.TwoPartyDealProtocol.Instigator
import net.corda.testing.initiateSingleShotProtocol
import net.corda.testing.initiateSingleShotFlow
import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.MockIdentityService
import java.time.LocalDate
@ -118,15 +118,15 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity
irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity
val acceptorTx = node2.initiateSingleShotProtocol(Instigator::class) { Acceptor(it) }.flatMap {
(it.psm as ProtocolStateMachine<SignedTransaction>).resultFuture
val acceptorTx = node2.initiateSingleShotFlow(Instigator::class) { Acceptor(it) }.flatMap {
(it.fsm as FlowStateMachine<SignedTransaction>).resultFuture
}
showProgressFor(listOf(node1, node2))
showConsensusFor(listOf(node1, node2, regulators[0]))
val instigator = Instigator(node2.info.legalIdentity, AutoOffer(notary.info.notaryIdentity, irs), node1.keyPair!!)
val instigatorTx: ListenableFuture<SignedTransaction> = node1.services.startProtocol(instigator).resultFuture
val instigatorTx: ListenableFuture<SignedTransaction> = node1.services.startFlow(instigator).resultFuture
return Futures.allAsList(instigatorTx, acceptorTx).flatMap { instigatorTx }
}

View File

@ -4,12 +4,12 @@ import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.crypto.generateKeyPair
import net.corda.core.flatMap
import net.corda.core.flows.FlowLogic
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.CityDatabase
import net.corda.core.node.PhysicalLocation
import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.containsType
import net.corda.core.protocols.ProtocolLogic
import net.corda.core.then
import net.corda.core.utilities.ProgressTracker
import net.corda.irs.api.NodeInterestRates
@ -31,7 +31,7 @@ import java.util.*
/**
* Base class for network simulations that are based on the unit test / mock environment.
*
* Sets up some nodes that can run protocols between each other, and exposes their progress trackers. Provides banks
* Sets up some nodes that can run flows between each other, and exposes their progress trackers. Provides banks
* in a few cities around the world.
*/
abstract class Simulation(val networkSendManuallyPumped: Boolean,
@ -202,9 +202,9 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
val clocks = (serviceProviders + regulators + banks).map { it.services.clock as TestClock }
// These are used from the network visualiser tool.
private val _allProtocolSteps = PublishSubject.create<Pair<SimulatedNode, ProgressTracker.Change>>()
private val _allFlowSteps = PublishSubject.create<Pair<SimulatedNode, ProgressTracker.Change>>()
private val _doneSteps = PublishSubject.create<Collection<SimulatedNode>>()
@Suppress("unused") val allProtocolSteps: Observable<Pair<SimulatedNode, ProgressTracker.Change>> = _allProtocolSteps
@Suppress("unused") val allFlowSteps: Observable<Pair<SimulatedNode, ProgressTracker.Change>> = _allFlowSteps
@Suppress("unused") val doneSteps: Observable<Collection<SimulatedNode>> = _doneSteps
private var pumpCursor = 0
@ -268,16 +268,16 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
protected fun showProgressFor(nodes: List<SimulatedNode>) {
nodes.forEach { node ->
node.smm.changes.filter { it.addOrRemove == AddOrRemove.ADD }.subscribe {
linkProtocolProgress(node, it.logic)
linkFlowProgress(node, it.logic)
}
}
}
private fun linkProtocolProgress(node: SimulatedNode, protocol: ProtocolLogic<*>) {
val pt = protocol.progressTracker ?: return
private fun linkFlowProgress(node: SimulatedNode, flow: FlowLogic<*>) {
val pt = flow.progressTracker ?: return
pt.changes.subscribe { change: ProgressTracker.Change ->
// Runs on node thread.
_allProtocolSteps.onNext(Pair(node, change))
_allFlowSteps.onNext(Pair(node, change))
}
}
@ -289,10 +289,10 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
}
}
private fun linkConsensus(nodes: Collection<SimulatedNode>, protocol: ProtocolLogic<*>) {
protocol.progressTracker?.changes?.subscribe { change: ProgressTracker.Change ->
private fun linkConsensus(nodes: Collection<SimulatedNode>, flow: FlowLogic<*>) {
flow.progressTracker?.changes?.subscribe { change: ProgressTracker.Change ->
// Runs on node thread.
if (protocol.progressTracker!!.currentStep == ProgressTracker.DONE) {
if (flow.progressTracker!!.currentStep == ProgressTracker.DONE) {
_doneSteps.onNext(nodes)
}
}

View File

@ -10,13 +10,13 @@ import net.corda.core.contracts.OwnableState
import net.corda.core.contracts.`issued by`
import net.corda.core.days
import net.corda.core.flatMap
import net.corda.core.flows.FlowStateMachine
import net.corda.core.node.recordTransactions
import net.corda.core.protocols.ProtocolStateMachine
import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction
import net.corda.protocols.TwoPartyTradeProtocol.Buyer
import net.corda.protocols.TwoPartyTradeProtocol.Seller
import net.corda.testing.initiateSingleShotProtocol
import net.corda.flows.TwoPartyTradeFlow.Buyer
import net.corda.flows.TwoPartyTradeFlow.Seller
import net.corda.testing.initiateSingleShotFlow
import net.corda.testing.node.InMemoryMessagingNetwork
import java.time.Instant
@ -50,12 +50,12 @@ class TradeSimulation(runAsync: Boolean, latencyInjector: InMemoryMessagingNetwo
val amount = 1000.DOLLARS
val buyerFuture = buyer.initiateSingleShotProtocol(Seller::class) {
val buyerFuture = buyer.initiateSingleShotFlow(Seller::class) {
Buyer(it, notary.info.notaryIdentity, amount, CommercialPaper.State::class.java)
}.flatMap { (it.psm as ProtocolStateMachine<SignedTransaction>).resultFuture }
}.flatMap { (it.fsm as FlowStateMachine<SignedTransaction>).resultFuture }
val sellerKey = seller.services.legalIdentityKey
val sellerProtocol = Seller(
val sellerFlow = Seller(
buyer.info.legalIdentity,
notary.info,
issuance.tx.outRef<OwnableState>(0),
@ -65,7 +65,7 @@ class TradeSimulation(runAsync: Boolean, latencyInjector: InMemoryMessagingNetwo
showConsensusFor(listOf(buyer, seller, notary))
showProgressFor(listOf(buyer, seller))
val sellerFuture = seller.services.startProtocol(sellerProtocol).resultFuture
val sellerFuture = seller.services.startFlow(sellerFlow).resultFuture
return Futures.successfulAsList(buyerFuture, sellerFuture)
}

View File

@ -1,6 +1,6 @@
# Register a ServiceLoader service extending from net.corda.node.CordaPluginRegistry
net.corda.irs.plugin.IRSPlugin
net.corda.irs.api.NodeInterestRates$Plugin
net.corda.irs.protocols.AutoOfferProtocol$Plugin
net.corda.irs.protocols.ExitServerProtocol$Plugin
net.corda.irs.protocols.UpdateBusinessDayProtocol$Plugin
net.corda.irs.flows.AutoOfferFlow$Plugin
net.corda.irs.flows.ExitServerFlow$Plugin
net.corda.irs.flows.UpdateBusinessDayFlow$Plugin

View File

@ -15,16 +15,19 @@ import net.corda.core.transactions.FilteredTransaction
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.LogHelper
import net.corda.irs.api.NodeInterestRates
import net.corda.irs.protocols.RatesFixProtocol
import net.corda.testing.node.MockNetwork
import net.corda.irs.flows.RatesFixFlow
import net.corda.node.utilities.configureDatabase
import net.corda.node.utilities.databaseTransaction
import net.corda.testing.ALICE_PUBKEY
import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.makeTestDataSourceProperties
import org.jetbrains.exposed.sql.Database
import org.junit.*
import org.junit.After
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import java.io.Closeable
import java.time.Clock
import kotlin.test.assertEquals
@ -198,10 +201,10 @@ class NodeInterestRatesTest {
val oracle = n2.info.serviceIdentities(NodeInterestRates.type).first()
fun filterCommands(c: Command) = oracle.owningKey in c.signers && c.value is Fix
val filterFuns = FilterFuns(filterCommands = ::filterCommands)
val protocol = RatesFixProtocol(tx, filterFuns, oracle, fixOf, "0.675".bd, "0.1".bd)
val flow = RatesFixFlow(tx, filterFuns, oracle, fixOf, "0.675".bd, "0.1".bd)
LogHelper.setLevel("rates")
net.runNetwork()
val future = n1.services.startProtocol(protocol).resultFuture
val future = n1.services.startFlow(flow).resultFuture
net.runNetwork()
future.get()
// We should now have a valid signature over our tx from the oracle.

View File

@ -1,16 +1,5 @@
package net.corda.netmap
import net.corda.netmap.VisualiserViewModel.Style
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.serialization.deserialize
import net.corda.core.then
import net.corda.core.utilities.ProgressTracker
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.statemachine.StateMachineManager
import net.corda.simulation.IRSSimulation
import net.corda.simulation.Simulation
import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.MockNetwork
import javafx.animation.*
import javafx.application.Application
import javafx.application.Platform
@ -22,6 +11,17 @@ import javafx.scene.input.KeyCodeCombination
import javafx.scene.layout.VBox
import javafx.stage.Stage
import javafx.util.Duration
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.serialization.deserialize
import net.corda.core.then
import net.corda.core.utilities.ProgressTracker
import net.corda.netmap.VisualiserViewModel.Style
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.statemachine.StateMachineManager
import net.corda.simulation.IRSSimulation
import net.corda.simulation.Simulation
import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.MockNetwork
import rx.Scheduler
import rx.schedulers.Schedulers
import java.time.format.DateTimeFormatter
@ -74,8 +74,8 @@ class NetworkMapVisualiser : Application() {
viewModel.displayStyle = if ("--circle" in parameters.raw) { Style.CIRCLE } else { viewModel.displayStyle }
val simulation = viewModel.simulation
// Update the white-backgrounded label indicating what protocol step it's up to.
simulation.allProtocolSteps.observeOn(uiThread).subscribe { step: Pair<Simulation.SimulatedNode, ProgressTracker.Change> ->
// Update the white-backgrounded label indicating what flow step it's up to.
simulation.allFlowSteps.observeOn(uiThread).subscribe { step: Pair<Simulation.SimulatedNode, ProgressTracker.Change> ->
val (node, change) = step
val label = viewModel.nodesToWidgets[node]!!.statusLabel
if (change is ProgressTracker.Change.Position) {
@ -213,23 +213,23 @@ class NetworkMapVisualiser : Application() {
}
private fun bindSidebar() {
viewModel.simulation.allProtocolSteps.observeOn(uiThread).subscribe { step: Pair<Simulation.SimulatedNode, ProgressTracker.Change> ->
viewModel.simulation.allFlowSteps.observeOn(uiThread).subscribe { step: Pair<Simulation.SimulatedNode, ProgressTracker.Change> ->
val (node, change) = step
if (change is ProgressTracker.Change.Position) {
val tracker = change.tracker.topLevelTracker
if (change.newStep == ProgressTracker.DONE) {
if (change.tracker == tracker) {
// Protocol done; schedule it for removal in a few seconds. We batch them up to make nicer
// Flow done; schedule it for removal in a few seconds. We batch them up to make nicer
// animations.
updateProgressTrackerWidget(change)
println("Protocol done for ${node.info.legalIdentity.name}")
println("Flow done for ${node.info.legalIdentity.name}")
viewModel.doneTrackers += tracker
} else {
// Subprotocol is done; ignore it.
// Subflow is done; ignore it.
}
} else if (!viewModel.trackerBoxes.containsKey(tracker)) {
// New protocol started up; add.
// New flow started up; add.
val extraLabel = viewModel.simulation.extraNodeLabels[node]
val label = if (extraLabel != null) "${node.info.legalIdentity.name}: $extraLabel" else node.info.legalIdentity.name
val widget = view.buildProgressTrackerWidget(label, tracker.topLevelTracker)
@ -343,7 +343,7 @@ class NetworkMapVisualiser : Application() {
// Loopback messages are boring.
if (transfer.sender.myAddress == transfer.recipients) return false
// Network map push acknowledgements are boring.
if (NetworkMapService.PUSH_ACK_PROTOCOL_TOPIC in transfer.message.topicSession.topic) return false
if (NetworkMapService.PUSH_ACK_FLOW_TOPIC in transfer.message.topicSession.topic) return false
val message = transfer.message.data.deserialize<Any>()
val messageClassType = message.javaClass.name
when (messageClassType) {

View File

@ -17,9 +17,7 @@ import com.opengamma.strata.pricer.rate.ImmutableRatesProvider
import com.opengamma.strata.pricer.swap.DiscountingSwapProductPricer
import com.opengamma.strata.product.swap.ResolvedSwapTrade
import net.corda.core.utilities.loggerFor
import net.corda.vega.protocols.toCordaCompatible
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import net.corda.vega.flows.toCordaCompatible
import java.time.LocalDate
/**

View File

@ -9,12 +9,12 @@ import net.corda.core.node.services.dealsWith
import net.corda.vega.analytics.InitialMarginTriple
import net.corda.vega.contracts.IRSState
import net.corda.vega.contracts.PortfolioState
import net.corda.vega.flows.IRSTradeFlow
import net.corda.vega.flows.SimmFlow
import net.corda.vega.flows.SimmRevaluation
import net.corda.vega.portfolio.Portfolio
import net.corda.vega.portfolio.toPortfolio
import net.corda.vega.portfolio.toStateAndRef
import net.corda.vega.protocols.IRSTradeProtocol
import net.corda.vega.protocols.SimmProtocol
import net.corda.vega.protocols.SimmRevaluation
import java.time.LocalDate
import java.time.LocalDateTime
import javax.ws.rs.*
@ -153,7 +153,7 @@ class PortfolioApi(val services: ServiceHub) {
return withParty(partyName) {
val buyer = if (swap.buySell.isBuy) ownParty else it
val seller = if (swap.buySell.isSell) ownParty else it
services.invokeProtocolAsync(IRSTradeProtocol.Requester::class.java, swap.toData(buyer, seller), it).resultFuture.get()
services.invokeFlowAsync(IRSTradeFlow.Requester::class.java, swap.toData(buyer, seller), it).resultFuture.get()
Response.accepted().entity("{}").build()
}
}
@ -268,9 +268,9 @@ class PortfolioApi(val services: ServiceHub) {
return withParty(partyName) { otherParty ->
val existingSwap = getPortfolioWith(otherParty)
if (existingSwap == null) {
services.invokeProtocolAsync(SimmProtocol.Requester::class.java, otherParty, params.valuationDate).resultFuture.get()
services.invokeFlowAsync(SimmFlow.Requester::class.java, otherParty, params.valuationDate).resultFuture.get()
} else {
services.invokeProtocolAsync(SimmRevaluation.Initiator::class.java, getPortfolioStateAndRefWith(otherParty).ref, params.valuationDate).resultFuture.get()
services.invokeFlowAsync(SimmRevaluation.Initiator::class.java, getPortfolioStateAndRefWith(otherParty).ref, params.valuationDate).resultFuture.get()
}
withPortfolio(otherParty) { portfolioState ->

View File

@ -3,9 +3,9 @@ package net.corda.vega.contracts
import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.protocols.ProtocolLogicRefFactory
import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.transactions.TransactionBuilder
import net.corda.vega.protocols.SimmRevaluation
import net.corda.vega.flows.SimmRevaluation
import java.security.PublicKey
import java.time.LocalDate
import java.time.ZoneOffset
@ -31,9 +31,9 @@ data class PortfolioState(val portfolio: List<StateRef>,
override val participants: List<CompositeKey>
get() = parties.map { it.owningKey }
override fun nextScheduledActivity(thisStateRef: StateRef, protocolLogicRefFactory: ProtocolLogicRefFactory): ScheduledActivity {
val protocol = protocolLogicRefFactory.create(SimmRevaluation.Initiator::class.java, thisStateRef, LocalDate.now())
return ScheduledActivity(protocol, LocalDate.now().plus(1, ChronoUnit.DAYS).atStartOfDay().toInstant(ZoneOffset.UTC))
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity {
val flow = flowLogicRefFactory.create(SimmRevaluation.Initiator::class.java, thisStateRef, LocalDate.now())
return ScheduledActivity(flow, LocalDate.now().plus(1, ChronoUnit.DAYS).atStartOfDay().toInstant(ZoneOffset.UTC))
}
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {

View File

@ -6,7 +6,7 @@ import net.corda.core.crypto.Party
import net.corda.core.transactions.TransactionBuilder
/**
* Deals implementing this interface will be usable with the StateRevisionProtocol that allows arbitrary updates
* Deals implementing this interface will be usable with the StateRevisionFlow that allows arbitrary updates
* to a state. This is not really an "amendable" state (recall the Corda model - all states are immutable in the
* functional sense) however it can be amended and then re-written as another state into the ledger.
*/

Some files were not shown because too many files have changed in this diff Show More