mirror of
https://github.com/corda/corda.git
synced 2025-06-18 15:18:16 +00:00
Generic startProtocol and typesafe wrappers, per-protocol permissions, CashProtocol, remove executeCommand, move almost all Cash-related things to :finance
This commit is contained in:
@ -72,7 +72,7 @@ class APIServerImpl(val node: AbstractNode) : APIServer {
|
||||
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)
|
||||
return node.services.startProtocol(protocolInstance).resultFuture
|
||||
} else {
|
||||
throw UnsupportedOperationException("Unsupported ProtocolRef type: $type")
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ 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
|
||||
@ -43,6 +44,8 @@ import net.corda.node.services.transactions.ValidatingNotaryService
|
||||
import net.corda.node.services.vault.CashBalanceAsMetricsObserver
|
||||
import net.corda.node.services.vault.NodeVaultService
|
||||
import net.corda.node.utilities.*
|
||||
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
|
||||
@ -110,7 +113,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val netwo
|
||||
override val monitoringService: MonitoringService = MonitoringService(MetricRegistry())
|
||||
override val protocolLogicRefFactory: ProtocolLogicRefFactory get() = protocolLogicFactory
|
||||
|
||||
override fun <T> startProtocol(logic: ProtocolLogic<T>): ListenableFuture<T> = smm.add(logic).resultFuture
|
||||
override fun <T> startProtocol(logic: ProtocolLogic<T>): ProtocolStateMachine<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" }
|
||||
@ -307,8 +310,24 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val netwo
|
||||
}
|
||||
}
|
||||
|
||||
private val defaultProtocolWhiteList: Map<Class<out ProtocolLogic<*>>, Set<Class<*>>> = mapOf(
|
||||
CashProtocol::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) {
|
||||
val argumentWhitelistClassNames = HashSet(extraArgumentTypes.map { it.name })
|
||||
protocolClass.constructors.forEach {
|
||||
it.parameters.mapTo(argumentWhitelistClassNames) { it.type.name }
|
||||
}
|
||||
protocolWhitelist.merge(protocolClass.name, argumentWhitelistClassNames, { x, y -> x + y })
|
||||
}
|
||||
|
||||
for (plugin in pluginRegistries) {
|
||||
for ((className, classWhitelist) in plugin.requiredProtocols) {
|
||||
protocolWhitelist.merge(className, classWhitelist, { x, y -> x + y })
|
||||
|
@ -1,8 +1,7 @@
|
||||
package net.corda.node.internal
|
||||
|
||||
import net.corda.contracts.asset.Cash
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.Party
|
||||
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
|
||||
@ -11,16 +10,16 @@ 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.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.node.services.messaging.*
|
||||
import net.corda.node.services.statemachine.ProtocolStateMachineImpl
|
||||
import net.corda.node.services.statemachine.StateMachineManager
|
||||
import net.corda.node.utilities.databaseTransaction
|
||||
import net.corda.protocols.BroadcastTransactionProtocol
|
||||
import net.corda.protocols.FinalityProtocol
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import rx.Observable
|
||||
import java.security.KeyPair
|
||||
|
||||
/**
|
||||
* Server side implementations of RPCs available to MQ based client tools. Execution takes place on the server
|
||||
@ -31,10 +30,6 @@ class CordaRPCOpsImpl(
|
||||
val smm: StateMachineManager,
|
||||
val database: Database
|
||||
) : CordaRPCOps {
|
||||
companion object {
|
||||
const val CASH_PERMISSION = "CASH"
|
||||
}
|
||||
|
||||
override val protocolVersion: Int get() = 0
|
||||
|
||||
override fun networkMapUpdates(): Pair<List<NodeInfo>, Observable<NetworkMapCache.MapChange>> {
|
||||
@ -67,17 +62,6 @@ class CordaRPCOpsImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun executeCommand(command: ClientToServiceCommand): TransactionBuildResult {
|
||||
requirePermission(CASH_PERMISSION)
|
||||
return databaseTransaction(database) {
|
||||
when (command) {
|
||||
is ClientToServiceCommand.IssueCash -> issueCash(command)
|
||||
is ClientToServiceCommand.PayCash -> initiatePayment(command)
|
||||
is ClientToServiceCommand.ExitCash -> exitCash(command)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun nodeIdentity(): NodeInfo {
|
||||
return services.myInfo
|
||||
}
|
||||
@ -94,83 +78,13 @@ class CordaRPCOpsImpl(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make a lightweight protocol that manages this workflow, rather than embedding it directly in the service
|
||||
private fun initiatePayment(req: ClientToServiceCommand.PayCash): TransactionBuildResult {
|
||||
val builder: TransactionBuilder = TransactionType.General.Builder(null)
|
||||
// TODO: Have some way of restricting this to states the caller controls
|
||||
try {
|
||||
val (spendTX, keysForSigning) = services.vaultService.generateSpend(builder,
|
||||
req.amount.withoutIssuer(), req.recipient.owningKey, setOf(req.amount.token.issuer.party))
|
||||
|
||||
keysForSigning.keys.forEach {
|
||||
val key = services.keyManagementService.keys[it] ?: throw IllegalStateException("Could not find signing key for ${it.toStringShort()}")
|
||||
builder.signWith(KeyPair(it, key))
|
||||
}
|
||||
|
||||
val tx = spendTX.toSignedTransaction(checkSufficientSignatures = false)
|
||||
val protocol = FinalityProtocol(tx, setOf(req), setOf(req.recipient))
|
||||
return TransactionBuildResult.ProtocolStarted(
|
||||
smm.add(protocol).id,
|
||||
tx,
|
||||
"Cash payment transaction generated"
|
||||
)
|
||||
} catch(ex: InsufficientBalanceException) {
|
||||
return TransactionBuildResult.Failed(ex.message ?: "Insufficient balance")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make a lightweight protocol that manages this workflow, rather than embedding it directly in the service
|
||||
private fun exitCash(req: ClientToServiceCommand.ExitCash): TransactionBuildResult {
|
||||
val builder: TransactionBuilder = TransactionType.General.Builder(null)
|
||||
try {
|
||||
val issuer = PartyAndReference(services.myInfo.legalIdentity, req.issueRef)
|
||||
Cash().generateExit(builder, req.amount.issuedBy(issuer),
|
||||
services.vaultService.currentVault.statesOfType<Cash.State>().filter { it.state.data.owner == issuer.party.owningKey })
|
||||
val myKey = services.legalIdentityKey
|
||||
builder.signWith(myKey)
|
||||
|
||||
// Work out who the owners of the burnt states were
|
||||
val inputStatesNullable = services.vaultService.statesForRefs(builder.inputStates())
|
||||
val inputStates = inputStatesNullable.values.filterNotNull().map { it.data }
|
||||
if (inputStatesNullable.size != inputStates.size) {
|
||||
val unresolvedStateRefs = inputStatesNullable.filter { it.value == null }.map { it.key }
|
||||
throw InputStateRefResolveFailed(unresolvedStateRefs)
|
||||
}
|
||||
|
||||
// TODO: Is it safe to drop participants we don't know how to contact? Does not knowing how to contact them
|
||||
// count as a reason to fail?
|
||||
val participants: Set<Party> = inputStates.filterIsInstance<Cash.State>().map { services.identityService.partyFromKey(it.owner) }.filterNotNull().toSet()
|
||||
|
||||
// Commit the transaction
|
||||
val tx = builder.toSignedTransaction(checkSufficientSignatures = false)
|
||||
val protocol = FinalityProtocol(tx, setOf(req), participants)
|
||||
return TransactionBuildResult.ProtocolStarted(
|
||||
smm.add(protocol).id,
|
||||
tx,
|
||||
"Cash destruction transaction generated"
|
||||
)
|
||||
} catch (ex: InsufficientBalanceException) {
|
||||
return TransactionBuildResult.Failed(ex.message ?: "Insufficient balance")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make a lightweight protocol that manages this workflow, rather than embedding it directly in the service
|
||||
private fun issueCash(req: ClientToServiceCommand.IssueCash): TransactionBuildResult {
|
||||
val builder: TransactionBuilder = TransactionType.General.Builder(notary = null)
|
||||
val issuer = PartyAndReference(services.myInfo.legalIdentity, req.issueRef)
|
||||
Cash().generateIssue(builder, req.amount.issuedBy(issuer), req.recipient.owningKey, req.notary)
|
||||
val myKey = services.legalIdentityKey
|
||||
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
|
||||
val protocol = BroadcastTransactionProtocol(tx, setOf(req), setOf(req.recipient))
|
||||
return TransactionBuildResult.ProtocolStarted(
|
||||
smm.add(protocol).id,
|
||||
tx,
|
||||
"Cash issuance completed"
|
||||
override fun <T: Any> startProtocolGeneric(logicType: Class<out ProtocolLogic<T>>, vararg args: Any?): ProtocolHandle<T> {
|
||||
requirePermission(logicType.name)
|
||||
val stateMachine = services.invokeProtocolAsync(logicType, *args) as ProtocolStateMachineImpl<T>
|
||||
return ProtocolHandle(
|
||||
id = stateMachine.id,
|
||||
progress = stateMachine.logic.progressTracker?.changes ?: Observable.empty<ProgressTracker.Change>(),
|
||||
returnValue = stateMachine.resultFuture.toObservable()
|
||||
)
|
||||
}
|
||||
|
||||
class InputStateRefResolveFailed(stateRefs: List<StateRef>) :
|
||||
Exception("Failed to resolve input StateRefs $stateRefs")
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.corda.node.services
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import net.corda.core.protocols.ProtocolLogic
|
||||
import net.corda.node.services.config.getListOrElse
|
||||
|
||||
/**
|
||||
@ -9,7 +10,7 @@ import net.corda.node.services.config.getListOrElse
|
||||
* to. These permissions are represented as [String]s to allow RPC implementations to add their own permissioning.
|
||||
*/
|
||||
interface RPCUserService {
|
||||
fun getUser(usename: String): User?
|
||||
fun getUser(username: String): User?
|
||||
val users: List<User>
|
||||
}
|
||||
|
||||
@ -25,13 +26,13 @@ class RPCUserServiceImpl(config: Config) : RPCUserService {
|
||||
val username = it.getString("user")
|
||||
require(username.matches("\\w+".toRegex())) { "Username $username contains invalid characters" }
|
||||
val password = it.getString("password")
|
||||
val permissions = it.getListOrElse<String>("permissions") { emptyList() }.map(String::toUpperCase).toSet()
|
||||
val permissions = it.getListOrElse<String>("permissions") { emptyList() }.toSet()
|
||||
User(username, password, permissions)
|
||||
}
|
||||
.associateBy(User::username)
|
||||
}
|
||||
|
||||
override fun getUser(usename: String): User? = _users[usename]
|
||||
override fun getUser(username: String): User? = _users[username]
|
||||
|
||||
override val users: List<User> get() = _users.values.toList()
|
||||
}
|
||||
@ -39,3 +40,5 @@ class RPCUserServiceImpl(config: Config) : RPCUserService {
|
||||
data class User(val username: String, val password: String, val permissions: Set<String>) {
|
||||
override fun toString(): String = "${javaClass.simpleName}($username, permissions=$permissions)"
|
||||
}
|
||||
|
||||
inline fun <reified P : ProtocolLogic<*>> startProtocolPermission(): String = P::class.java.name
|
||||
|
@ -6,6 +6,7 @@ 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 org.slf4j.LoggerFactory
|
||||
@ -67,9 +68,9 @@ 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>): ListenableFuture<T>
|
||||
abstract fun <T> startProtocol(logic: ProtocolLogic<T>): ProtocolStateMachine<T>
|
||||
|
||||
override fun <T : Any> invokeProtocolAsync(logicType: Class<out ProtocolLogic<T>>, vararg args: Any?): ListenableFuture<T> {
|
||||
override fun <T : Any> invokeProtocolAsync(logicType: Class<out ProtocolLogic<T>>, vararg args: Any?): ProtocolStateMachine<T> {
|
||||
val logicRef = protocolLogicRefFactory.create(logicType, *args)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val logic = protocolLogicRefFactory.toProtocolLogic(logicRef) as ProtocolLogic<T>
|
||||
|
@ -1,6 +1,5 @@
|
||||
package net.corda.node.services.messaging
|
||||
|
||||
import net.corda.core.contracts.ClientToServiceCommand
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
import net.corda.core.crypto.SecureHash
|
||||
@ -8,8 +7,10 @@ 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.StateMachineManager
|
||||
import net.corda.node.utilities.AddOrRemove
|
||||
@ -54,31 +55,9 @@ sealed class StateMachineUpdate(val id: StateMachineRunId) {
|
||||
}
|
||||
}
|
||||
|
||||
sealed class TransactionBuildResult {
|
||||
/**
|
||||
* State indicating that a protocol is managing this request, and that the client should track protocol state machine
|
||||
* updates for further information. The monitor will separately receive notification of the state machine having been
|
||||
* added, as it would any other state machine. This response is used solely to enable the monitor to identify
|
||||
* the state machine (and its progress) as associated with the request.
|
||||
*
|
||||
* @param transaction the transaction created as a result, in the case where the protocol has completed.
|
||||
*/
|
||||
class ProtocolStarted(val id: StateMachineRunId, val transaction: SignedTransaction?, val message: String?) : TransactionBuildResult() {
|
||||
override fun toString() = "Started($message)"
|
||||
}
|
||||
|
||||
/**
|
||||
* 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?) : TransactionBuildResult() {
|
||||
override fun toString() = "Failed($message)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RPC operations that the node exposes to clients using the Java client library. These can be called from
|
||||
* client apps and are implemented by the node in the [ServerRPCOps] class.
|
||||
* client apps and are implemented by the node in the [CordaRPCOpsImpl] class.
|
||||
*/
|
||||
interface CordaRPCOps : RPCOps {
|
||||
/**
|
||||
@ -113,15 +92,17 @@ interface CordaRPCOps : RPCOps {
|
||||
fun networkMapUpdates(): Pair<List<NodeInfo>, Observable<NetworkMapCache.MapChange>>
|
||||
|
||||
/**
|
||||
* Executes the given command if the user is permissioned to do so, possibly triggering cash creation etc.
|
||||
* TODO: The signature of this is weird because it's the remains of an old service call, we should have a call for each command instead.
|
||||
* Start the given protocol with the given arguments, returning an [Observable] with a single observation of the
|
||||
* result of running the protocol.
|
||||
*/
|
||||
fun executeCommand(command: ClientToServiceCommand): TransactionBuildResult
|
||||
@RPCReturnsObservables
|
||||
fun <T: Any> startProtocolGeneric(logicType: Class<out ProtocolLogic<T>>, vararg args: Any?): ProtocolHandle<T>
|
||||
|
||||
/**
|
||||
* Returns Node's identity, assuming this will not change while the node is running.
|
||||
*/
|
||||
fun nodeIdentity(): NodeInfo
|
||||
|
||||
/*
|
||||
* Add note(s) to an existing Vault transaction
|
||||
*/
|
||||
@ -132,3 +113,50 @@ interface CordaRPCOps : RPCOps {
|
||||
*/
|
||||
fun getVaultTransactionNotes(txnId: SecureHash): Iterable<String>
|
||||
}
|
||||
|
||||
/**
|
||||
* These allow type safe invocations of protocols, e.g.:
|
||||
*
|
||||
* val rpc: CordaRPCOps = (..)
|
||||
* rpc.startProtocol(::ResolveTransactionsProtocol, 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.
|
||||
*/
|
||||
inline fun <T : Any, reified R : ProtocolLogic<T>> CordaRPCOps.startProtocol(
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
protocolConstructor: () -> R
|
||||
) = startProtocolGeneric(R::class.java)
|
||||
inline fun <T : Any, A, reified R : ProtocolLogic<T>> CordaRPCOps.startProtocol(
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
protocolConstructor: (A) -> R,
|
||||
arg0: A
|
||||
) = startProtocolGeneric(R::class.java, arg0)
|
||||
inline fun <T : Any, A, B, reified R : ProtocolLogic<T>> CordaRPCOps.startProtocol(
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
protocolConstructor: (A, B) -> R,
|
||||
arg0: A,
|
||||
arg1: B
|
||||
) = startProtocolGeneric(R::class.java, arg0, arg1)
|
||||
inline fun <T : Any, A, B, C, reified R: ProtocolLogic<T>> CordaRPCOps.startProtocol(
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
protocolConstructor: (A, B, C) -> R,
|
||||
arg0: A,
|
||||
arg1: B,
|
||||
arg2: C
|
||||
) = startProtocolGeneric(R::class.java, arg0, arg1, arg2)
|
||||
inline fun <T : Any, A, B, C, D, reified R : ProtocolLogic<T>> CordaRPCOps.startProtocol(
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
protocolConstructor: (A, B, C, D) -> R,
|
||||
arg0: A,
|
||||
arg1: B,
|
||||
arg2: C,
|
||||
arg3: D
|
||||
) = startProtocolGeneric(R::class.java, arg0, arg1, arg2, arg3)
|
||||
|
||||
data class ProtocolHandle<A>(
|
||||
val id: StateMachineRunId,
|
||||
val progress: Observable<ProgressTracker.Change>,
|
||||
val returnValue: Observable<A>
|
||||
)
|
||||
|
||||
|
@ -28,6 +28,7 @@ import net.corda.core.serialization.*
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.node.services.User
|
||||
import net.corda.protocols.TransactionBuildResult
|
||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey
|
||||
import org.objenesis.strategy.StdInstantiatorStrategy
|
||||
@ -204,6 +205,8 @@ private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null)
|
||||
register(RPCException::class.java)
|
||||
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)
|
||||
}
|
||||
|
||||
// Helper method, attempt to reduce boiler plate code
|
||||
|
@ -12,9 +12,13 @@ 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.network.NetworkMapService
|
||||
import net.corda.node.services.startProtocolPermission
|
||||
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
|
||||
@ -44,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(CordaRPCOpsImpl.CASH_PERMISSION)))
|
||||
CURRENT_RPC_USER.set(User("user", "pwd", permissions = setOf(startProtocolPermission<CashProtocol>())))
|
||||
|
||||
stateMachineUpdates = rpc.stateMachinesAndUpdates().second
|
||||
transactions = rpc.verifiedTransactions().second
|
||||
@ -63,8 +67,8 @@ class CordaRPCOpsImplTest {
|
||||
|
||||
// Tell the monitoring service node to issue some cash
|
||||
val recipient = aliceNode.info.legalIdentity
|
||||
val outEvent = ClientToServiceCommand.IssueCash(Amount(quantity, GBP), ref, recipient, notaryNode.info.notaryIdentity)
|
||||
rpc.executeCommand(outEvent)
|
||||
val outEvent = CashCommand.IssueCash(Amount(quantity, GBP), ref, recipient, notaryNode.info.notaryIdentity)
|
||||
rpc.startProtocol(::CashProtocol, outEvent)
|
||||
network.runNetwork()
|
||||
|
||||
val expectedState = Cash.State(Amount(quantity,
|
||||
@ -101,7 +105,7 @@ class CordaRPCOpsImplTest {
|
||||
@Test
|
||||
fun `issue and move`() {
|
||||
|
||||
rpc.executeCommand(ClientToServiceCommand.IssueCash(
|
||||
rpc.startProtocol(::CashProtocol, CashCommand.IssueCash(
|
||||
amount = Amount(100, USD),
|
||||
issueRef = OpaqueBytes(ByteArray(1, { 1 })),
|
||||
recipient = aliceNode.info.legalIdentity,
|
||||
@ -110,7 +114,7 @@ class CordaRPCOpsImplTest {
|
||||
|
||||
network.runNetwork()
|
||||
|
||||
rpc.executeCommand(ClientToServiceCommand.PayCash(
|
||||
rpc.startProtocol(::CashProtocol, CashCommand.PayCash(
|
||||
amount = Amount(100, Issued(PartyAndReference(aliceNode.info.legalIdentity, OpaqueBytes(ByteArray(1, { 1 }))), USD)),
|
||||
recipient = aliceNode.info.legalIdentity
|
||||
))
|
||||
@ -182,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.executeCommand(ClientToServiceCommand.IssueCash(
|
||||
rpc.startProtocol(::CashProtocol, CashCommand.IssueCash(
|
||||
amount = Amount(100, USD),
|
||||
issueRef = OpaqueBytes(ByteArray(1, { 1 })),
|
||||
recipient = aliceNode.info.legalIdentity,
|
||||
|
@ -53,7 +53,7 @@ class AttachmentTests {
|
||||
network.runNetwork()
|
||||
val f1 = n1.services.startProtocol(FetchAttachmentsProtocol(setOf(id), n0.info.legalIdentity))
|
||||
network.runNetwork()
|
||||
assertEquals(0, f1.get().fromDisk.size)
|
||||
assertEquals(0, f1.resultFuture.get().fromDisk.size)
|
||||
|
||||
// Verify it was inserted into node one's store.
|
||||
val attachment = n1.storage.attachments.openAttachment(id)!!
|
||||
@ -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)).get()
|
||||
val response: FetchDataProtocol.Result<Attachment> = n1.services.startProtocol(FetchAttachmentsProtocol(setOf(id), n0.info.legalIdentity)).resultFuture.get()
|
||||
assertEquals(attachment, response.fromDisk[0])
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ class AttachmentTests {
|
||||
network.runNetwork()
|
||||
val f1 = n1.services.startProtocol(FetchAttachmentsProtocol(setOf(hash), n0.info.legalIdentity))
|
||||
network.runNetwork()
|
||||
val e = assertFailsWith<FetchDataProtocol.HashNotFound> { rootCauseExceptions { f1.get() } }
|
||||
val e = assertFailsWith<FetchDataProtocol.HashNotFound> { rootCauseExceptions { f1.resultFuture.get() } }
|
||||
assertEquals(hash, e.requested)
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ class AttachmentTests {
|
||||
val f1 = n1.services.startProtocol(FetchAttachmentsProtocol(setOf(id), n0.info.legalIdentity))
|
||||
network.runNetwork()
|
||||
assertFailsWith<FetchDataProtocol.DownloadedVsRequestedDataMismatch> {
|
||||
rootCauseExceptions { f1.get() }
|
||||
rootCauseExceptions { f1.resultFuture.get() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
package net.corda.node.services
|
||||
|
||||
import com.codahale.metrics.MetricRegistry
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import net.corda.core.crypto.Party
|
||||
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
|
||||
@ -79,7 +79,7 @@ open class MockServiceHubInternal(
|
||||
|
||||
override fun recordTransactions(txs: Iterable<SignedTransaction>) = recordTransactionsInternal(txStorageService, txs)
|
||||
|
||||
override fun <T> startProtocol(logic: ProtocolLogic<T>): ListenableFuture<T> = smm.add(logic).resultFuture
|
||||
override fun <T> startProtocol(logic: ProtocolLogic<T>): ProtocolStateMachine<T> = smm.add(logic)
|
||||
|
||||
override fun registerProtocolInitiator(markerClass: KClass<*>, protocolFactory: (Party) -> ProtocolLogic<*>) {
|
||||
protocolFactories[markerClass.java] = protocolFactory
|
||||
|
@ -53,7 +53,7 @@ class NotaryChangeTests {
|
||||
|
||||
net.runNetwork()
|
||||
|
||||
val newState = future.get()
|
||||
val newState = future.resultFuture.get()
|
||||
assertEquals(newState.state.notary, newNotary)
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ class NotaryChangeTests {
|
||||
|
||||
net.runNetwork()
|
||||
|
||||
val newState = future.get()
|
||||
val newState = future.resultFuture.get()
|
||||
assertEquals(newState.state.notary, newNotary)
|
||||
val loadedStateA = clientNodeA.services.loadState(newState.ref)
|
||||
val loadedStateB = clientNodeB.services.loadState(newState.ref)
|
||||
@ -82,7 +82,7 @@ class NotaryChangeTests {
|
||||
|
||||
net.runNetwork()
|
||||
|
||||
val ex = assertFailsWith(ExecutionException::class) { future.get() }
|
||||
val ex = assertFailsWith(ExecutionException::class) { future.resultFuture.get() }
|
||||
val error = (ex.cause as StateReplacementException).error
|
||||
assertTrue(error is StateReplacementRefused)
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ class NotaryServiceTests {
|
||||
|
||||
net.runNetwork()
|
||||
|
||||
val ex = assertFailsWith(ExecutionException::class) { future.get() }
|
||||
val ex = assertFailsWith(ExecutionException::class) { future.resultFuture.get() }
|
||||
val notaryError = (ex.cause as NotaryException).error as NotaryError.Conflict
|
||||
assertEquals(notaryError.tx, stx.tx)
|
||||
notaryError.conflict.verified()
|
||||
@ -110,7 +110,7 @@ class NotaryServiceTests {
|
||||
|
||||
private fun runNotaryClient(stx: SignedTransaction): ListenableFuture<DigitalSignature.WithKey> {
|
||||
val protocol = NotaryProtocol.Client(stx)
|
||||
val future = clientNode.services.startProtocol(protocol)
|
||||
val future = clientNode.services.startProtocol(protocol).resultFuture
|
||||
net.runNetwork()
|
||||
return future
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ class ValidatingNotaryServiceTests {
|
||||
|
||||
private fun runClient(stx: SignedTransaction): ListenableFuture<DigitalSignature.WithKey> {
|
||||
val protocol = NotaryProtocol.Client(stx)
|
||||
val future = clientNode.services.startProtocol(protocol)
|
||||
val future = clientNode.services.startProtocol(protocol).resultFuture
|
||||
net.runNetwork()
|
||||
return future
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ class DataVendingServiceTests {
|
||||
|
||||
private class NotifyTxProtocol(val otherParty: Party, val stx: SignedTransaction) : ProtocolLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() = send(otherParty, NotifyTxRequest(stx, emptySet()))
|
||||
override fun call() = send(otherParty, NotifyTxRequest(stx))
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user