CORDA-3959: Eliminate InternalCordaRPCOps (#6600)

* CORDA-3959: Make `ThreadContextAdjustingRpcOpsProxy` flexible for `RPCOps` it uses

* CORDA-3959: More changes towards supporting multiple `RPCOps` implementations

* CORDA-3959: Detekt baseline update

* CORDA-3959: Integration test compilation fix

* CORDA-3959: Introduce `CheckpointRPCOpsImpl` and wire it on

* CORDA-3959: Use multiple RPCOps interfaces in the shell commands

* CORDA-3959: Detekt baseline update

* CORDA-3959: Update RPCPermissionsTests

* CORDA-3959: Update RPCSecurityManagerTest

* CORDA-3959: Remove deprecated marker and rename the property

* CORDA-3959: Detekt baseline

* CORDA-3959: Introduce AttachmentTrustInfoRPCOpsImpl and wire it on

* CORDA-3959: Delete `InternalCordaRPCOps`

* CORDA-3959: Detekt baseline update

* CORDA-3959: Rename `CheckpointRPCOps` to `FlowManagerRPCOps`
This commit is contained in:
Viktor Kolomeyko
2020-08-13 14:41:52 +01:00
committed by GitHub
parent c12c582eb4
commit b81eb1559d
46 changed files with 643 additions and 318 deletions

View File

@ -12,7 +12,6 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.PLATFORM_VERSION import net.corda.core.internal.PLATFORM_VERSION
import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.VisibleForTesting
import net.corda.core.internal.createInstancesOfClassesImplementing import net.corda.core.internal.createInstancesOfClassesImplementing
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.messaging.ClientRpcSslOptions import net.corda.core.messaging.ClientRpcSslOptions
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.serialization.SerializationCustomSerializer import net.corda.core.serialization.SerializationCustomSerializer
@ -494,7 +493,7 @@ class CordaRPCClient private constructor(
} }
} }
private fun getRpcClient(): RPCClient<InternalCordaRPCOps> { private fun getRpcClient(): RPCClient<CordaRPCOps> {
return when { return when {
// Client->RPC broker // Client->RPC broker
haAddressPool.isEmpty() -> RPCClient( haAddressPool.isEmpty() -> RPCClient(
@ -619,7 +618,7 @@ class CordaRPCClient private constructor(
) )
} else { } else {
CordaRPCConnection(getRpcClient().start( CordaRPCConnection(getRpcClient().start(
InternalCordaRPCOps::class.java, CordaRPCOps::class.java,
username, username,
password, password,
externalTrace, externalTrace,

View File

@ -19,7 +19,6 @@ import net.corda.core.internal.LazyStickyPool
import net.corda.core.internal.LifeCycle import net.corda.core.internal.LifeCycle
import net.corda.core.internal.NamedCacheFactory import net.corda.core.internal.NamedCacheFactory
import net.corda.core.internal.ThreadBox import net.corda.core.internal.ThreadBox
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.internal.times import net.corda.core.internal.times
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
@ -363,10 +362,10 @@ internal class RPCClientProxyHandler(
private fun produceMethodFullyQualifiedName(method: Method) : String { private fun produceMethodFullyQualifiedName(method: Method) : String {
/* /*
* Until version 4.3, rpc calls did not include class names. * Until version 4.3, rpc calls did not include class names.
* Up to this version, only CordaRPCOps and InternalCordaRPCOps were supported. * Up to this version, only CordaRPCOps was supported.
* So, for these classes only methods are sent across the wire to preserve backwards compatibility. * So, for these classes only methods are sent across the wire to preserve backwards compatibility.
*/ */
return if (CordaRPCOps::class.java == rpcOpsClass || InternalCordaRPCOps::class.java == rpcOpsClass) { return if (CordaRPCOps::class.java == rpcOpsClass) {
method.name method.name
} else { } else {
rpcOpsClass.name + CLASS_METHOD_DIVIDER + method.name rpcOpsClass.name + CLASS_METHOD_DIVIDER + method.name

View File

@ -19,7 +19,6 @@ import net.corda.client.rpc.internal.ReconnectingCordaRPCOps.ReconnectingRPCConn
import net.corda.client.rpc.internal.ReconnectingCordaRPCOps.ReconnectingRPCConnection.CurrentState.UNCONNECTED import net.corda.client.rpc.internal.ReconnectingCordaRPCOps.ReconnectingRPCConnection.CurrentState.UNCONNECTED
import net.corda.client.rpc.reconnect.CouldNotStartFlowException import net.corda.client.rpc.reconnect.CouldNotStartFlowException
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.internal.min import net.corda.core.internal.min
import net.corda.core.internal.times import net.corda.core.internal.times
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
@ -65,7 +64,7 @@ import java.util.concurrent.TimeUnit
// ReconnectingObservables and other things can attach themselves as listeners for reconnect events. // ReconnectingObservables and other things can attach themselves as listeners for reconnect events.
class ReconnectingCordaRPCOps private constructor( class ReconnectingCordaRPCOps private constructor(
val reconnectingRPCConnection: ReconnectingRPCConnection val reconnectingRPCConnection: ReconnectingRPCConnection
) : InternalCordaRPCOps by proxy(reconnectingRPCConnection) { ) : CordaRPCOps by proxy(reconnectingRPCConnection) {
constructor( constructor(
nodeHostAndPorts: List<NetworkHostAndPort>, nodeHostAndPorts: List<NetworkHostAndPort>,
username: String, username: String,
@ -86,11 +85,11 @@ class ReconnectingCordaRPCOps private constructor(
observersPool)) observersPool))
private companion object { private companion object {
private val log = contextLogger() private val log = contextLogger()
private fun proxy(reconnectingRPCConnection: ReconnectingRPCConnection): InternalCordaRPCOps { private fun proxy(reconnectingRPCConnection: ReconnectingRPCConnection): CordaRPCOps {
return Proxy.newProxyInstance( return Proxy.newProxyInstance(
this::class.java.classLoader, this::class.java.classLoader,
arrayOf(InternalCordaRPCOps::class.java), arrayOf(CordaRPCOps::class.java),
ErrorInterceptingHandler(reconnectingRPCConnection)) as InternalCordaRPCOps ErrorInterceptingHandler(reconnectingRPCConnection)) as CordaRPCOps
} }
} }
private val retryFlowsPool = Executors.newScheduledThreadPool(1) private val retryFlowsPool = Executors.newScheduledThreadPool(1)

View File

@ -1,6 +1,8 @@
package net.corda.client.rpc package net.corda.client.rpc
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.node.internal.rpc.proxies.RpcAuthHelper.methodFullName
import net.corda.node.services.rpc.rpcContext import net.corda.node.services.rpc.rpcContext
import net.corda.testing.node.User import net.corda.testing.node.User
import net.corda.testing.node.internal.RPCDriverDSL import net.corda.testing.node.internal.RPCDriverDSL
@ -25,16 +27,17 @@ class RPCPermissionsTests : AbstractRPCTest() {
fun validatePermission(method: String, target: String? = null) fun validatePermission(method: String, target: String? = null)
} }
class TestOpsImpl : TestOps { private class TestOpsImpl : TestOps {
override val protocolVersion = 1000 override val protocolVersion = 1000
override fun validatePermission(method: String, target: String?) { override fun validatePermission(method: String, target: String?) {
val methodFullName = methodFullName(CordaRPCOps::class.java, method)
val authorized = if (target == null) { val authorized = if (target == null) {
rpcContext().isPermitted(method) rpcContext().isPermitted(methodFullName)
} else { } else {
rpcContext().isPermitted(method, target) rpcContext().isPermitted(methodFullName, target)
} }
if (!authorized) { if (!authorized) {
throw PermissionException("RPC user not authorized") throw PermissionException("RPC user not authorized for: $method")
} }
} }
} }

View File

@ -86,7 +86,7 @@ class ContractUpgradeFlowRPCTest : WithContracts, WithFinality {
return startRpcClient<CordaRPCOps>( return startRpcClient<CordaRPCOps>(
rpcAddress = startRpcServer( rpcAddress = startRpcServer(
rpcUser = user, rpcUser = user,
ops = node.rpcOps ops = node.cordaRPCOps
).get().broker.hostAndPort!!, ).get().broker.hostAndPort!!,
username = user.username, username = user.username,
password = user.password password = user.password

View File

@ -0,0 +1,11 @@
package net.corda.core.internal.messaging
import net.corda.core.internal.AttachmentTrustInfo
import net.corda.core.messaging.RPCOps
interface AttachmentTrustInfoRPCOps : RPCOps {
/**
* Get all the attachment trust information
*/
val attachmentTrustInfos: List<AttachmentTrustInfo>
}

View File

@ -0,0 +1,13 @@
package net.corda.core.internal.messaging
import net.corda.core.messaging.RPCOps
/**
* RPC operations to perform operations related to flows including management of associated persistent states like checkpoints.
*/
interface FlowManagerRPCOps : RPCOps {
/**
* Dump all the current flow checkpoints as JSON into a zip file in the node's log directory.
*/
fun dumpCheckpoints()
}

View File

@ -1,16 +0,0 @@
package net.corda.core.internal.messaging
import net.corda.core.internal.AttachmentTrustInfo
import net.corda.core.messaging.CordaRPCOps
/**
* Contains internal RPC functions that should not be publicly exposed in [CordaRPCOps]
*/
interface InternalCordaRPCOps : CordaRPCOps {
/** Dump all the current flow checkpoints as JSON into a zip file in the node's log directory. */
fun dumpCheckpoints()
/** Get all attachment trust information */
val attachmentTrustInfos: List<AttachmentTrustInfo>
}

View File

@ -151,7 +151,7 @@
<ID>ComplexMethod:NetworkRegistrationHelper.kt$NetworkRegistrationHelper$ private fun pollServerForCertificates(requestId: String): List&lt;X509Certificate&gt;</ID> <ID>ComplexMethod:NetworkRegistrationHelper.kt$NetworkRegistrationHelper$ private fun pollServerForCertificates(requestId: String): List&lt;X509Certificate&gt;</ID>
<ID>ComplexMethod:NewTransaction.kt$NewTransaction$fun show(window: Window)</ID> <ID>ComplexMethod:NewTransaction.kt$NewTransaction$fun show(window: Window)</ID>
<ID>ComplexMethod:NewTransaction.kt$NewTransaction$private fun newTransactionDialog(window: Window)</ID> <ID>ComplexMethod:NewTransaction.kt$NewTransaction$private fun newTransactionDialog(window: Window)</ID>
<ID>ComplexMethod:Node.kt$Node$override fun startMessagingService(rpcOps: RPCOps, nodeInfo: NodeInfo, myNotaryIdentity: PartyAndCertificate?, networkParameters: NetworkParameters)</ID> <ID>ComplexMethod:Node.kt$Node$override fun startMessagingService(rpcOps: List&lt;RPCOps&gt;, nodeInfo: NodeInfo, myNotaryIdentity: PartyAndCertificate?, networkParameters: NetworkParameters)</ID>
<ID>ComplexMethod:NodeNamedCache.kt$DefaultNamedCacheFactory$open protected fun &lt;K, V&gt; configuredForNamed(caffeine: Caffeine&lt;K, V&gt;, name: String): Caffeine&lt;K, V&gt;</ID> <ID>ComplexMethod:NodeNamedCache.kt$DefaultNamedCacheFactory$open protected fun &lt;K, V&gt; configuredForNamed(caffeine: Caffeine&lt;K, V&gt;, name: String): Caffeine&lt;K, V&gt;</ID>
<ID>ComplexMethod:NodeVaultService.kt$NodeVaultService$@Throws(VaultQueryException::class) private fun &lt;T : ContractState&gt; _queryBy(criteria: QueryCriteria, paging_: PageSpecification, sorting: Sort, contractStateType: Class&lt;out T&gt;, skipPagingChecks: Boolean): Vault.Page&lt;T&gt;</ID> <ID>ComplexMethod:NodeVaultService.kt$NodeVaultService$@Throws(VaultQueryException::class) private fun &lt;T : ContractState&gt; _queryBy(criteria: QueryCriteria, paging_: PageSpecification, sorting: Sort, contractStateType: Class&lt;out T&gt;, skipPagingChecks: Boolean): Vault.Page&lt;T&gt;</ID>
<ID>ComplexMethod:NodeVaultService.kt$NodeVaultService$private fun makeUpdates(batch: Iterable&lt;CoreTransaction&gt;, statesToRecord: StatesToRecord, previouslySeen: Boolean): List&lt;Vault.Update&lt;ContractState&gt;&gt;</ID> <ID>ComplexMethod:NodeVaultService.kt$NodeVaultService$private fun makeUpdates(batch: Iterable&lt;CoreTransaction&gt;, statesToRecord: StatesToRecord, previouslySeen: Boolean): List&lt;Vault.Update&lt;ContractState&gt;&gt;</ID>
@ -159,15 +159,12 @@
<ID>ComplexMethod:ObjectDiffer.kt$ObjectDiffer$fun diff(a: Any?, b: Any?): DiffTree?</ID> <ID>ComplexMethod:ObjectDiffer.kt$ObjectDiffer$fun diff(a: Any?, b: Any?): DiffTree?</ID>
<ID>ComplexMethod:Obligation.kt$Obligation$override fun verify(tx: LedgerTransaction)</ID> <ID>ComplexMethod:Obligation.kt$Obligation$override fun verify(tx: LedgerTransaction)</ID>
<ID>ComplexMethod:QuasarInstrumentationHook.kt$QuasarInstrumentationHookAgent.Companion$@JvmStatic fun premain(argumentsString: String?, instrumentation: Instrumentation)</ID> <ID>ComplexMethod:QuasarInstrumentationHook.kt$QuasarInstrumentationHookAgent.Companion$@JvmStatic fun premain(argumentsString: String?, instrumentation: Instrumentation)</ID>
<ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$ private fun close(notify: Boolean = true)</ID>
<ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$// The handler for Artemis messages. private fun artemisMessageHandler(message: ClientMessage)</ID> <ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$// The handler for Artemis messages. private fun artemisMessageHandler(message: ClientMessage)</ID>
<ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$// This is the general function that transforms a client side RPC to internal Artemis messages. override fun invoke(proxy: Any, method: Method, arguments: Array&lt;out Any?&gt;?): Any?</ID> <ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$// This is the general function that transforms a client side RPC to internal Artemis messages. override fun invoke(proxy: Any, method: Method, arguments: Array&lt;out Any?&gt;?): Any?</ID>
<ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$private fun attemptReconnect()</ID> <ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$private fun attemptReconnect()</ID>
<ID>ComplexMethod:RPCServer.kt$RPCServer$private fun clientArtemisMessageHandler(artemisMessage: ClientMessage)</ID> <ID>ComplexMethod:RPCServer.kt$RPCServer$private fun clientArtemisMessageHandler(artemisMessage: ClientMessage)</ID>
<ID>ComplexMethod:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps.ReconnectingRPCConnection$ private tailrec fun establishConnectionWithRetry( retryInterval: Duration, roundRobinIndex: Int = 0, retries: Int = -1 ): CordaRPCConnection?</ID> <ID>ComplexMethod:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps.ReconnectingRPCConnection$ private tailrec fun establishConnectionWithRetry( retryInterval: Duration, roundRobinIndex: Int = 0, retries: Int = -1 ): CordaRPCConnection?</ID>
<ID>ComplexMethod:RemoteTypeCarpenter.kt$SchemaBuildingRemoteTypeCarpenter$override fun carpent(typeInformation: RemoteTypeInformation): Type</ID> <ID>ComplexMethod:RemoteTypeCarpenter.kt$SchemaBuildingRemoteTypeCarpenter$override fun carpent(typeInformation: RemoteTypeInformation): Type</ID>
<ID>ComplexMethod:SchemaMigration.kt$SchemaMigration$ private fun migrateOlderDatabaseToUseLiquibase(existingCheckpoints: Boolean): Boolean</ID>
<ID>ComplexMethod:SchemaMigration.kt$SchemaMigration$private fun doRunMigration( run: Boolean, check: Boolean, existingCheckpoints: Boolean? = null )</ID>
<ID>ComplexMethod:SendTransactionFlow.kt$DataVendingFlow$@Suspendable override fun call(): Void?</ID> <ID>ComplexMethod:SendTransactionFlow.kt$DataVendingFlow$@Suspendable override fun call(): Void?</ID>
<ID>ComplexMethod:ShellCmdLineOptions.kt$ShellCmdLineOptions$private fun toConfigFile(): Config</ID> <ID>ComplexMethod:ShellCmdLineOptions.kt$ShellCmdLineOptions$private fun toConfigFile(): Config</ID>
<ID>ComplexMethod:StartedFlowTransition.kt$StartedFlowTransition$override fun transition(): TransitionResult</ID> <ID>ComplexMethod:StartedFlowTransition.kt$StartedFlowTransition$override fun transition(): TransitionResult</ID>
@ -176,7 +173,6 @@
<ID>ComplexMethod:TlsDiffAlgorithmsTest.kt$TlsDiffAlgorithmsTest$@Test(timeout=300_000) fun testClientServerTlsExchange()</ID> <ID>ComplexMethod:TlsDiffAlgorithmsTest.kt$TlsDiffAlgorithmsTest$@Test(timeout=300_000) fun testClientServerTlsExchange()</ID>
<ID>ComplexMethod:TlsDiffProtocolsTest.kt$TlsDiffProtocolsTest$@Test(timeout=300_000) fun testClientServerTlsExchange()</ID> <ID>ComplexMethod:TlsDiffProtocolsTest.kt$TlsDiffProtocolsTest$@Test(timeout=300_000) fun testClientServerTlsExchange()</ID>
<ID>ComplexMethod:TransactionUtils.kt$ fun createComponentGroups(inputs: List&lt;StateRef&gt;, outputs: List&lt;TransactionState&lt;ContractState&gt;&gt;, commands: List&lt;Command&lt;*&gt;&gt;, attachments: List&lt;SecureHash&gt;, notary: Party?, timeWindow: TimeWindow?, references: List&lt;StateRef&gt;, networkParametersHash: SecureHash?): List&lt;ComponentGroup&gt;</ID> <ID>ComplexMethod:TransactionUtils.kt$ fun createComponentGroups(inputs: List&lt;StateRef&gt;, outputs: List&lt;TransactionState&lt;ContractState&gt;&gt;, commands: List&lt;Command&lt;*&gt;&gt;, attachments: List&lt;SecureHash&gt;, notary: Party?, timeWindow: TimeWindow?, references: List&lt;StateRef&gt;, networkParametersHash: SecureHash?): List&lt;ComponentGroup&gt;</ID>
<ID>ComplexMethod:TransitionExecutorImpl.kt$TransitionExecutorImpl$@Suppress("NestedBlockDepth", "ReturnCount") @Suspendable override fun executeTransition( fiber: FlowFiber, previousState: StateMachineState, event: Event, transition: TransitionResult, actionExecutor: ActionExecutor ): Pair&lt;FlowContinuation, StateMachineState&gt;</ID>
<ID>ComplexMethod:TypeModellingFingerPrinter.kt$FingerPrintingState$// For a type we haven't seen before, determine the correct path depending on the type of type it is. private fun fingerprintNewType(type: LocalTypeInformation)</ID> <ID>ComplexMethod:TypeModellingFingerPrinter.kt$FingerPrintingState$// For a type we haven't seen before, determine the correct path depending on the type of type it is. private fun fingerprintNewType(type: LocalTypeInformation)</ID>
<ID>ComplexMethod:UniversalContract.kt$UniversalContract$override fun verify(tx: LedgerTransaction)</ID> <ID>ComplexMethod:UniversalContract.kt$UniversalContract$override fun verify(tx: LedgerTransaction)</ID>
<ID>ComplexMethod:Util.kt$fun &lt;T&gt; debugCompare(perLeft: Perceivable&lt;T&gt;, perRight: Perceivable&lt;T&gt;)</ID> <ID>ComplexMethod:Util.kt$fun &lt;T&gt; debugCompare(perLeft: Perceivable&lt;T&gt;, perRight: Perceivable&lt;T&gt;)</ID>
@ -612,8 +608,6 @@
<ID>LongParameterList:AbstractCashSelection.kt$AbstractCashSelection$(connection: Connection, amount: Amount&lt;Currency&gt;, lockId: UUID, notary: Party?, onlyFromIssuerParties: Set&lt;AbstractParty&gt;, withIssuerRefs: Set&lt;OpaqueBytes&gt;, withResultSet: (ResultSet) -&gt; Boolean)</ID> <ID>LongParameterList:AbstractCashSelection.kt$AbstractCashSelection$(connection: Connection, amount: Amount&lt;Currency&gt;, lockId: UUID, notary: Party?, onlyFromIssuerParties: Set&lt;AbstractParty&gt;, withIssuerRefs: Set&lt;OpaqueBytes&gt;, withResultSet: (ResultSet) -&gt; Boolean)</ID>
<ID>LongParameterList:AbstractCashSelection.kt$AbstractCashSelection$(services: ServiceHub, amount: Amount&lt;Currency&gt;, lockId: UUID, notary: Party?, onlyFromIssuerParties: Set&lt;AbstractParty&gt;, withIssuerRefs: Set&lt;OpaqueBytes&gt;, stateAndRefs: MutableList&lt;StateAndRef&lt;Cash.State&gt;&gt;)</ID> <ID>LongParameterList:AbstractCashSelection.kt$AbstractCashSelection$(services: ServiceHub, amount: Amount&lt;Currency&gt;, lockId: UUID, notary: Party?, onlyFromIssuerParties: Set&lt;AbstractParty&gt;, withIssuerRefs: Set&lt;OpaqueBytes&gt;, stateAndRefs: MutableList&lt;StateAndRef&lt;Cash.State&gt;&gt;)</ID>
<ID>LongParameterList:AbstractCashSelection.kt$AbstractCashSelection$(services: ServiceHub, amount: Amount&lt;Currency&gt;, onlyFromIssuerParties: Set&lt;AbstractParty&gt; = emptySet(), notary: Party? = null, lockId: UUID, withIssuerRefs: Set&lt;OpaqueBytes&gt; = emptySet())</ID> <ID>LongParameterList:AbstractCashSelection.kt$AbstractCashSelection$(services: ServiceHub, amount: Amount&lt;Currency&gt;, onlyFromIssuerParties: Set&lt;AbstractParty&gt; = emptySet(), notary: Party? = null, lockId: UUID, withIssuerRefs: Set&lt;OpaqueBytes&gt; = emptySet())</ID>
<ID>LongParameterList:AbstractNode.kt$(databaseConfig: DatabaseConfig, wellKnownPartyFromX500Name: (CordaX500Name) -&gt; Party?, wellKnownPartyFromAnonymous: (AbstractParty) -&gt; Party?, schemaService: SchemaService, hikariProperties: Properties, cacheFactory: NamedCacheFactory, customClassLoader: ClassLoader?)</ID>
<ID>LongParameterList:AbstractNode.kt$(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemas: Set&lt;MappedSchema&gt;, metricRegistry: MetricRegistry? = null, cordappLoader: CordappLoader? = null, currentDir: Path? = null, ourName: CordaX500Name)</ID>
<ID>LongParameterList:ArtemisMessagingServer.kt$ArtemisMessagingServer$(name: String, send: Boolean = false, consume: Boolean = false, createDurableQueue: Boolean = false, deleteDurableQueue: Boolean = false, createNonDurableQueue: Boolean = false, deleteNonDurableQueue: Boolean = false, manage: Boolean = false, browse: Boolean = false)</ID> <ID>LongParameterList:ArtemisMessagingServer.kt$ArtemisMessagingServer$(name: String, send: Boolean = false, consume: Boolean = false, createDurableQueue: Boolean = false, deleteDurableQueue: Boolean = false, createNonDurableQueue: Boolean = false, deleteNonDurableQueue: Boolean = false, manage: Boolean = false, browse: Boolean = false)</ID>
<ID>LongParameterList:ArtemisRpcBroker.kt$ArtemisRpcBroker.Companion$(configuration: MutualSslConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, securityManager: RPCSecurityManager, maxMessageSize: Int, journalBufferTimeout: Int?, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean)</ID> <ID>LongParameterList:ArtemisRpcBroker.kt$ArtemisRpcBroker.Companion$(configuration: MutualSslConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, securityManager: RPCSecurityManager, maxMessageSize: Int, journalBufferTimeout: Int?, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean)</ID>
<ID>LongParameterList:ArtemisRpcBroker.kt$ArtemisRpcBroker.Companion$(configuration: MutualSslConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, sslOptions: BrokerRpcSslOptions, securityManager: RPCSecurityManager, maxMessageSize: Int, journalBufferTimeout: Int?, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean)</ID> <ID>LongParameterList:ArtemisRpcBroker.kt$ArtemisRpcBroker.Companion$(configuration: MutualSslConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, sslOptions: BrokerRpcSslOptions, securityManager: RPCSecurityManager, maxMessageSize: Int, journalBufferTimeout: Int?, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean)</ID>
@ -655,13 +649,11 @@
<ID>LongParameterList:IdenticonRenderer.kt$IdenticonRenderer$(g: GraphicsContext, x: Double, y: Double, patchIndex: Int, turn: Int, patchSize: Double, _invert: Boolean, color: PatchColor)</ID> <ID>LongParameterList:IdenticonRenderer.kt$IdenticonRenderer$(g: GraphicsContext, x: Double, y: Double, patchIndex: Int, turn: Int, patchSize: Double, _invert: Boolean, color: PatchColor)</ID>
<ID>LongParameterList:Injectors.kt$( metricRegistry: MetricRegistry, parallelism: Int, overallDuration: Duration, injectionRate: Rate, queueSizeMetricName: String = "QueueSize", workDurationMetricName: String = "WorkDuration", work: () -&gt; Unit )</ID> <ID>LongParameterList:Injectors.kt$( metricRegistry: MetricRegistry, parallelism: Int, overallDuration: Duration, injectionRate: Rate, queueSizeMetricName: String = "QueueSize", workDurationMetricName: String = "WorkDuration", work: () -&gt; Unit )</ID>
<ID>LongParameterList:InteractiveShell.kt$InteractiveShell$(nameFragment: String, inputData: String, output: RenderPrintWriter, rpcOps: CordaRPCOps, ansiProgressRenderer: ANSIProgressRenderer, inputObjectMapper: ObjectMapper = createYamlInputMapper(rpcOps))</ID> <ID>LongParameterList:InteractiveShell.kt$InteractiveShell$(nameFragment: String, inputData: String, output: RenderPrintWriter, rpcOps: CordaRPCOps, ansiProgressRenderer: ANSIProgressRenderer, inputObjectMapper: ObjectMapper = createYamlInputMapper(rpcOps))</ID>
<ID>LongParameterList:InternalTestUtils.kt$(hikariProperties: Properties, databaseConfig: DatabaseConfig, wellKnownPartyFromX500Name: (CordaX500Name) -&gt; Party?, wellKnownPartyFromAnonymous: (AbstractParty) -&gt; Party?, schemaService: SchemaService = NodeSchemaService(), internalSchemas: Set&lt;MappedSchema&gt; = NodeSchemaService().internalSchemas(), cacheFactory: NamedCacheFactory = TestingNamedCacheFactory(), ourName: CordaX500Name = TestIdentity(ALICE_NAME, 70).name)</ID>
<ID>LongParameterList:InternalTestUtils.kt$(inputs: List&lt;StateRef&gt;, attachments: List&lt;SecureHash&gt;, outputs: List&lt;TransactionState&lt;*&gt;&gt;, commands: List&lt;Command&lt;*&gt;&gt;, notary: Party?, timeWindow: TimeWindow?, privacySalt: PrivacySalt = PrivacySalt())</ID> <ID>LongParameterList:InternalTestUtils.kt$(inputs: List&lt;StateRef&gt;, attachments: List&lt;SecureHash&gt;, outputs: List&lt;TransactionState&lt;*&gt;&gt;, commands: List&lt;Command&lt;*&gt;&gt;, notary: Party?, timeWindow: TimeWindow?, privacySalt: PrivacySalt = PrivacySalt())</ID>
<ID>LongParameterList:JarSignatureTestUtils.kt$JarSignatureTestUtils$(alias: String = "Test", storePassword: String = "secret!", name: String = CODE_SIGNER.toString(), keyalg: String = "RSA", keyPassword: String = storePassword, storeName: String = "_teststore")</ID> <ID>LongParameterList:JarSignatureTestUtils.kt$JarSignatureTestUtils$(alias: String = "Test", storePassword: String = "secret!", name: String = CODE_SIGNER.toString(), keyalg: String = "RSA", keyPassword: String = storePassword, storeName: String = "_teststore")</ID>
<ID>LongParameterList:MockServices.kt$MockServices.Companion$( cordappLoader: CordappLoader, identityService: IdentityService, networkParameters: NetworkParameters, initialIdentity: TestIdentity, moreKeys: Set&lt;KeyPair&gt;, keyManagementService: KeyManagementService, schemaService: SchemaService, persistence: CordaPersistence )</ID> <ID>LongParameterList:MockServices.kt$MockServices.Companion$( cordappLoader: CordappLoader, identityService: IdentityService, networkParameters: NetworkParameters, initialIdentity: TestIdentity, moreKeys: Set&lt;KeyPair&gt;, keyManagementService: KeyManagementService, schemaService: SchemaService, persistence: CordaPersistence )</ID>
<ID>LongParameterList:MockServices.kt$MockServices.Companion$( cordappPackages: List&lt;String&gt;, initialIdentity: TestIdentity, networkParameters: NetworkParameters = testNetworkParameters(modifiedTime = Instant.MIN), moreKeys: Set&lt;KeyPair&gt;, moreIdentities: Set&lt;PartyAndCertificate&gt;, cacheFactory: TestingNamedCacheFactory = TestingNamedCacheFactory() )</ID> <ID>LongParameterList:MockServices.kt$MockServices.Companion$( cordappPackages: List&lt;String&gt;, initialIdentity: TestIdentity, networkParameters: NetworkParameters = testNetworkParameters(modifiedTime = Instant.MIN), moreKeys: Set&lt;KeyPair&gt;, moreIdentities: Set&lt;PartyAndCertificate&gt;, cacheFactory: TestingNamedCacheFactory = TestingNamedCacheFactory() )</ID>
<ID>LongParameterList:NetworkBootstrapperTest.kt$NetworkBootstrapperTest$(copyCordapps: CopyCordapps = CopyCordapps.FirstRunOnly, packageOwnership: Map&lt;String, PublicKey&gt;? = emptyMap(), minimumPlatformVerison: Int? = PLATFORM_VERSION, maxMessageSize: Int? = DEFAULT_MAX_MESSAGE_SIZE, maxTransactionSize: Int? = DEFAULT_MAX_TRANSACTION_SIZE, eventHorizon: Duration? = 30.days)</ID> <ID>LongParameterList:NetworkBootstrapperTest.kt$NetworkBootstrapperTest$(copyCordapps: CopyCordapps = CopyCordapps.FirstRunOnly, packageOwnership: Map&lt;String, PublicKey&gt;? = emptyMap(), minimumPlatformVerison: Int? = PLATFORM_VERSION, maxMessageSize: Int? = DEFAULT_MAX_MESSAGE_SIZE, maxTransactionSize: Int? = DEFAULT_MAX_TRANSACTION_SIZE, eventHorizon: Duration? = 30.days)</ID>
<ID>LongParameterList:NetworkMapUpdater.kt$NetworkMapUpdater$(trustRoot: X509Certificate, currentParametersHash: SecureHash, ourNodeInfo: SignedNodeInfo, networkParameters: NetworkParameters, keyManagementService: KeyManagementService, networkParameterAcceptanceSettings: NetworkParameterAcceptanceSettings)</ID>
<ID>LongParameterList:NetworkParameters.kt$NetworkParameters$(minimumPlatformVersion: Int = this.minimumPlatformVersion, notaries: List&lt;NotaryInfo&gt; = this.notaries, maxMessageSize: Int = this.maxMessageSize, maxTransactionSize: Int = this.maxTransactionSize, modifiedTime: Instant = this.modifiedTime, epoch: Int = this.epoch, whitelistedContractImplementations: Map&lt;String, List&lt;AttachmentId&gt;&gt; = this.whitelistedContractImplementations )</ID> <ID>LongParameterList:NetworkParameters.kt$NetworkParameters$(minimumPlatformVersion: Int = this.minimumPlatformVersion, notaries: List&lt;NotaryInfo&gt; = this.notaries, maxMessageSize: Int = this.maxMessageSize, maxTransactionSize: Int = this.maxTransactionSize, modifiedTime: Instant = this.modifiedTime, epoch: Int = this.epoch, whitelistedContractImplementations: Map&lt;String, List&lt;AttachmentId&gt;&gt; = this.whitelistedContractImplementations )</ID>
<ID>LongParameterList:NetworkParameters.kt$NetworkParameters$(minimumPlatformVersion: Int = this.minimumPlatformVersion, notaries: List&lt;NotaryInfo&gt; = this.notaries, maxMessageSize: Int = this.maxMessageSize, maxTransactionSize: Int = this.maxTransactionSize, modifiedTime: Instant = this.modifiedTime, epoch: Int = this.epoch, whitelistedContractImplementations: Map&lt;String, List&lt;AttachmentId&gt;&gt; = this.whitelistedContractImplementations, eventHorizon: Duration = this.eventHorizon )</ID> <ID>LongParameterList:NetworkParameters.kt$NetworkParameters$(minimumPlatformVersion: Int = this.minimumPlatformVersion, notaries: List&lt;NotaryInfo&gt; = this.notaries, maxMessageSize: Int = this.maxMessageSize, maxTransactionSize: Int = this.maxTransactionSize, modifiedTime: Instant = this.modifiedTime, epoch: Int = this.epoch, whitelistedContractImplementations: Map&lt;String, List&lt;AttachmentId&gt;&gt; = this.whitelistedContractImplementations, eventHorizon: Duration = this.eventHorizon )</ID>
<ID>LongParameterList:NodeParameters.kt$NodeParameters$( providedName: CordaX500Name?, rpcUsers: List&lt;User&gt;, verifierType: VerifierType, customOverrides: Map&lt;String, Any?&gt;, startInSameProcess: Boolean?, maximumHeapSize: String )</ID> <ID>LongParameterList:NodeParameters.kt$NodeParameters$( providedName: CordaX500Name?, rpcUsers: List&lt;User&gt;, verifierType: VerifierType, customOverrides: Map&lt;String, Any?&gt;, startInSameProcess: Boolean?, maximumHeapSize: String )</ID>
@ -770,6 +762,7 @@
<ID>MagicNumber:CordaRPCClient.kt$CordaRPCClient$128</ID> <ID>MagicNumber:CordaRPCClient.kt$CordaRPCClient$128</ID>
<ID>MagicNumber:CordaRPCClient.kt$CordaRPCClientConfiguration$3</ID> <ID>MagicNumber:CordaRPCClient.kt$CordaRPCClientConfiguration$3</ID>
<ID>MagicNumber:CordaRPCClient.kt$CordaRPCClientConfiguration$5</ID> <ID>MagicNumber:CordaRPCClient.kt$CordaRPCClientConfiguration$5</ID>
<ID>MagicNumber:CordaSSHAuthInfo.kt$CordaSSHAuthInfo$10</ID>
<ID>MagicNumber:CordaSecurityProvider.kt$CordaSecurityProvider$0.1</ID> <ID>MagicNumber:CordaSecurityProvider.kt$CordaSecurityProvider$0.1</ID>
<ID>MagicNumber:Crypto.kt$Crypto$2048</ID> <ID>MagicNumber:Crypto.kt$Crypto$2048</ID>
<ID>MagicNumber:Crypto.kt$Crypto$256</ID> <ID>MagicNumber:Crypto.kt$Crypto$256</ID>
@ -1080,6 +1073,7 @@
<ID>MagicNumber:RPCDriver.kt$RandomRpcUser.Companion$100</ID> <ID>MagicNumber:RPCDriver.kt$RandomRpcUser.Companion$100</ID>
<ID>MagicNumber:RPCDriver.kt$RandomRpcUser.Companion$3</ID> <ID>MagicNumber:RPCDriver.kt$RandomRpcUser.Companion$3</ID>
<ID>MagicNumber:RPCDriver.kt$RandomRpcUser.Companion$4</ID> <ID>MagicNumber:RPCDriver.kt$RandomRpcUser.Companion$4</ID>
<ID>MagicNumber:RPCPermissionResolver.kt$RPCPermissionResolver$20</ID>
<ID>MagicNumber:RPCServer.kt$RPCServer$5</ID> <ID>MagicNumber:RPCServer.kt$RPCServer$5</ID>
<ID>MagicNumber:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps$4</ID> <ID>MagicNumber:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps$4</ID>
<ID>MagicNumber:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps.ErrorInterceptingHandler$1000</ID> <ID>MagicNumber:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps.ErrorInterceptingHandler$1000</ID>
@ -1243,8 +1237,7 @@
<ID>SpreadOperator:AssertingTestDatabaseContext.kt$AssertingTestDatabaseContext$(*expectedScripts)</ID> <ID>SpreadOperator:AssertingTestDatabaseContext.kt$AssertingTestDatabaseContext$(*expectedScripts)</ID>
<ID>SpreadOperator:AttachmentDemo.kt$(*args)</ID> <ID>SpreadOperator:AttachmentDemo.kt$(*args)</ID>
<ID>SpreadOperator:AttachmentsClassLoaderStaticContractTests.kt$AttachmentsClassLoaderStaticContractTests$(*packages.toTypedArray())</ID> <ID>SpreadOperator:AttachmentsClassLoaderStaticContractTests.kt$AttachmentsClassLoaderStaticContractTests$(*packages.toTypedArray())</ID>
<ID>SpreadOperator:AuthenticatedRpcOpsProxy.kt$(methodName, *(args.map(Class&lt;*&gt;::getName).toTypedArray()))</ID> <ID>SpreadOperator:AuthenticatedRpcOpsProxy.kt$AuthenticatedRpcOpsProxy.PermissionsEnforcingInvocationHandler$(methodFullName(method), *(args.map(Class&lt;*&gt;::getName).toTypedArray()))</ID>
<ID>SpreadOperator:AuthenticatedRpcOpsProxy.kt$AuthenticatedRpcOpsProxy$(logicType, *args)</ID>
<ID>SpreadOperator:AzureInstantiator.kt$AzureInstantiator$(*portsToOpen.toIntArray())</ID> <ID>SpreadOperator:AzureInstantiator.kt$AzureInstantiator$(*portsToOpen.toIntArray())</ID>
<ID>SpreadOperator:ByteArrays.kt$OpaqueBytes.Companion$(*b)</ID> <ID>SpreadOperator:ByteArrays.kt$OpaqueBytes.Companion$(*b)</ID>
<ID>SpreadOperator:CertificateRevocationListNodeTests.kt$CertificateRevocationListNodeTests$(newNodeCert, INTERMEDIATE_CA.certificate, *nodeKeyStore.query { getCertificateChain(X509Utilities.CORDA_CLIENT_CA) }.drop(2).toTypedArray())</ID> <ID>SpreadOperator:CertificateRevocationListNodeTests.kt$CertificateRevocationListNodeTests$(newNodeCert, INTERMEDIATE_CA.certificate, *nodeKeyStore.query { getCertificateChain(X509Utilities.CORDA_CLIENT_CA) }.drop(2).toTypedArray())</ID>
@ -1350,8 +1343,7 @@
<ID>SpreadOperator:PropertyValidationTest.kt$PropertyValidationTest$(*key.split(".").toTypedArray(), nestedKey)</ID> <ID>SpreadOperator:PropertyValidationTest.kt$PropertyValidationTest$(*key.split(".").toTypedArray(), nestedKey)</ID>
<ID>SpreadOperator:RPCClient.kt$RPCClient$(*haPoolTransportConfigurations.toTypedArray())</ID> <ID>SpreadOperator:RPCClient.kt$RPCClient$(*haPoolTransportConfigurations.toTypedArray())</ID>
<ID>SpreadOperator:RPCDriver.kt$RandomRpcUser.Companion$(handle.proxy, *arguments.toTypedArray())</ID> <ID>SpreadOperator:RPCDriver.kt$RandomRpcUser.Companion$(handle.proxy, *arguments.toTypedArray())</ID>
<ID>SpreadOperator:RPCOpsWithContext.kt$(cordaRPCOps, *(args ?: arrayOf()))</ID> <ID>SpreadOperator:RPCSecurityManagerTest.kt$RPCSecurityManagerTest$(methodName, *args)</ID>
<ID>SpreadOperator:RPCSecurityManagerTest.kt$RPCSecurityManagerTest$(request.first(), *args)</ID>
<ID>SpreadOperator:RPCServer.kt$RPCServer$(invocationTarget.instance, *arguments.toTypedArray())</ID> <ID>SpreadOperator:RPCServer.kt$RPCServer$(invocationTarget.instance, *arguments.toTypedArray())</ID>
<ID>SpreadOperator:ReactiveArtemisConsumer.kt$ReactiveArtemisConsumer.Companion$(queueName, *queueNames)</ID> <ID>SpreadOperator:ReactiveArtemisConsumer.kt$ReactiveArtemisConsumer.Companion$(queueName, *queueNames)</ID>
<ID>SpreadOperator:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps.ErrorInterceptingHandler$(reconnectingRPCConnection.proxy, *(args ?: emptyArray()))</ID> <ID>SpreadOperator:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps.ErrorInterceptingHandler$(reconnectingRPCConnection.proxy, *(args ?: emptyArray()))</ID>
@ -1379,7 +1371,6 @@
<ID>SpreadOperator:X509Utilities.kt$X509Utilities$(*certificates)</ID> <ID>SpreadOperator:X509Utilities.kt$X509Utilities$(*certificates)</ID>
<ID>ThrowsCount:AMQPTypeIdentifierParser.kt$AMQPTypeIdentifierParser$// Make sure our inputs aren't designed to blow things up. private fun validate(typeString: String)</ID> <ID>ThrowsCount:AMQPTypeIdentifierParser.kt$AMQPTypeIdentifierParser$// Make sure our inputs aren't designed to blow things up. private fun validate(typeString: String)</ID>
<ID>ThrowsCount:AbstractNode.kt$AbstractNode$private fun installCordaServices()</ID> <ID>ThrowsCount:AbstractNode.kt$AbstractNode$private fun installCordaServices()</ID>
<ID>ThrowsCount:AbstractNode.kt$fun CordaPersistence.startHikariPool(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemas: Set&lt;MappedSchema&gt;, metricRegistry: MetricRegistry? = null, cordappLoader: CordappLoader? = null, currentDir: Path? = null, ourName: CordaX500Name)</ID>
<ID>ThrowsCount:ArtemisMessagingServer.kt$ArtemisMessagingServer$// TODO: Maybe wrap [IOException] on a key store load error so that it's clearly splitting key store loading from // Artemis IO errors @Throws(IOException::class, AddressBindingException::class, KeyStoreException::class) private fun configureAndStartServer()</ID> <ID>ThrowsCount:ArtemisMessagingServer.kt$ArtemisMessagingServer$// TODO: Maybe wrap [IOException] on a key store load error so that it's clearly splitting key store loading from // Artemis IO errors @Throws(IOException::class, AddressBindingException::class, KeyStoreException::class) private fun configureAndStartServer()</ID>
<ID>ThrowsCount:BrokerJaasLoginModule.kt$BaseBrokerJaasLoginModule$@Suppress("DEPRECATION") // should use java.security.cert.X509Certificate protected fun getUsernamePasswordAndCerts(): Triple&lt;String, String, Array&lt;javax.security.cert.X509Certificate&gt;?&gt;</ID> <ID>ThrowsCount:BrokerJaasLoginModule.kt$BaseBrokerJaasLoginModule$@Suppress("DEPRECATION") // should use java.security.cert.X509Certificate protected fun getUsernamePasswordAndCerts(): Triple&lt;String, String, Array&lt;javax.security.cert.X509Certificate&gt;?&gt;</ID>
<ID>ThrowsCount:CheckpointVerifier.kt$CheckpointVerifier$ fun verifyCheckpointsCompatible( checkpointStorage: CheckpointStorage, currentCordapps: List&lt;Cordapp&gt;, platformVersion: Int, serviceHub: ServiceHub, tokenizableServices: List&lt;Any&gt; )</ID> <ID>ThrowsCount:CheckpointVerifier.kt$CheckpointVerifier$ fun verifyCheckpointsCompatible( checkpointStorage: CheckpointStorage, currentCordapps: List&lt;Cordapp&gt;, platformVersion: Int, serviceHub: ServiceHub, tokenizableServices: List&lt;Any&gt; )</ID>
@ -1403,7 +1394,6 @@
<ID>ThrowsCount:PropertyDescriptor.kt$PropertyDescriptor$ fun validate()</ID> <ID>ThrowsCount:PropertyDescriptor.kt$PropertyDescriptor$ fun validate()</ID>
<ID>ThrowsCount:RPCApi.kt$RPCApi.ServerToClient.Companion$fun fromClientMessage(context: SerializationContext, message: ClientMessage): ServerToClient</ID> <ID>ThrowsCount:RPCApi.kt$RPCApi.ServerToClient.Companion$fun fromClientMessage(context: SerializationContext, message: ClientMessage): ServerToClient</ID>
<ID>ThrowsCount:RPCServer.kt$RPCServer$private fun invokeRpc(context: RpcAuthContext, inMethodName: String, arguments: List&lt;Any?&gt;): Try&lt;Any&gt;</ID> <ID>ThrowsCount:RPCServer.kt$RPCServer$private fun invokeRpc(context: RpcAuthContext, inMethodName: String, arguments: List&lt;Any?&gt;): Try&lt;Any&gt;</ID>
<ID>ThrowsCount:SchemaMigration.kt$SchemaMigration$private fun doRunMigration( run: Boolean, check: Boolean, existingCheckpoints: Boolean? = null )</ID>
<ID>ThrowsCount:ServicesForResolutionImpl.kt$ServicesForResolutionImpl$// We may need to recursively chase transactions if there are notary changes. fun inner(stateRef: StateRef, forContractClassName: String?): Attachment</ID> <ID>ThrowsCount:ServicesForResolutionImpl.kt$ServicesForResolutionImpl$// We may need to recursively chase transactions if there are notary changes. fun inner(stateRef: StateRef, forContractClassName: String?): Attachment</ID>
<ID>ThrowsCount:SignedNodeInfo.kt$SignedNodeInfo$// TODO Add root cert param (or TrustAnchor) to make sure all the identities belong to the same root fun verified(): NodeInfo</ID> <ID>ThrowsCount:SignedNodeInfo.kt$SignedNodeInfo$// TODO Add root cert param (or TrustAnchor) to make sure all the identities belong to the same root fun verified(): NodeInfo</ID>
<ID>ThrowsCount:SignedTransaction.kt$SignedTransaction$@DeleteForDJVM private fun resolveAndCheckNetworkParameters(services: ServiceHub)</ID> <ID>ThrowsCount:SignedTransaction.kt$SignedTransaction$@DeleteForDJVM private fun resolveAndCheckNetworkParameters(services: ServiceHub)</ID>
@ -1466,7 +1456,6 @@
<ID>TooGenericExceptionCaught:Eventually.kt$e: Exception</ID> <ID>TooGenericExceptionCaught:Eventually.kt$e: Exception</ID>
<ID>TooGenericExceptionCaught:Expect.kt$exception: Exception</ID> <ID>TooGenericExceptionCaught:Expect.kt$exception: Exception</ID>
<ID>TooGenericExceptionCaught:Explorer.kt$Explorer$e: Exception</ID> <ID>TooGenericExceptionCaught:Explorer.kt$Explorer$e: Exception</ID>
<ID>TooGenericExceptionCaught:FiberDeserializationCheckingInterceptor.kt$FiberDeserializationChecker$exception: Exception</ID>
<ID>TooGenericExceptionCaught:FinanceJSONSupport.kt$CalendarDeserializer$e: Exception</ID> <ID>TooGenericExceptionCaught:FinanceJSONSupport.kt$CalendarDeserializer$e: Exception</ID>
<ID>TooGenericExceptionCaught:FlowHandle.kt$FlowProgressHandleImpl$e: Exception</ID> <ID>TooGenericExceptionCaught:FlowHandle.kt$FlowProgressHandleImpl$e: Exception</ID>
<ID>TooGenericExceptionCaught:FlowMessaging.kt$FlowMessagingImpl$exception: Exception</ID> <ID>TooGenericExceptionCaught:FlowMessaging.kt$FlowMessagingImpl$exception: Exception</ID>
@ -1541,6 +1530,7 @@
<ID>TooGenericExceptionCaught:RPCClient.kt$RPCClient$exception: Throwable</ID> <ID>TooGenericExceptionCaught:RPCClient.kt$RPCClient$exception: Throwable</ID>
<ID>TooGenericExceptionCaught:RPCClientProxyHandler.kt$RPCClientProxyHandler$e: Exception</ID> <ID>TooGenericExceptionCaught:RPCClientProxyHandler.kt$RPCClientProxyHandler$e: Exception</ID>
<ID>TooGenericExceptionCaught:RPCClientProxyHandler.kt$RPCClientProxyHandler$e: RuntimeException</ID> <ID>TooGenericExceptionCaught:RPCClientProxyHandler.kt$RPCClientProxyHandler$e: RuntimeException</ID>
<ID>TooGenericExceptionCaught:RPCPermissionResolver.kt$RPCPermissionResolver.InterfaceMethodMapCacheLoader$ex: Exception</ID>
<ID>TooGenericExceptionCaught:RPCServer.kt$RPCServer$e: Exception</ID> <ID>TooGenericExceptionCaught:RPCServer.kt$RPCServer$e: Exception</ID>
<ID>TooGenericExceptionCaught:RPCServer.kt$RPCServer$exception: Throwable</ID> <ID>TooGenericExceptionCaught:RPCServer.kt$RPCServer$exception: Throwable</ID>
<ID>TooGenericExceptionCaught:RPCServer.kt$RPCServer$throwable: Throwable</ID> <ID>TooGenericExceptionCaught:RPCServer.kt$RPCServer$throwable: Throwable</ID>
@ -1630,7 +1620,7 @@
<ID>TooManyFunctions:ContractUpgradeTransactions.kt$ContractUpgradeLedgerTransaction : FullTransactionTransactionWithSignatures</ID> <ID>TooManyFunctions:ContractUpgradeTransactions.kt$ContractUpgradeLedgerTransaction : FullTransactionTransactionWithSignatures</ID>
<ID>TooManyFunctions:CordaRPCOps.kt$CordaRPCOps : RPCOps</ID> <ID>TooManyFunctions:CordaRPCOps.kt$CordaRPCOps : RPCOps</ID>
<ID>TooManyFunctions:CordaRPCOps.kt$net.corda.core.messaging.CordaRPCOps.kt</ID> <ID>TooManyFunctions:CordaRPCOps.kt$net.corda.core.messaging.CordaRPCOps.kt</ID>
<ID>TooManyFunctions:CordaRPCOpsImpl.kt$CordaRPCOpsImpl : InternalCordaRPCOpsAutoCloseable</ID> <ID>TooManyFunctions:CordaRPCOpsImpl.kt$CordaRPCOpsImpl : CordaRPCOpsAutoCloseable</ID>
<ID>TooManyFunctions:Crypto.kt$Crypto</ID> <ID>TooManyFunctions:Crypto.kt$Crypto</ID>
<ID>TooManyFunctions:CryptoUtils.kt$net.corda.core.crypto.CryptoUtils.kt</ID> <ID>TooManyFunctions:CryptoUtils.kt$net.corda.core.crypto.CryptoUtils.kt</ID>
<ID>TooManyFunctions:Currencies.kt$net.corda.finance.Currencies.kt</ID> <ID>TooManyFunctions:Currencies.kt$net.corda.finance.Currencies.kt</ID>
@ -1663,8 +1653,6 @@
<ID>TooManyFunctions:P2PMessagingClient.kt$P2PMessagingClient : SingletonSerializeAsTokenMessagingServiceAddressToArtemisQueueResolverServiceStateSupport</ID> <ID>TooManyFunctions:P2PMessagingClient.kt$P2PMessagingClient : SingletonSerializeAsTokenMessagingServiceAddressToArtemisQueueResolverServiceStateSupport</ID>
<ID>TooManyFunctions:PathUtils.kt$net.corda.core.internal.PathUtils.kt</ID> <ID>TooManyFunctions:PathUtils.kt$net.corda.core.internal.PathUtils.kt</ID>
<ID>TooManyFunctions:Perceivable.kt$net.corda.finance.contracts.universal.Perceivable.kt</ID> <ID>TooManyFunctions:Perceivable.kt$net.corda.finance.contracts.universal.Perceivable.kt</ID>
<ID>TooManyFunctions:PersistentIdentityService.kt$PersistentIdentityService : SingletonSerializeAsTokenIdentityServiceInternal</ID>
<ID>TooManyFunctions:PersistentNetworkMapCache.kt$PersistentNetworkMapCache : NetworkMapCacheInternalSingletonSerializeAsToken</ID>
<ID>TooManyFunctions:PortfolioApi.kt$PortfolioApi</ID> <ID>TooManyFunctions:PortfolioApi.kt$PortfolioApi</ID>
<ID>TooManyFunctions:PropertyDescriptor.kt$net.corda.serialization.internal.amqp.PropertyDescriptor.kt</ID> <ID>TooManyFunctions:PropertyDescriptor.kt$net.corda.serialization.internal.amqp.PropertyDescriptor.kt</ID>
<ID>TooManyFunctions:QueryCriteria.kt$QueryCriteria$VaultQueryCriteria : CommonQueryCriteria</ID> <ID>TooManyFunctions:QueryCriteria.kt$QueryCriteria$VaultQueryCriteria : CommonQueryCriteria</ID>
@ -1700,8 +1688,6 @@
<ID>UnusedImports:Amount.kt$import net.corda.core.crypto.CompositeKey</ID> <ID>UnusedImports:Amount.kt$import net.corda.core.crypto.CompositeKey</ID>
<ID>UnusedImports:Amount.kt$import net.corda.core.identity.Party</ID> <ID>UnusedImports:Amount.kt$import net.corda.core.identity.Party</ID>
<ID>UnusedImports:DummyLinearStateSchemaV1.kt$import net.corda.core.contracts.ContractState</ID> <ID>UnusedImports:DummyLinearStateSchemaV1.kt$import net.corda.core.contracts.ContractState</ID>
<ID>UnusedImports:InternalTestUtils.kt$import java.nio.file.Files</ID>
<ID>UnusedImports:InternalTestUtils.kt$import net.corda.nodeapi.internal.loadDevCaTrustStore</ID>
<ID>UnusedImports:NetworkMap.kt$import net.corda.core.node.NodeInfo</ID> <ID>UnusedImports:NetworkMap.kt$import net.corda.core.node.NodeInfo</ID>
<ID>UnusedImports:NodeParameters.kt$import net.corda.core.identity.Party</ID> <ID>UnusedImports:NodeParameters.kt$import net.corda.core.identity.Party</ID>
<ID>UnusedImports:SerializerFactory.kt$import java.io.NotSerializableException</ID> <ID>UnusedImports:SerializerFactory.kt$import java.io.NotSerializableException</ID>
@ -1960,7 +1946,6 @@
<ID>WildcardImport:CordaExceptionTest.kt$import org.junit.Assert.*</ID> <ID>WildcardImport:CordaExceptionTest.kt$import org.junit.Assert.*</ID>
<ID>WildcardImport:CordaFutureImplTest.kt$import com.nhaarman.mockito_kotlin.*</ID> <ID>WildcardImport:CordaFutureImplTest.kt$import com.nhaarman.mockito_kotlin.*</ID>
<ID>WildcardImport:CordaInternal.kt$import kotlin.annotation.AnnotationTarget.*</ID> <ID>WildcardImport:CordaInternal.kt$import kotlin.annotation.AnnotationTarget.*</ID>
<ID>WildcardImport:CordaMigration.kt$import net.corda.node.services.persistence.*</ID>
<ID>WildcardImport:CordaModule.kt$import com.fasterxml.jackson.annotation.*</ID> <ID>WildcardImport:CordaModule.kt$import com.fasterxml.jackson.annotation.*</ID>
<ID>WildcardImport:CordaModule.kt$import com.fasterxml.jackson.databind.*</ID> <ID>WildcardImport:CordaModule.kt$import com.fasterxml.jackson.databind.*</ID>
<ID>WildcardImport:CordaModule.kt$import net.corda.core.contracts.*</ID> <ID>WildcardImport:CordaModule.kt$import net.corda.core.contracts.*</ID>
@ -1990,7 +1975,6 @@
<ID>WildcardImport:DBTransactionStorage.kt$import javax.persistence.*</ID> <ID>WildcardImport:DBTransactionStorage.kt$import javax.persistence.*</ID>
<ID>WildcardImport:DBTransactionStorage.kt$import net.corda.core.serialization.*</ID> <ID>WildcardImport:DBTransactionStorage.kt$import net.corda.core.serialization.*</ID>
<ID>WildcardImport:DBTransactionStorage.kt$import net.corda.nodeapi.internal.persistence.*</ID> <ID>WildcardImport:DBTransactionStorage.kt$import net.corda.nodeapi.internal.persistence.*</ID>
<ID>WildcardImport:DBTransactionStorageTests.kt$import net.corda.testing.core.*</ID>
<ID>WildcardImport:DeleteForDJVM.kt$import kotlin.annotation.AnnotationTarget.*</ID> <ID>WildcardImport:DeleteForDJVM.kt$import kotlin.annotation.AnnotationTarget.*</ID>
<ID>WildcardImport:DemoBench.kt$import tornadofx.*</ID> <ID>WildcardImport:DemoBench.kt$import tornadofx.*</ID>
<ID>WildcardImport:DemoBenchNodeInfoFilesCopier.kt$import tornadofx.*</ID> <ID>WildcardImport:DemoBenchNodeInfoFilesCopier.kt$import tornadofx.*</ID>
@ -2008,7 +1992,6 @@
<ID>WildcardImport:DigitalSignatureWithCert.kt$import java.security.cert.*</ID> <ID>WildcardImport:DigitalSignatureWithCert.kt$import java.security.cert.*</ID>
<ID>WildcardImport:DistributedServiceTests.kt$import net.corda.testing.core.*</ID> <ID>WildcardImport:DistributedServiceTests.kt$import net.corda.testing.core.*</ID>
<ID>WildcardImport:DockerInstantiator.kt$import com.github.dockerjava.api.model.*</ID> <ID>WildcardImport:DockerInstantiator.kt$import com.github.dockerjava.api.model.*</ID>
<ID>WildcardImport:DriverDSLImpl.kt$import net.corda.testing.driver.*</ID>
<ID>WildcardImport:DummyContract.kt$import net.corda.core.contracts.*</ID> <ID>WildcardImport:DummyContract.kt$import net.corda.core.contracts.*</ID>
<ID>WildcardImport:DummyContractV2.kt$import net.corda.core.contracts.*</ID> <ID>WildcardImport:DummyContractV2.kt$import net.corda.core.contracts.*</ID>
<ID>WildcardImport:DummyContractV3.kt$import net.corda.core.contracts.*</ID> <ID>WildcardImport:DummyContractV3.kt$import net.corda.core.contracts.*</ID>
@ -2036,12 +2019,10 @@
<ID>WildcardImport:FlowCheckpointCordapp.kt$import net.corda.core.flows.*</ID> <ID>WildcardImport:FlowCheckpointCordapp.kt$import net.corda.core.flows.*</ID>
<ID>WildcardImport:FlowCheckpointVersionNodeStartupCheckTest.kt$import net.corda.core.flows.*</ID> <ID>WildcardImport:FlowCheckpointVersionNodeStartupCheckTest.kt$import net.corda.core.flows.*</ID>
<ID>WildcardImport:FlowCheckpointVersionNodeStartupCheckTest.kt$import net.corda.core.internal.*</ID> <ID>WildcardImport:FlowCheckpointVersionNodeStartupCheckTest.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:FlowFrameworkPersistenceTests.kt$import net.corda.testing.node.internal.*</ID>
<ID>WildcardImport:FlowFrameworkTripartyTests.kt$import net.corda.testing.node.internal.*</ID> <ID>WildcardImport:FlowFrameworkTripartyTests.kt$import net.corda.testing.node.internal.*</ID>
<ID>WildcardImport:FlowLogicRefFactoryImpl.kt$import net.corda.core.flows.*</ID> <ID>WildcardImport:FlowLogicRefFactoryImpl.kt$import net.corda.core.flows.*</ID>
<ID>WildcardImport:FlowMatchers.kt$import net.corda.coretesting.internal.matchers.*</ID> <ID>WildcardImport:FlowMatchers.kt$import net.corda.coretesting.internal.matchers.*</ID>
<ID>WildcardImport:FlowOverrideTests.kt$import net.corda.core.flows.*</ID> <ID>WildcardImport:FlowOverrideTests.kt$import net.corda.core.flows.*</ID>
<ID>WildcardImport:FlowRetryTest.kt$import net.corda.core.flows.*</ID>
<ID>WildcardImport:FlowStackSnapshotTest.kt$import net.corda.core.flows.*</ID> <ID>WildcardImport:FlowStackSnapshotTest.kt$import net.corda.core.flows.*</ID>
<ID>WildcardImport:FlowStateMachine.kt$import net.corda.core.flows.*</ID> <ID>WildcardImport:FlowStateMachine.kt$import net.corda.core.flows.*</ID>
<ID>WildcardImport:FlowsDrainingModeContentionTest.kt$import net.corda.core.flows.*</ID> <ID>WildcardImport:FlowsDrainingModeContentionTest.kt$import net.corda.core.flows.*</ID>
@ -2087,7 +2068,6 @@
<ID>WildcardImport:InternalMockNetwork.kt$import net.corda.core.internal.*</ID> <ID>WildcardImport:InternalMockNetwork.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:InternalMockNetwork.kt$import net.corda.node.services.config.*</ID> <ID>WildcardImport:InternalMockNetwork.kt$import net.corda.node.services.config.*</ID>
<ID>WildcardImport:InternalMockNetwork.kt$import net.corda.testing.node.*</ID> <ID>WildcardImport:InternalMockNetwork.kt$import net.corda.testing.node.*</ID>
<ID>WildcardImport:InternalTestUtils.kt$import net.corda.core.contracts.*</ID>
<ID>WildcardImport:IssuerModel.kt$import tornadofx.*</ID> <ID>WildcardImport:IssuerModel.kt$import tornadofx.*</ID>
<ID>WildcardImport:JVMConfig.kt$import tornadofx.*</ID> <ID>WildcardImport:JVMConfig.kt$import tornadofx.*</ID>
<ID>WildcardImport:JacksonSupport.kt$import com.fasterxml.jackson.core.*</ID> <ID>WildcardImport:JacksonSupport.kt$import com.fasterxml.jackson.core.*</ID>
@ -2187,7 +2167,6 @@
<ID>WildcardImport:NodeAttachmentServiceTest.kt$import org.assertj.core.api.Assertions.*</ID> <ID>WildcardImport:NodeAttachmentServiceTest.kt$import org.assertj.core.api.Assertions.*</ID>
<ID>WildcardImport:NodeAttachmentTrustCalculator.kt$import net.corda.core.internal.*</ID> <ID>WildcardImport:NodeAttachmentTrustCalculator.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:NodeBasedTest.kt$import net.corda.node.services.config.*</ID> <ID>WildcardImport:NodeBasedTest.kt$import net.corda.node.services.config.*</ID>
<ID>WildcardImport:NodeController.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:NodeController.kt$import tornadofx.*</ID> <ID>WildcardImport:NodeController.kt$import tornadofx.*</ID>
<ID>WildcardImport:NodeControllerTest.kt$import kotlin.test.*</ID> <ID>WildcardImport:NodeControllerTest.kt$import kotlin.test.*</ID>
<ID>WildcardImport:NodeData.kt$import tornadofx.*</ID> <ID>WildcardImport:NodeData.kt$import tornadofx.*</ID>
@ -2197,7 +2176,6 @@
<ID>WildcardImport:NodeInterestRates.kt$import net.corda.core.flows.*</ID> <ID>WildcardImport:NodeInterestRates.kt$import net.corda.core.flows.*</ID>
<ID>WildcardImport:NodeInterestRatesTest.kt$import net.corda.testing.core.*</ID> <ID>WildcardImport:NodeInterestRatesTest.kt$import net.corda.testing.core.*</ID>
<ID>WildcardImport:NodeInterestRatesTest.kt$import org.junit.Assert.*</ID> <ID>WildcardImport:NodeInterestRatesTest.kt$import org.junit.Assert.*</ID>
<ID>WildcardImport:NodeProcess.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:NodeRegistrationTest.kt$import javax.ws.rs.*</ID> <ID>WildcardImport:NodeRegistrationTest.kt$import javax.ws.rs.*</ID>
<ID>WildcardImport:NodeSchedulerService.kt$import net.corda.core.internal.*</ID> <ID>WildcardImport:NodeSchedulerService.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:NodeSchedulerServiceTest.kt$import com.nhaarman.mockito_kotlin.*</ID> <ID>WildcardImport:NodeSchedulerServiceTest.kt$import com.nhaarman.mockito_kotlin.*</ID>

View File

@ -129,7 +129,7 @@ class ArtemisRpcTests {
private fun <OPS : RPCOps> InternalRPCMessagingClient.start(ops: OPS, securityManager: RPCSecurityManager, brokerControl: ActiveMQServerControl) { private fun <OPS : RPCOps> InternalRPCMessagingClient.start(ops: OPS, securityManager: RPCSecurityManager, brokerControl: ActiveMQServerControl) {
apply { apply {
init(ops, securityManager, TestingNamedCacheFactory()) init(listOf(ops), securityManager, TestingNamedCacheFactory())
start(brokerControl) start(brokerControl)
} }
} }

View File

@ -11,7 +11,6 @@ import net.corda.core.internal.div
import net.corda.core.internal.inputStream import net.corda.core.internal.inputStream
import net.corda.core.internal.isRegularFile import net.corda.core.internal.isRegularFile
import net.corda.core.internal.list import net.corda.core.internal.list
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.internal.readFully import net.corda.core.internal.readFully
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
@ -21,6 +20,7 @@ import net.corda.node.services.statemachine.CountUpDownLatch
import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.ALICE_NAME
import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.driver.internal.checkpoint.CheckpointRpcHelper.checkpointsRpc
import net.corda.testing.node.User import net.corda.testing.node.User
import net.corda.testing.node.internal.enclosedCordapp import net.corda.testing.node.internal.enclosedCordapp
import org.junit.Test import org.junit.Test
@ -44,15 +44,14 @@ class DumpCheckpointsTest {
val nodeAHandle = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() val nodeAHandle = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow()
CordaRPCClient(nodeAHandle.rpcAddress).start(user.username, user.password).use { CordaRPCClient(nodeAHandle.rpcAddress).start(user.username, user.password).use {
val proxy = it.proxy as InternalCordaRPCOps
// 1 for GetNumberOfCheckpointsFlow itself // 1 for GetNumberOfCheckpointsFlow itself
val checkPointCountFuture = proxy.startFlow(::GetNumberOfCheckpointsFlow).returnValue val checkPointCountFuture = it.proxy.startFlow(::GetNumberOfCheckpointsFlow).returnValue
val logDirPath = nodeAHandle.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME val logDirPath = nodeAHandle.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME
logDirPath.createDirectories() logDirPath.createDirectories()
dumpCheckPointLatch.await() dumpCheckPointLatch.await()
proxy.dumpCheckpoints() nodeAHandle.checkpointsRpc.use { checkpointRPCOps -> checkpointRPCOps.dumpCheckpoints() }
flowProceedLatch.countDown() flowProceedLatch.countDown()
assertEquals(1, checkPointCountFuture.get()) assertEquals(1, checkPointCountFuture.get())

View File

@ -37,7 +37,8 @@ import net.corda.core.internal.concurrent.flatMap
import net.corda.core.internal.concurrent.map import net.corda.core.internal.concurrent.map
import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.openFuture
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.internal.messaging.InternalCordaRPCOps import net.corda.core.internal.messaging.AttachmentTrustInfoRPCOps
import net.corda.core.internal.messaging.FlowManagerRPCOps
import net.corda.core.internal.notary.NotaryService import net.corda.core.internal.notary.NotaryService
import net.corda.core.internal.rootMessage import net.corda.core.internal.rootMessage
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
@ -71,6 +72,8 @@ import net.corda.djvm.source.EmptyApi
import net.corda.djvm.source.UserSource import net.corda.djvm.source.UserSource
import net.corda.node.CordaClock import net.corda.node.CordaClock
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.internal.attachments.AttachmentTrustInfoRPCOpsImpl
import net.corda.node.internal.checkpoints.FlowManagerRPCOpsImpl
import net.corda.node.internal.classloading.requireAnnotation import net.corda.node.internal.classloading.requireAnnotation
import net.corda.node.internal.cordapp.CordappConfigFileProvider import net.corda.node.internal.cordapp.CordappConfigFileProvider
import net.corda.node.internal.cordapp.CordappProviderImpl import net.corda.node.internal.cordapp.CordappProviderImpl
@ -394,21 +397,28 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
return this return this
} }
/** The implementation of the [CordaRPCOps] interface used by this node. */ /** The implementation of the [RPCOps] interfaces used by this node. */
open fun makeRPCOps(cordappLoader: CordappLoader, checkpointDumper: CheckpointDumperImpl): CordaRPCOps { open fun makeRPCOps(cordappLoader: CordappLoader): List<RPCOps> {
val ops: InternalCordaRPCOps = CordaRPCOpsImpl( val cordaRPCOpsImpl = Pair(CordaRPCOps::class.java, CordaRPCOpsImpl(
services, services,
smm, smm,
flowStarter, flowStarter
checkpointDumper
) { ) {
shutdownExecutor.submit(::stop) shutdownExecutor.submit(::stop)
}.also { it.closeOnStop() } }.also { it.closeOnStop() })
val proxies = mutableListOf<(InternalCordaRPCOps) -> InternalCordaRPCOps>()
// Mind that order is relevant here. val checkpointRPCOpsImpl = Pair(FlowManagerRPCOps::class.java, FlowManagerRPCOpsImpl(checkpointDumper))
proxies += ::AuthenticatedRpcOpsProxy
proxies += { ThreadContextAdjustingRpcOpsProxy(it, cordappLoader.appClassLoader) } val attachmentTrustInfoRPCOps = Pair(AttachmentTrustInfoRPCOps::class.java, AttachmentTrustInfoRPCOpsImpl(services.attachmentTrustCalculator))
return proxies.fold(ops) { delegate, decorate -> decorate(delegate) }
return listOf(cordaRPCOpsImpl, checkpointRPCOpsImpl, attachmentTrustInfoRPCOps).map { rpcOpsImplPair ->
// Mind that order of proxies is important
val targetInterface = rpcOpsImplPair.first
val stage1Proxy = AuthenticatedRpcOpsProxy.proxy(rpcOpsImplPair.second, targetInterface)
val stage2Proxy = ThreadContextAdjustingRpcOpsProxy.proxy(stage1Proxy, targetInterface, cordappLoader.appClassLoader)
stage2Proxy
}
} }
private fun quasarExcludePackages(nodeConfiguration: NodeConfiguration) { private fun quasarExcludePackages(nodeConfiguration: NodeConfiguration) {
@ -513,7 +523,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
installCoreFlows() installCoreFlows()
registerCordappFlows() registerCordappFlows()
services.rpcFlows += cordappLoader.cordapps.flatMap { it.rpcFlows } services.rpcFlows += cordappLoader.cordapps.flatMap { it.rpcFlows }
val rpcOps = makeRPCOps(cordappLoader, checkpointDumper) val rpcOps = makeRPCOps(cordappLoader)
startShell() startShell()
networkMapClient?.start(trustRoot) networkMapClient?.start(trustRoot)
@ -623,7 +633,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
} }
/** Subclasses must override this to create a "started" node of the desired type, using the provided machinery. */ /** Subclasses must override this to create a "started" node of the desired type, using the provided machinery. */
abstract fun createStartedNode(nodeInfo: NodeInfo, rpcOps: CordaRPCOps, notaryService: NotaryService?): S abstract fun createStartedNode(nodeInfo: NodeInfo, rpcOps: List<RPCOps>, notaryService: NotaryService?): S
private fun verifyCheckpointsCompatible(tokenizableServices: List<Any>) { private fun verifyCheckpointsCompatible(tokenizableServices: List<Any>) {
try { try {
@ -1035,7 +1045,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
protected abstract fun makeMessagingService(): MessagingService protected abstract fun makeMessagingService(): MessagingService
protected abstract fun startMessagingService(rpcOps: RPCOps, protected abstract fun startMessagingService(rpcOps: List<RPCOps>,
nodeInfo: NodeInfo, nodeInfo: NodeInfo,
myNotaryIdentity: PartyAndCertificate?, myNotaryIdentity: PartyAndCertificate?,
networkParameters: NetworkParameters) networkParameters: NetworkParameters)

View File

@ -18,12 +18,11 @@ import net.corda.core.flows.StateMachineRunId
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.AttachmentTrustInfo
import net.corda.core.internal.FlowStateMachineHandle import net.corda.core.internal.FlowStateMachineHandle
import net.corda.core.internal.RPC_UPLOADER import net.corda.core.internal.RPC_UPLOADER
import net.corda.core.internal.STRUCTURAL_STEP_PREFIX import net.corda.core.internal.STRUCTURAL_STEP_PREFIX
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.internal.sign import net.corda.core.internal.sign
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.DataFeed import net.corda.core.messaging.DataFeed
import net.corda.core.messaging.FlowHandle import net.corda.core.messaging.FlowHandle
import net.corda.core.messaging.FlowHandleImpl import net.corda.core.messaging.FlowHandleImpl
@ -54,7 +53,6 @@ import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.services.api.FlowStarter import net.corda.node.services.api.FlowStarter
import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.services.rpc.CheckpointDumperImpl
import net.corda.node.services.rpc.context import net.corda.node.services.rpc.context
import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.statemachine.StateMachineManager
import net.corda.nodeapi.exceptions.NonRpcFlowException import net.corda.nodeapi.exceptions.NonRpcFlowException
@ -75,9 +73,8 @@ internal class CordaRPCOpsImpl(
private val services: ServiceHubInternal, private val services: ServiceHubInternal,
private val smm: StateMachineManager, private val smm: StateMachineManager,
private val flowStarter: FlowStarter, private val flowStarter: FlowStarter,
private val checkpointDumper: CheckpointDumperImpl,
private val shutdownNode: () -> Unit private val shutdownNode: () -> Unit
) : InternalCordaRPCOps, AutoCloseable { ) : CordaRPCOps, AutoCloseable {
private companion object { private companion object {
private val logger = loggerFor<CordaRPCOpsImpl>() private val logger = loggerFor<CordaRPCOpsImpl>()
@ -157,13 +154,6 @@ internal class CordaRPCOpsImpl(
return services.validatedTransactions.track() return services.validatedTransactions.track()
} }
override fun dumpCheckpoints() = checkpointDumper.dumpCheckpoints()
override val attachmentTrustInfos: List<AttachmentTrustInfo>
get() {
return services.attachmentTrustCalculator.calculateAllTrustInfo()
}
override fun stateMachinesSnapshot(): List<StateMachineInfo> { override fun stateMachinesSnapshot(): List<StateMachineInfo> {
val (snapshot, updates) = stateMachinesFeed() val (snapshot, updates) = stateMachinesFeed()
updates.notUsed() updates.notUsed()

View File

@ -22,7 +22,6 @@ import net.corda.core.internal.errors.AddressBindingException
import net.corda.core.internal.getJavaUpdateVersion import net.corda.core.internal.getJavaUpdateVersion
import net.corda.core.internal.isRegularFile import net.corda.core.internal.isRegularFile
import net.corda.core.internal.notary.NotaryService import net.corda.core.internal.notary.NotaryService
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
@ -140,7 +139,7 @@ open class Node(configuration: NodeConfiguration,
allowHibernateToManageAppSchema = allowHibernateToManageAppSchema allowHibernateToManageAppSchema = allowHibernateToManageAppSchema
) { ) {
override fun createStartedNode(nodeInfo: NodeInfo, rpcOps: CordaRPCOps, notaryService: NotaryService?): NodeInfo = override fun createStartedNode(nodeInfo: NodeInfo, rpcOps: List<RPCOps>, notaryService: NotaryService?): NodeInfo =
nodeInfo nodeInfo
companion object { companion object {
@ -340,7 +339,7 @@ open class Node(configuration: NodeConfiguration,
) )
} }
override fun startMessagingService(rpcOps: RPCOps, nodeInfo: NodeInfo, myNotaryIdentity: PartyAndCertificate?, networkParameters: NetworkParameters) { override fun startMessagingService(rpcOps: List<RPCOps>, nodeInfo: NodeInfo, myNotaryIdentity: PartyAndCertificate?, networkParameters: NetworkParameters) {
require(nodeInfo.legalIdentities.size in 1..2) { "Currently nodes must have a primary address and optionally one serviced address" } require(nodeInfo.legalIdentities.size in 1..2) { "Currently nodes must have a primary address and optionally one serviced address" }
network as P2PMessagingClient network as P2PMessagingClient

View File

@ -0,0 +1,16 @@
package net.corda.node.internal.attachments
import net.corda.core.internal.AttachmentTrustCalculator
import net.corda.core.internal.AttachmentTrustInfo
import net.corda.core.internal.PLATFORM_VERSION
import net.corda.core.internal.messaging.AttachmentTrustInfoRPCOps
class AttachmentTrustInfoRPCOpsImpl(private val attachmentTrustCalculator: AttachmentTrustCalculator) : AttachmentTrustInfoRPCOps {
override val protocolVersion: Int = PLATFORM_VERSION
override val attachmentTrustInfos: List<AttachmentTrustInfo>
get() {
return attachmentTrustCalculator.calculateAllTrustInfo()
}
}

View File

@ -0,0 +1,15 @@
package net.corda.node.internal.checkpoints
import net.corda.core.internal.PLATFORM_VERSION
import net.corda.core.internal.messaging.FlowManagerRPCOps
import net.corda.node.services.rpc.CheckpointDumperImpl
/**
* Implementation of [FlowManagerRPCOps]
*/
internal class FlowManagerRPCOpsImpl(private val checkpointDumper: CheckpointDumperImpl) : FlowManagerRPCOps {
override val protocolVersion: Int = PLATFORM_VERSION
override fun dumpCheckpoints() = checkpointDumper.dumpCheckpoints()
}

View File

@ -1,55 +1,72 @@
package net.corda.node.internal.rpc.proxies package net.corda.node.internal.rpc.proxies
import net.corda.client.rpc.PermissionException import net.corda.client.rpc.PermissionException
import net.corda.core.flows.FlowLogic
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.internal.utilities.InvocationHandlerTemplate import net.corda.core.internal.utilities.InvocationHandlerTemplate
import net.corda.core.messaging.RPCOps
import net.corda.node.internal.rpc.proxies.RpcAuthHelper.methodFullName
import net.corda.node.services.rpc.RpcAuthContext import net.corda.node.services.rpc.RpcAuthContext
import net.corda.node.services.rpc.rpcContext import net.corda.node.services.rpc.rpcContext
import java.lang.reflect.Method import java.lang.reflect.Method
import java.lang.reflect.Proxy import java.lang.reflect.Proxy
/** /**
* Implementation of [CordaRPCOps] that checks authorisation. * Creates proxy that checks entitlements for every RPCOps interface call.
*/ */
internal class AuthenticatedRpcOpsProxy(private val delegate: InternalCordaRPCOps) : InternalCordaRPCOps by proxy(delegate, ::rpcContext) { internal object AuthenticatedRpcOpsProxy {
/**
* Returns the RPC protocol version, which is the same the node's Platform Version. Exists since version 1 so guaranteed
* to be present.
*
* TODO: Why is this logic duplicated vs the actual implementation?
*/
override val protocolVersion: Int get() = delegate.nodeInfo().platformVersion
// Need overriding to pass additional `listOf(logicType)` argument for polymorphic `startFlow` permissions. fun <T : RPCOps> proxy(delegate: T, targetInterface: Class<out T>): T {
override fun <T> startFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?) = guard("startFlowDynamic", listOf(logicType), ::rpcContext) { require(targetInterface.isInterface) { "Interface is expected instead of $targetInterface" }
delegate.startFlowDynamic(logicType, *args) val handler = PermissionsEnforcingInvocationHandler(delegate, targetInterface)
@Suppress("UNCHECKED_CAST")
return Proxy.newProxyInstance(delegate::class.java.classLoader, arrayOf(targetInterface), handler) as T
} }
// Need overriding to pass additional `listOf(logicType)` argument for polymorphic `startFlow` permissions. private class PermissionsEnforcingInvocationHandler(override val delegate: Any, private val clazz: Class<*>) : InvocationHandlerTemplate {
override fun <T> startTrackedFlowDynamic(logicType: Class<out FlowLogic<T>>, vararg args: Any?) = guard("startTrackedFlowDynamic", listOf(logicType), ::rpcContext) {
delegate.startTrackedFlowDynamic(logicType, *args) private val exemptMethod = RPCOps::class.java.getMethod("getProtocolVersion")
private val namedInterfaces = setOf(
net.corda.core.messaging.CordaRPCOps::class.java)
private val namedMethods = setOf("startFlowDynamic", "startTrackedFlowDynamic")
override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?): Any? {
if (method == exemptMethod) {
// "getProtocolVersion" is an exempt from entitlements check as this is the very first *any* RPCClient calls upon login
return super.invoke(proxy, method, arguments)
} }
private companion object { val importantArgs = if (clazz in namedInterfaces && method.name in namedMethods) {
private fun proxy(delegate: InternalCordaRPCOps, context: () -> RpcAuthContext): InternalCordaRPCOps { // Normally list of arguments makes no difference when checking entitlements, however when starting flows
val handler = PermissionsEnforcingInvocationHandler(delegate, context) // first argument represents a class name of the flow to be started and special handling applies in this case with
return Proxy.newProxyInstance(delegate::class.java.classLoader, arrayOf(InternalCordaRPCOps::class.java), handler) as InternalCordaRPCOps // name of the class extracted and passed into `guard` method for entitlements check.
} val nonNullArgs = requireNotNull(arguments)
require(nonNullArgs.isNotEmpty())
val firstArgClass = requireNotNull(nonNullArgs[0] as? Class<*>)
listOf(firstArgClass)
} else emptyList()
return guard(method, importantArgs, ::rpcContext) { super.invoke(proxy, method, arguments) }
} }
private class PermissionsEnforcingInvocationHandler(override val delegate: InternalCordaRPCOps, private val context: () -> RpcAuthContext) : InvocationHandlerTemplate { private fun <RESULT> guard(method: Method, args: List<Class<*>>, context: () -> RpcAuthContext, action: () -> RESULT): RESULT {
override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?) = guard(method.name, context) { super.invoke(proxy, method, arguments) } if (!context().isPermitted(methodFullName(method), *(args.map(Class<*>::getName).toTypedArray()))) {
} throw PermissionException("User not authorized to perform RPC call $method with target $args")
}
private fun <RESULT> guard(methodName: String, context: () -> RpcAuthContext, action: () -> RESULT) = guard(methodName, emptyList(), context, action)
private fun <RESULT> guard(methodName: String, args: List<Class<*>>, context: () -> RpcAuthContext, action: () -> RESULT): RESULT {
if (!context().isPermitted(methodName, *(args.map(Class<*>::getName).toTypedArray()))) {
throw PermissionException("User not authorized to perform RPC call $methodName with target $args")
} else { } else {
return action() return action()
} }
}
}
}
object RpcAuthHelper {
const val INTERFACE_SEPARATOR = "#"
fun methodFullName(method: Method):String = methodFullName(method.declaringClass, method.name)
fun methodFullName(clazz: Class<*>, methodName: String): String {
require(clazz.isInterface) { "Must be an interface: $clazz"}
require(RPCOps::class.java.isAssignableFrom(clazz)) { "Must be assignable from RPCOps: $clazz" }
return clazz.name + INTERFACE_SEPARATOR + methodName
}
} }

View File

@ -1,30 +1,28 @@
package net.corda.node.internal.rpc.proxies package net.corda.node.internal.rpc.proxies
import net.corda.core.internal.executeWithThreadContextClassLoader import net.corda.core.internal.executeWithThreadContextClassLoader
import net.corda.core.internal.messaging.InternalCordaRPCOps import net.corda.core.messaging.RPCOps
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.internal.utilities.InvocationHandlerTemplate import net.corda.core.internal.utilities.InvocationHandlerTemplate
import java.lang.reflect.Method import java.lang.reflect.Method
import java.lang.reflect.Proxy import java.lang.reflect.Proxy
/** /**
* A [CordaRPCOps] proxy that adjusts the thread context's class loader temporarily on every invocation with the provided classloader. * A proxy that adjusts the thread context's class loader temporarily on every invocation of supplied interface with the provided classloader.
* As an example, this can be used to work-around cases, where 3rd party libraries prioritise the thread context's class loader over the current one, * As an example, this can be used to work-around cases, where 3rd party libraries prioritise the thread context's class loader over the current one,
* without sensible fallbacks to the classloader of the current instance. * without sensible fallbacks to the classloader of the current instance.
* If clients' CorDapps use one of these libraries, this temporary adjustment can ensure that any provided classes from these libraries will be available during RPC calls. * If clients' CorDapps use one of these libraries, this temporary adjustment can ensure that any provided classes from these libraries will be available during RPC calls.
*/ */
internal class ThreadContextAdjustingRpcOpsProxy(private val delegate: InternalCordaRPCOps, private val classLoader: ClassLoader) : InternalCordaRPCOps by proxy(delegate, classLoader) { internal object ThreadContextAdjustingRpcOpsProxy {
private companion object { fun <T : RPCOps> proxy(delegate: T, clazz: Class<out T>, classLoader: ClassLoader): T {
private fun proxy(delegate: InternalCordaRPCOps, classLoader: ClassLoader): InternalCordaRPCOps { require(clazz.isInterface) { "Interface is expected instead of $clazz" }
val handler = ThreadContextAdjustingRpcOpsProxy.ThreadContextAdjustingInvocationHandler(delegate, classLoader) val handler = ThreadContextAdjustingInvocationHandler(delegate, classLoader)
return Proxy.newProxyInstance(delegate::class.java.classLoader, arrayOf(InternalCordaRPCOps::class.java), handler) as InternalCordaRPCOps @Suppress("UNCHECKED_CAST")
} return Proxy.newProxyInstance(delegate::class.java.classLoader, arrayOf(clazz), handler) as T
} }
private class ThreadContextAdjustingInvocationHandler(override val delegate: InternalCordaRPCOps, private val classLoader: ClassLoader) : InvocationHandlerTemplate { internal class ThreadContextAdjustingInvocationHandler(override val delegate: Any, private val classLoader: ClassLoader) : InvocationHandlerTemplate {
override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?): Any? { override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?): Any? {
return executeWithThreadContextClassLoader(this.classLoader) { super.invoke(proxy, method, arguments) } return executeWithThreadContextClassLoader(this.classLoader) { super.invoke(proxy, method, arguments) }
} }
} }
} }

View File

@ -0,0 +1,175 @@
package net.corda.node.internal.security
import com.github.benmanes.caffeine.cache.CacheLoader
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.LoadingCache
import net.corda.core.internal.toMultiMap
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.RPCOps
import net.corda.node.internal.rpc.proxies.RpcAuthHelper.INTERFACE_SEPARATOR
import net.corda.node.internal.rpc.proxies.RpcAuthHelper.methodFullName
import org.apache.shiro.authz.Permission
import org.apache.shiro.authz.permission.PermissionResolver
import org.slf4j.LoggerFactory
import java.lang.reflect.Method
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KProperty
import kotlin.reflect.jvm.javaMethod
/*
* A [org.apache.shiro.authz.permission.PermissionResolver] implementation for RPC permissions.
* Provides a method to construct an [RPCPermission] instance from its string representation
* in the form used by a Node admin.
*
* Currently valid permission strings have the forms:
*
* - `ALL`: allowing all type of RPC calls
*
* - `InvokeRpc.$RPCMethodName`: allowing to call a given RPC method without restrictions on its arguments.
*
* - `StartFlow.$FlowClassName`: allowing to call a `startFlow*` RPC method targeting a Flow instance
* of a given class
*/
internal object RPCPermissionResolver : PermissionResolver {
private val logger = LoggerFactory.getLogger(RPCPermissionResolver::class.java)
private const val SEPARATOR = '.'
private const val NEW_STYLE_SEP = ":"
private const val ACTION_START_FLOW = "startflow"
private const val ACTION_INVOKE_RPC = "invokerpc"
private const val ACTION_ALL = "all"
private val FLOW_RPC_CALLS = setOf(
"startFlowDynamic",
"startTrackedFlowDynamic",
"startFlow",
"startTrackedFlow")
override fun resolvePermission(representation: String): Permission {
when (representation.substringBefore(SEPARATOR).toLowerCase()) {
ACTION_INVOKE_RPC -> {
val rpcCall = representation.substringAfter(SEPARATOR, "")
require(representation.count { it == SEPARATOR } == 1 && rpcCall.isNotEmpty()) {
"Malformed permission string"
}
val legacyPermitted = when (rpcCall) {
"startFlow" -> setOf("startFlowDynamic", rpcCall)
"startTrackedFlow" -> setOf("startTrackedFlowDynamic", rpcCall)
else -> setOf(rpcCall)
}
return RPCPermission(legacyPermitted.toFullyQualified())
}
ACTION_START_FLOW -> {
val targetFlow = representation.substringAfter(SEPARATOR, "")
require(targetFlow.isNotEmpty()) {
"Missing target flow after StartFlow"
}
return RPCPermission(FLOW_RPC_CALLS.toFullyQualified(), targetFlow)
}
ACTION_ALL -> {
// Leaving empty set of targets and actions to match everything
return RPCPermission()
}
else -> return attemptNewStyleParsing(representation)
}
}
private fun Set<String>.toFullyQualified(): Set<String> {
return map { methodFullName(CordaRPCOps::class.java, it) }.toSet()
}
/**
* New style permissions representation:
* 1. Fully qualified form: InvokeRpc:com.fully.qualified.package.CustomClientRpcOps#firstMethod
* 2. All methods of the interface: InvokeRpc:com.fully.qualified.package.CustomClientRpcOps#ALL
* 3. Methods of specific group: InvokeRpc:com.fully.qualified.package.CustomClientRpcOps#READONLY
*/
private fun attemptNewStyleParsing(permAsString: String): Permission {
return when(permAsString.substringBefore(NEW_STYLE_SEP).toLowerCase()) {
ACTION_INVOKE_RPC -> {
val interfaceAndMethods = permAsString.substringAfter(NEW_STYLE_SEP, "")
val interfaceParts = interfaceAndMethods.split(INTERFACE_SEPARATOR)
require(interfaceParts.size == 2) { "Malformed to comply with new style of InvokeRpc: $interfaceAndMethods" }
val methodsMap = requireNotNull(cache.get(interfaceParts[0]))
{ "Method map for ${interfaceParts[0]} must not be null in the cache. There must have been error processing interface. " +
"Please look at the error log lines above." }
val lookupKey = interfaceAndMethods.toLowerCase()
val methods = requireNotNull(methodsMap[lookupKey]) { "Cannot find record for " +
"'$lookupKey' for interface '${interfaceParts[0]}' in $methodsMap. " +
"Please check permissions configuration string '$permAsString' matching class representation." }
RPCPermission(methods)
}
else -> throw IllegalArgumentException("Unable to parse permission as string: $permAsString")
}
}
private val cache: LoadingCache<String, Map<String, Set<String>>> = Caffeine.newBuilder()
.maximumSize(java.lang.Long.getLong("net.corda.node.internal.security.rpc.interface.cacheSize", 20))
.build(InterfaceMethodMapCacheLoader())
private class InterfaceMethodMapCacheLoader : CacheLoader<String, Map<String, Set<String>>> {
override fun load(interfaceName: String): Map<String, Set<String>>? {
return try {
inspectInterface(interfaceName)
} catch (ex: Exception) {
logger.error("Unexpected error when populating cache for $interfaceName", ex)
null
}
}
}
/**
* Returns a map where key is either:
* - fully qualified interface method name;
* or
* - Wildcard string representing the group of methods like: ALL, READ_ONLY, etc.
* Value is always a set of fully qualified method names.
*/
internal fun inspectInterface(interfaceName: String): Map<String, Set<String>> {
val interfaceClass = Class.forName(interfaceName).kotlin
require(interfaceClass.java.isInterface) { "Must be an interface: $interfaceClass"}
require(RPCOps::class.java.isAssignableFrom(interfaceClass.java)) { "Must be assignable from RPCOps: $interfaceClass" }
val membersPairs = interfaceClass.members.flatMap { member ->
when(member) {
is KFunction -> {
val method = member.javaMethod
if (method != null) {
processMethod(method, interfaceClass)
} else {
logger.info("KFunction $member does not have Java representation - ignoring")
emptyList()
}
}
is KProperty -> {
val method = member.getter.javaMethod
if (method != null) {
processMethod(method, interfaceClass)
} else {
logger.info("KProperty $member does not have Java representation - ignoring")
emptyList()
}
}
else -> {
logger.info("$member is an unhandled type of KCallable - ignoring")
emptyList()
}
}
}
// Pack the pairs into desired resulting data structure
return membersPairs.toMultiMap().mapValues { it.value.toSet() }
}
private fun processMethod(method: Method, interfaceClass: KClass<out Any>): List<Pair<String, String>> {
if(!RPCOps::class.java.isAssignableFrom(method.declaringClass)) {
// To prevent going too deep to Object level
return emptyList()
}
val allKey = methodFullName(interfaceClass.java, ACTION_ALL).toLowerCase()
val methodFullName = methodFullName(method)
return listOf(allKey to methodFullName) + // ALL group
listOf(methodFullName.toLowerCase() to methodFullName) // Full method names individually
}
}

View File

@ -16,10 +16,8 @@ import org.apache.shiro.authc.*
import org.apache.shiro.authc.credential.PasswordMatcher import org.apache.shiro.authc.credential.PasswordMatcher
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher import org.apache.shiro.authc.credential.SimpleCredentialsMatcher
import org.apache.shiro.authz.AuthorizationInfo import org.apache.shiro.authz.AuthorizationInfo
import org.apache.shiro.authz.Permission
import org.apache.shiro.authz.SimpleAuthorizationInfo import org.apache.shiro.authz.SimpleAuthorizationInfo
import org.apache.shiro.authz.permission.DomainPermission import org.apache.shiro.authz.permission.DomainPermission
import org.apache.shiro.authz.permission.PermissionResolver
import org.apache.shiro.cache.CacheManager import org.apache.shiro.cache.CacheManager
import org.apache.shiro.mgt.DefaultSecurityManager import org.apache.shiro.mgt.DefaultSecurityManager
import org.apache.shiro.realm.AuthorizingRealm import org.apache.shiro.realm.AuthorizingRealm
@ -106,7 +104,7 @@ class RPCSecurityManagerImpl(config: AuthServiceConfig, cacheFactory: NamedCache
* represented by instances of the [Permission] interface which offers a single method: [implies], to * represented by instances of the [Permission] interface which offers a single method: [implies], to
* test if the 'x implies y' binary predicate is satisfied. * test if the 'x implies y' binary predicate is satisfied.
*/ */
private class RPCPermission : DomainPermission { internal class RPCPermission : DomainPermission {
/** /**
* Helper constructor directly setting actions and target field * Helper constructor directly setting actions and target field
@ -123,63 +121,6 @@ private class RPCPermission : DomainPermission {
constructor() : super() constructor() : super()
} }
/*
* A [org.apache.shiro.authz.permission.PermissionResolver] implementation for RPC permissions.
* Provides a method to construct an [RPCPermission] instance from its string representation
* in the form used by a Node admin.
*
* Currently valid permission strings have the forms:
*
* - `ALL`: allowing all type of RPC calls
*
* - `InvokeRpc.$RPCMethodName`: allowing to call a given RPC method without restrictions on its arguments.
*
* - `StartFlow.$FlowClassName`: allowing to call a `startFlow*` RPC method targeting a Flow instance
* of a given class
*/
private object RPCPermissionResolver : PermissionResolver {
private const val SEPARATOR = '.'
private const val ACTION_START_FLOW = "startflow"
private const val ACTION_INVOKE_RPC = "invokerpc"
private const val ACTION_ALL = "all"
private val FLOW_RPC_CALLS = setOf(
"startFlowDynamic",
"startTrackedFlowDynamic",
"startFlow",
"startTrackedFlow")
override fun resolvePermission(representation: String): Permission {
val action = representation.substringBefore(SEPARATOR).toLowerCase()
when (action) {
ACTION_INVOKE_RPC -> {
val rpcCall = representation.substringAfter(SEPARATOR, "")
require(representation.count { it == SEPARATOR } == 1 && !rpcCall.isEmpty()) {
"Malformed permission string"
}
val permitted = when(rpcCall) {
"startFlow" -> setOf("startFlowDynamic", rpcCall)
"startTrackedFlow" -> setOf("startTrackedFlowDynamic", rpcCall)
else -> setOf(rpcCall)
}
return RPCPermission(permitted)
}
ACTION_START_FLOW -> {
val targetFlow = representation.substringAfter(SEPARATOR, "")
require(targetFlow.isNotEmpty()) {
"Missing target flow after StartFlow"
}
return RPCPermission(FLOW_RPC_CALLS, targetFlow)
}
ACTION_ALL -> {
// Leaving empty set of targets and actions to match everything
return RPCPermission()
}
else -> throw IllegalArgumentException("Unknown permission action specifier: $action")
}
}
}
class ShiroAuthorizingSubject( class ShiroAuthorizingSubject(
private val subjectId: PrincipalCollection, private val subjectId: PrincipalCollection,
private val manager: DefaultSecurityManager) : AuthorizingSubject { private val manager: DefaultSecurityManager) : AuthorizingSubject {

View File

@ -21,7 +21,7 @@ class InternalRPCMessagingClient(val sslConfig: MutualSslConfiguration, val serv
private var locator: ServerLocator? = null private var locator: ServerLocator? = null
private var rpcServer: RPCServer? = null private var rpcServer: RPCServer? = null
fun init(rpcOps: RPCOps, securityManager: RPCSecurityManager, cacheFactory: NamedCacheFactory) = synchronized(this) { fun init(rpcOps: List<RPCOps>, securityManager: RPCSecurityManager, cacheFactory: NamedCacheFactory) = synchronized(this) {
val tcpTransport = ArtemisTcpTransport.rpcInternalClientTcpTransport(serverAddress, sslConfig) val tcpTransport = ArtemisTcpTransport.rpcInternalClientTcpTransport(serverAddress, sslConfig)
locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply { locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply {

View File

@ -14,7 +14,6 @@ import net.corda.core.context.Trace.InvocationId
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.LifeCycle import net.corda.core.internal.LifeCycle
import net.corda.core.internal.NamedCacheFactory import net.corda.core.internal.NamedCacheFactory
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext
@ -177,10 +176,10 @@ class RPCServer(
val groupedMethods = with(interfaceClass) { val groupedMethods = with(interfaceClass) {
/* /*
* Until version 4.3, rpc calls did not include class names. * Until version 4.3, rpc calls did not include class names.
* Up to this version, only CordaRPCOps and InternalCordaRPCOps were supported. * Up to this version, only CordaRPCOps was supported.
* So, for these classes methods are registered without their class name as well to preserve backwards compatibility. * So, for these classes methods are registered without their class name as well to preserve backwards compatibility.
*/ */
if(interfaceClass == CordaRPCOps::class.java || interfaceClass == InternalCordaRPCOps::class.java) { if (interfaceClass == CordaRPCOps::class.java) {
methods.groupBy { it.name } methods.groupBy { it.name }
} else { } else {
methods.groupBy { interfaceClass.name + CLASS_METHOD_DIVIDER + it.name } methods.groupBy { interfaceClass.name + CLASS_METHOD_DIVIDER + it.name }

View File

@ -98,7 +98,7 @@ class CordaRPCOpsImplTest {
fun setup() { fun setup() {
mockNet = InternalMockNetwork(cordappsForAllNodes = FINANCE_CORDAPPS) mockNet = InternalMockNetwork(cordappsForAllNodes = FINANCE_CORDAPPS)
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME)) aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
rpc = aliceNode.rpcOps rpc = aliceNode.cordaRPCOps
CURRENT_RPC_CONTEXT.set(RpcAuthContext(InvocationContext.rpc(testActor()), buildSubject("TEST_USER", emptySet()))) CURRENT_RPC_CONTEXT.set(RpcAuthContext(InvocationContext.rpc(testActor()), buildSubject("TEST_USER", emptySet())))
mockNet.runNetwork() mockNet.runNetwork()

View File

@ -3,7 +3,7 @@ package net.corda.node.internal.rpc.proxies
import com.nhaarman.mockito_kotlin.any import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.mock
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.internal.messaging.InternalCordaRPCOps import net.corda.core.messaging.CordaRPCOps
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
import org.mockito.Mockito.`when` import org.mockito.Mockito.`when`
@ -12,9 +12,9 @@ class ThreadContextAdjustingRpcOpsProxyTest {
private val coreOps = mock<InstrumentedCordaRPCOps>() private val coreOps = mock<InstrumentedCordaRPCOps>()
private val mockClassloader = mock<ClassLoader>() private val mockClassloader = mock<ClassLoader>()
private val proxy = ThreadContextAdjustingRpcOpsProxy(coreOps, mockClassloader) private val proxy = ThreadContextAdjustingRpcOpsProxy.proxy(coreOps, CordaRPCOps::class.java, mockClassloader)
private interface InstrumentedCordaRPCOps : InternalCordaRPCOps { private interface InstrumentedCordaRPCOps : CordaRPCOps {
fun getThreadContextClassLoader(): ClassLoader = Thread.currentThread().contextClassLoader fun getThreadContextClassLoader(): ClassLoader = Thread.currentThread().contextClassLoader
} }

View File

@ -0,0 +1,57 @@
package net.corda.node.internal.security
import net.corda.core.messaging.RPCOps
import net.corda.node.internal.rpc.proxies.RpcAuthHelper.methodFullName
import org.junit.Test
import java.time.ZonedDateTime
import kotlin.test.assertEquals
class RPCPermissionResolverTest {
@Suppress("unused")
interface Alpha : RPCOps {
fun readAlpha() : String
}
@Suppress("unused")
interface Beta : Alpha {
val betaValue : Int
fun writeBeta(foo: Int)
fun nothingSpecial() : Int
}
@Suppress("unused")
interface Gamma : Beta {
fun readGamma() : ZonedDateTime
}
private val readAlphaMethod = methodFullName(Alpha::class.java.getMethod("readAlpha"))
private val readAlphaMethodKey = readAlphaMethod.toLowerCase()
@Test(timeout=300_000)
fun `test Alpha`() {
with(RPCPermissionResolver.inspectInterface(Alpha::class.java.name)) {
assertEquals(3, size, toString()) // protocolVersion, ALL, readAlpha
assertEquals(setOf(readAlphaMethod), this[readAlphaMethodKey], toString())
}
}
@Test(timeout=300_000)
fun `test Beta`() {
with(RPCPermissionResolver.inspectInterface(Beta::class.java.name)) {
assertEquals(6, size, toString()) // protocolVersion, ALL, readAlpha
// and 3 x Beta methods
}
}
@Test(timeout=300_000)
fun `test Gamma`() {
with(RPCPermissionResolver.inspectInterface(Gamma::class.java.name)) {
assertEquals(7, size, toString()) // protocolVersion, ALL, readAlpha,
// 3 x Beta methods and 1 Gamma method
}
}
}

View File

@ -3,6 +3,7 @@ package net.corda.node.services
import net.corda.core.context.AuthServiceId import net.corda.core.context.AuthServiceId
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.node.internal.rpc.proxies.RpcAuthHelper
import net.corda.node.internal.security.Password import net.corda.node.internal.security.Password
import net.corda.node.internal.security.RPCSecurityManagerImpl import net.corda.node.internal.security.RPCSecurityManagerImpl
import net.corda.node.internal.security.tryAuthenticate import net.corda.node.internal.security.tryAuthenticate
@ -152,14 +153,14 @@ class RPCSecurityManagerTest {
userRealms.tryAuthenticate("user", Password("password"))!!, userRealms.tryAuthenticate("user", Password("password"))!!,
userRealms.buildSubject("user"))) { userRealms.buildSubject("user"))) {
for (request in permitted) { for (request in permitted) {
val call = request.first() val methodName = RpcAuthHelper.methodFullName(CordaRPCOps::class.java, request.first())
val args = request.drop(1).toTypedArray() val args = request.drop(1).toTypedArray()
require(subject.isPermitted(request.first(), *args)) { require(subject.isPermitted(methodName, *args)) {
"User ${subject.principal} should be permitted $call with target '${request.toList()}'" "User ${subject.principal} should be permitted $methodName with target '${request.toList()}'"
} }
if (args.isEmpty()) { if (args.isEmpty()) {
require(subject.isPermitted(request.first(), "XXX")) { require(subject.isPermitted(methodName, "XXX")) {
"User ${subject.principal} should be permitted $call with any target" "User ${subject.principal} should be permitted $methodName with any target"
} }
} }
} }

View File

@ -0,0 +1,24 @@
package net.corda.testing.driver.internal.checkpoint
import net.corda.core.internal.messaging.FlowManagerRPCOps
import net.corda.testing.driver.NodeHandle
object CheckpointRpcHelper {
interface CloseableFlowManagerRPCOps : FlowManagerRPCOps, AutoCloseable
val NodeHandle.checkpointsRpc: CloseableFlowManagerRPCOps
get() {
val user = rpcUsers.first()
val rpcConnection = net.corda.client.rpc.internal.RPCClient<FlowManagerRPCOps>(rpcAddress)
.start(FlowManagerRPCOps::class.java, user.username, user.password)
val proxy = rpcConnection.proxy
return object : CloseableFlowManagerRPCOps, FlowManagerRPCOps by proxy {
override fun close() {
rpcConnection.close()
}
}
}
}

View File

@ -109,7 +109,7 @@ interface TestStartedNode {
val services: StartedNodeServices val services: StartedNodeServices
val smm: StateMachineManager val smm: StateMachineManager
val attachments: NodeAttachmentService val attachments: NodeAttachmentService
val rpcOps: CordaRPCOps val rpcOpsList: List<RPCOps>
val network: MockNodeMessagingService val network: MockNodeMessagingService
val database: CordaPersistence val database: CordaPersistence
val notaryService: NotaryService? val notaryService: NotaryService?
@ -135,6 +135,9 @@ interface TestStartedNode {
fun <T : FlowLogic<*>> registerInitiatedFlow(initiatedFlowClass: Class<T>, track: Boolean = false): Observable<T> fun <T : FlowLogic<*>> registerInitiatedFlow(initiatedFlowClass: Class<T>, track: Boolean = false): Observable<T>
fun <T : FlowLogic<*>> registerInitiatedFlow(initiatingFlowClass: Class<out FlowLogic<*>>, initiatedFlowClass: Class<T>, track: Boolean = false): Observable<T> fun <T : FlowLogic<*>> registerInitiatedFlow(initiatingFlowClass: Class<out FlowLogic<*>>, initiatedFlowClass: Class<T>, track: Boolean = false): Observable<T>
val cordaRPCOps: CordaRPCOps
get() = rpcOpsList.mapNotNull { it as? CordaRPCOps }.single()
} }
open class InternalMockNetwork(cordappPackages: List<String> = emptyList(), open class InternalMockNetwork(cordappPackages: List<String> = emptyList(),
@ -306,7 +309,7 @@ open class InternalMockNetwork(cordappPackages: List<String> = emptyList(),
override val info: NodeInfo, override val info: NodeInfo,
override val smm: StateMachineManager, override val smm: StateMachineManager,
override val database: CordaPersistence, override val database: CordaPersistence,
override val rpcOps: CordaRPCOps, override val rpcOpsList: List<RPCOps>,
override val notaryService: NotaryService?) : TestStartedNode { override val notaryService: NotaryService?) : TestStartedNode {
override fun dispose() = internals.stop() override fun dispose() = internals.stop()
@ -347,7 +350,7 @@ open class InternalMockNetwork(cordappPackages: List<String> = emptyList(),
override val started: TestStartedNode? get() = super.started override val started: TestStartedNode? get() = super.started
override fun createStartedNode(nodeInfo: NodeInfo, rpcOps: CordaRPCOps, notaryService: NotaryService?): TestStartedNode { override fun createStartedNode(nodeInfo: NodeInfo, rpcOps: List<RPCOps>, notaryService: NotaryService?): TestStartedNode {
return TestStartedNodeImpl( return TestStartedNodeImpl(
this, this,
attachments, attachments,
@ -380,7 +383,7 @@ open class InternalMockNetwork(cordappPackages: List<String> = emptyList(),
return MockNodeMessagingService(configuration, serverThread).closeOnStop() return MockNodeMessagingService(configuration, serverThread).closeOnStop()
} }
override fun startMessagingService(rpcOps: RPCOps, override fun startMessagingService(rpcOps: List<RPCOps>,
nodeInfo: NodeInfo, nodeInfo: NodeInfo,
myNotaryIdentity: PartyAndCertificate?, myNotaryIdentity: PartyAndCertificate?,
networkParameters: NetworkParameters) { networkParameters: NetworkParameters) {

View File

@ -21,7 +21,6 @@ import net.corda.core.internal.createDirectories
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.internal.inputStream import net.corda.core.internal.inputStream
import net.corda.core.internal.list import net.corda.core.internal.list
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.messaging.ClientRpcSslOptions import net.corda.core.messaging.ClientRpcSslOptions
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
@ -49,6 +48,7 @@ import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.driver.internal.NodeHandleInternal import net.corda.testing.driver.internal.NodeHandleInternal
import net.corda.testing.driver.internal.checkpoint.CheckpointRpcHelper.checkpointsRpc
import net.corda.testing.internal.useSslRpcOverrides import net.corda.testing.internal.useSslRpcOverrides
import net.corda.testing.node.User import net.corda.testing.node.User
import net.corda.testing.node.internal.enclosedCordapp import net.corda.testing.node.internal.enclosedCordapp
@ -299,7 +299,7 @@ class InteractiveShellIntegrationTest {
(alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).createDirectories() (alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).createDirectories()
alice.rpc.startFlow(::ExternalOperationFlow) alice.rpc.startFlow(::ExternalOperationFlow)
ExternalOperation.lock.acquire() ExternalOperation.lock.acquire()
InteractiveShell.runDumpCheckpoints(alice.rpc as InternalCordaRPCOps) alice.checkpointsRpc.use { InteractiveShell.runDumpCheckpoints(it) }
ExternalOperation.lock2.release() ExternalOperation.lock2.release()
val zipFile = (alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list().first { "checkpoints_dump-" in it.toString() } val zipFile = (alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list().first { "checkpoints_dump-" in it.toString() }
@ -322,7 +322,7 @@ class InteractiveShellIntegrationTest {
(alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).createDirectories() (alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).createDirectories()
alice.rpc.startFlow(::ExternalAsyncOperationFlow) alice.rpc.startFlow(::ExternalAsyncOperationFlow)
ExternalAsyncOperation.lock.acquire() ExternalAsyncOperation.lock.acquire()
InteractiveShell.runDumpCheckpoints(alice.rpc as InternalCordaRPCOps) alice.checkpointsRpc.use { InteractiveShell.runDumpCheckpoints(it) }
ExternalAsyncOperation.future.complete(null) ExternalAsyncOperation.future.complete(null)
val zipFile = (alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list().first { "checkpoints_dump-" in it.toString() } val zipFile = (alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list().first { "checkpoints_dump-" in it.toString() }
val json = ZipInputStream(zipFile.inputStream()).use { zip -> val json = ZipInputStream(zipFile.inputStream()).use { zip ->
@ -350,7 +350,7 @@ class InteractiveShellIntegrationTest {
assertThrows<TimeoutException> { assertThrows<TimeoutException> {
alice.rpc.startFlow(::WaitForStateConsumptionFlow, stateRefs).returnValue.getOrThrow(10.seconds) alice.rpc.startFlow(::WaitForStateConsumptionFlow, stateRefs).returnValue.getOrThrow(10.seconds)
} }
InteractiveShell.runDumpCheckpoints(alice.rpc as InternalCordaRPCOps) alice.checkpointsRpc.use { InteractiveShell.runDumpCheckpoints(it) }
val zipFile = (alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list().first { "checkpoints_dump-" in it.toString() } val zipFile = (alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list().first { "checkpoints_dump-" in it.toString() }
val json = ZipInputStream(zipFile.inputStream()).use { zip -> val json = ZipInputStream(zipFile.inputStream()).use { zip ->
zip.nextEntry zip.nextEntry
@ -390,7 +390,7 @@ class InteractiveShellIntegrationTest {
Thread.sleep(5000) Thread.sleep(5000)
mockRenderPrintWriter() mockRenderPrintWriter()
InteractiveShell.runDumpCheckpoints(aliceNode.rpc as InternalCordaRPCOps) aliceNode.checkpointsRpc.use { InteractiveShell.runDumpCheckpoints(it) }
val zipFile = (aliceNode.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list().first { "checkpoints_dump-" in it.toString() } val zipFile = (aliceNode.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list().first { "checkpoints_dump-" in it.toString() }
val json = ZipInputStream(zipFile.inputStream()).use { zip -> val json = ZipInputStream(zipFile.inputStream()).use { zip ->

View File

@ -1,14 +1,22 @@
package net.corda.tools.shell; package net.corda.tools.shell;
import net.corda.core.internal.messaging.AttachmentTrustInfoRPCOps;
import org.crsh.cli.Command; import org.crsh.cli.Command;
import org.crsh.cli.Man; import org.crsh.cli.Man;
import org.crsh.cli.Named; import org.crsh.cli.Named;
import org.crsh.cli.Usage; import org.crsh.cli.Usage;
import org.jetbrains.annotations.NotNull;
import static net.corda.tools.shell.InteractiveShell.runAttachmentTrustInfoView; import static net.corda.tools.shell.InteractiveShell.runAttachmentTrustInfoView;
@Named("attachments") @Named("attachments")
public class AttachmentShellCommand extends InteractiveShellCommand { public class AttachmentShellCommand extends InteractiveShellCommand<AttachmentTrustInfoRPCOps> {
@NotNull
@Override
public Class<AttachmentTrustInfoRPCOps> getRpcOpsClass() {
return AttachmentTrustInfoRPCOps.class;
}
@Command @Command
@Man("Displays the trusted CorDapp attachments that have been manually installed or received over the network") @Man("Displays the trusted CorDapp attachments that have been manually installed or received over the network")

View File

@ -1,14 +1,22 @@
package net.corda.tools.shell; package net.corda.tools.shell;
import net.corda.core.internal.messaging.FlowManagerRPCOps;
import org.crsh.cli.Command; import org.crsh.cli.Command;
import org.crsh.cli.Man; import org.crsh.cli.Man;
import org.crsh.cli.Named; import org.crsh.cli.Named;
import org.crsh.cli.Usage; import org.crsh.cli.Usage;
import org.jetbrains.annotations.NotNull;
import static net.corda.tools.shell.InteractiveShell.*; import static net.corda.tools.shell.InteractiveShell.*;
@Named("checkpoints") @Named("checkpoints")
public class CheckpointShellCommand extends InteractiveShellCommand { public class CheckpointShellCommand extends InteractiveShellCommand<FlowManagerRPCOps> {
@NotNull
@Override
public Class<FlowManagerRPCOps> getRpcOpsClass() {
return FlowManagerRPCOps.class;
}
@Command @Command
@Man("Outputs the contents of all checkpoints as json to be manually reviewed") @Man("Outputs the contents of all checkpoints as json to be manually reviewed")

View File

@ -27,7 +27,7 @@ import static net.corda.tools.shell.InteractiveShell.runStateMachinesView;
"flow constructors (the right one is picked automatically) are then specified using the same syntax as for the run command." "flow constructors (the right one is picked automatically) are then specified using the same syntax as for the run command."
) )
@Named("flow") @Named("flow")
public class FlowShellCommand extends InteractiveShellCommand { public class FlowShellCommand extends CordaRpcOpsShellCommand {
private static final Logger logger = LoggerFactory.getLogger(FlowShellCommand.class); private static final Logger logger = LoggerFactory.getLogger(FlowShellCommand.class);

View File

@ -19,7 +19,7 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
@Named("hashLookup") @Named("hashLookup")
public class HashLookupShellCommand extends InteractiveShellCommand { public class HashLookupShellCommand extends CordaRpcOpsShellCommand {
private static Logger logger = LoggerFactory.getLogger(HashLookupShellCommand.class); private static Logger logger = LoggerFactory.getLogger(HashLookupShellCommand.class);
private static final String manualText ="Checks if a transaction matching a specified Id hash value is recorded on this node.\n\n" + private static final String manualText ="Checks if a transaction matching a specified Id hash value is recorded on this node.\n\n" +
"Both the transaction Id and the hashed value of a transaction Id (as returned by the Notary in case of a double-spend) is a valid input.\n" + "Both the transaction Id and the hashed value of a transaction Id (as returned by the Notary in case of a double-spend) is a valid input.\n" +

View File

@ -18,7 +18,7 @@ import java.util.Map;
@Man("Allows you to see and update the format that's currently used for the commands' output.") @Man("Allows you to see and update the format that's currently used for the commands' output.")
@Usage("Allows you to see and update the format that's currently used for the commands' output.") @Usage("Allows you to see and update the format that's currently used for the commands' output.")
@Named("output-format") @Named("output-format")
public class OutputFormatCommand extends InteractiveShellCommand { public class OutputFormatCommand extends CordaRpcOpsShellCommand {
public OutputFormatCommand() {} public OutputFormatCommand() {}

View File

@ -25,7 +25,7 @@ import static java.util.Comparator.comparing;
// is the closest you can get in Kotlin to raw types. // is the closest you can get in Kotlin to raw types.
@Named("run") @Named("run")
public class RunShellCommand extends InteractiveShellCommand { public class RunShellCommand extends CordaRpcOpsShellCommand {
private static final Logger logger = LoggerFactory.getLogger(RunShellCommand.class); private static final Logger logger = LoggerFactory.getLogger(RunShellCommand.class);

View File

@ -0,0 +1,8 @@
package net.corda.tools.shell;
import net.corda.core.messaging.RPCOps;
import org.crsh.auth.AuthInfo;
public interface SshAuthInfo extends AuthInfo {
<T extends RPCOps> T getOrCreateRpcOps(Class<T> rpcOpsClass);
}

View File

@ -13,7 +13,7 @@ import java.util.*;
import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.joining;
@Named("start") @Named("start")
public class StartShellCommand extends InteractiveShellCommand { public class StartShellCommand extends CordaRpcOpsShellCommand {
private static Logger logger = LoggerFactory.getLogger(StartShellCommand.class); private static Logger logger = LoggerFactory.getLogger(StartShellCommand.class);

View File

@ -1,14 +1,13 @@
package net.corda.tools.shell package net.corda.tools.shell
import net.corda.client.rpc.CordaRPCConnection import net.corda.core.messaging.CordaRPCOps
import net.corda.core.internal.messaging.InternalCordaRPCOps
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.crsh.auth.AuthInfo import org.crsh.auth.AuthInfo
import org.crsh.auth.AuthenticationPlugin import org.crsh.auth.AuthenticationPlugin
import org.crsh.plugin.CRaSHPlugin import org.crsh.plugin.CRaSHPlugin
class CordaAuthenticationPlugin(private val makeRPCConnection: (username: String, credential: String) -> CordaRPCConnection) : CRaSHPlugin<AuthenticationPlugin<String>>(), AuthenticationPlugin<String> { internal class CordaAuthenticationPlugin(private val rpcOpsProducer: RPCOpsProducer) : CRaSHPlugin<AuthenticationPlugin<String>>(), AuthenticationPlugin<String> {
companion object { companion object {
private val logger = loggerFor<CordaAuthenticationPlugin>() private val logger = loggerFor<CordaAuthenticationPlugin>()
@ -24,9 +23,10 @@ class CordaAuthenticationPlugin(private val makeRPCConnection: (username: String
return AuthInfo.UNSUCCESSFUL return AuthInfo.UNSUCCESSFUL
} }
try { try {
val connection = makeRPCConnection(username, credential) val cordaSSHAuthInfo = CordaSSHAuthInfo(rpcOpsProducer, username, credential, isSsh = true)
val ops = connection.proxy as InternalCordaRPCOps // We cannot guarantee authentication happened successfully till `RCPClient` session been established, hence doing a dummy call
return CordaSSHAuthInfo(true, ops, isSsh = true, rpcConn = connection) cordaSSHAuthInfo.getOrCreateRpcOps(CordaRPCOps::class.java).protocolVersion
return cordaSSHAuthInfo
} catch (e: ActiveMQSecurityException) { } catch (e: ActiveMQSecurityException) {
logger.warn(e.message) logger.warn(e.message)
} catch (e: Exception) { } catch (e: Exception) {

View File

@ -8,6 +8,6 @@ class CordaDisconnectPlugin : CRaSHPlugin<DisconnectPlugin>(), DisconnectPlugin
override fun getImplementation() = this override fun getImplementation() = this
override fun onDisconnect(userName: String?, authInfo: AuthInfo?) { override fun onDisconnect(userName: String?, authInfo: AuthInfo?) {
(authInfo as? CordaSSHAuthInfo)?.rpcConn?.forceClose() (authInfo as? CordaSSHAuthInfo)?.cleanUp()
} }
} }

View File

@ -0,0 +1,22 @@
package net.corda.tools.shell
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.type.TypeFactory
import net.corda.core.messaging.CordaRPCOps
internal abstract class CordaRpcOpsShellCommand : InteractiveShellCommand<CordaRPCOps>() {
override val rpcOpsClass: Class<out CordaRPCOps> = CordaRPCOps::class.java
fun objectMapper(classLoader: ClassLoader?): ObjectMapper {
val om = createYamlInputMapper()
if (classLoader != null) {
om.typeFactory = TypeFactory.defaultInstance().withClassLoader(classLoader)
}
return om
}
private fun createYamlInputMapper(): ObjectMapper {
val rpcOps = ops()
return InteractiveShell.createYamlInputMapper(rpcOps)
}
}

View File

@ -1,17 +1,53 @@
package net.corda.tools.shell package net.corda.tools.shell
import com.fasterxml.jackson.databind.ObjectMapper import com.github.benmanes.caffeine.cache.CacheLoader
import net.corda.client.rpc.CordaRPCConnection import com.github.benmanes.caffeine.cache.Caffeine
import net.corda.core.internal.messaging.InternalCordaRPCOps import com.github.benmanes.caffeine.cache.RemovalListener
import net.corda.tools.shell.InteractiveShell.createYamlInputMapper import com.google.common.util.concurrent.MoreExecutors
import net.corda.client.rpc.RPCConnection
import net.corda.core.internal.utilities.InvocationHandlerTemplate
import net.corda.core.messaging.RPCOps
import net.corda.tools.shell.utlities.ANSIProgressRenderer import net.corda.tools.shell.utlities.ANSIProgressRenderer
import org.crsh.auth.AuthInfo import java.lang.reflect.Proxy
class CordaSSHAuthInfo(val successful: Boolean, val rpcOps: InternalCordaRPCOps, val ansiProgressRenderer: ANSIProgressRenderer? = null, internal class CordaSSHAuthInfo(private val rpcOpsProducer: RPCOpsProducer,
val isSsh: Boolean = false, val rpcConn: CordaRPCConnection? = null) : AuthInfo { private val username: String, private val credential: String, val ansiProgressRenderer: ANSIProgressRenderer? = null,
override fun isSuccessful(): Boolean = successful val isSsh: Boolean = false) : SshAuthInfo {
override fun isSuccessful(): Boolean = true
val yamlInputMapper: ObjectMapper by lazy { /**
createYamlInputMapper(rpcOps) * It is necessary to have a cache to prevent creation of too many proxies for the same class. Proxy ensures that RPC connections gracefully
* closed when cache entry is eliminated
*/
private val proxiesCache = Caffeine.newBuilder()
.maximumSize(10)
.removalListener(RemovalListener<Class<out RPCOps>, Pair<RPCOps, RPCConnection<RPCOps>>> { _, value, _ -> value?.second?.close() })
.executor(MoreExecutors.directExecutor())
.build(CacheLoader<Class<out RPCOps>, Pair<RPCOps, RPCConnection<RPCOps>>> { key -> createRpcOps(key) })
override fun <T : RPCOps> getOrCreateRpcOps(rpcOpsClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
return proxiesCache.get(rpcOpsClass)!!.first as T
}
fun cleanUp() {
proxiesCache.asMap().forEach {
proxiesCache.invalidate(it.key)
it.value.second.forceClose()
}
}
private fun <T : RPCOps> createRpcOps(rpcOpsClass: Class<out T>): Pair<T, RPCConnection<T>> {
val producerResult = rpcOpsProducer(username, credential, rpcOpsClass)
val anotherProxy = proxyRPCOps(producerResult.proxy, rpcOpsClass)
return anotherProxy to producerResult
}
private fun <T : RPCOps> proxyRPCOps(instance: T, rpcOpsClass: Class<out T>): T {
require(rpcOpsClass.isInterface) { "$rpcOpsClass must be an interface" }
@Suppress("UNCHECKED_CAST")
return Proxy.newProxyInstance(rpcOpsClass.classLoader, arrayOf(rpcOpsClass), object : InvocationHandlerTemplate {
override val delegate = instance
}) as T
} }
} }

View File

@ -9,11 +9,8 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator
import net.corda.client.jackson.JacksonSupport import net.corda.client.jackson.JacksonSupport
import net.corda.client.jackson.StringToMethodCallParser import net.corda.client.jackson.StringToMethodCallParser
import net.corda.client.rpc.CordaRPCClient
import net.corda.client.rpc.CordaRPCClientConfiguration
import net.corda.client.rpc.CordaRPCConnection
import net.corda.client.rpc.GracefulReconnect
import net.corda.client.rpc.PermissionException import net.corda.client.rpc.PermissionException
import net.corda.client.rpc.RPCConnection
import net.corda.client.rpc.internal.RPCUtils.isShutdownMethodName import net.corda.client.rpc.internal.RPCUtils.isShutdownMethodName
import net.corda.client.rpc.notUsed import net.corda.client.rpc.notUsed
import net.corda.core.CordaException import net.corda.core.CordaException
@ -27,7 +24,8 @@ import net.corda.core.internal.concurrent.doneFuture
import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.openFuture
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.internal.messaging.InternalCordaRPCOps import net.corda.core.internal.messaging.AttachmentTrustInfoRPCOps
import net.corda.core.internal.messaging.FlowManagerRPCOps
import net.corda.core.internal.packageName_ import net.corda.core.internal.packageName_
import net.corda.core.internal.rootCause import net.corda.core.internal.rootCause
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
@ -72,11 +70,10 @@ import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type import java.lang.reflect.Type
import java.lang.reflect.UndeclaredThrowableException import java.lang.reflect.UndeclaredThrowableException
import java.nio.file.Path import java.nio.file.Path
import java.util.* import java.util.Properties
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
import java.util.concurrent.Future import java.util.concurrent.Future
import kotlin.collections.ArrayList
import kotlin.concurrent.thread import kotlin.concurrent.thread
// TODO: Add command history. // TODO: Add command history.
@ -95,9 +92,9 @@ const val STANDALONE_SHELL_PERMISSION = "ALL"
@Suppress("MaxLineLength") @Suppress("MaxLineLength")
object InteractiveShell { object InteractiveShell {
private val log = LoggerFactory.getLogger(javaClass) private val log = LoggerFactory.getLogger(javaClass)
private lateinit var makeRPCConnection: (username: String, password: String) -> CordaRPCConnection private lateinit var rpcOpsProducer: RPCOpsProducer
private lateinit var ops: InternalCordaRPCOps private lateinit var startupValidation: Lazy<CordaRPCOps>
private lateinit var rpcConn: CordaRPCConnection private var rpcConn: RPCConnection<CordaRPCOps>? = null
private var shell: Shell? = null private var shell: Shell? = null
private var classLoader: ClassLoader? = null private var classLoader: ClassLoader? = null
private lateinit var shellConfiguration: ShellConfiguration private lateinit var shellConfiguration: ShellConfiguration
@ -113,26 +110,7 @@ object InteractiveShell {
} }
fun startShell(configuration: ShellConfiguration, classLoader: ClassLoader? = null, standalone: Boolean = false) { fun startShell(configuration: ShellConfiguration, classLoader: ClassLoader? = null, standalone: Boolean = false) {
makeRPCConnection = { username: String, password: String -> rpcOpsProducer = DefaultRPCOpsProducer(configuration, classLoader, standalone)
val connection = if (standalone) {
CordaRPCClient(
configuration.hostAndPort,
configuration.ssl,
classLoader
).start(username, password, gracefulReconnect = GracefulReconnect())
} else {
CordaRPCClient(
hostAndPort = configuration.hostAndPort,
configuration = CordaRPCClientConfiguration.DEFAULT.copy(
maxReconnectAttempts = 1
),
sslConfiguration = configuration.ssl,
classLoader = classLoader
).start(username, password)
}
rpcConn = connection
connection
}
launchShell(configuration, standalone, classLoader) launchShell(configuration, standalone, classLoader)
} }
@ -253,7 +231,7 @@ object InteractiveShell {
// Don't use the Java language plugin (we may not have tools.jar available at runtime), this // Don't use the Java language plugin (we may not have tools.jar available at runtime), this
// will cause any commands using JIT Java compilation to be suppressed. In CRaSH upstream that // will cause any commands using JIT Java compilation to be suppressed. In CRaSH upstream that
// is only the 'jmx' command. // is only the 'jmx' command.
return super.getPlugins().filterNot { it is JavaLanguage } + CordaAuthenticationPlugin(makeRPCConnection) + return super.getPlugins().filterNot { it is JavaLanguage } + CordaAuthenticationPlugin(rpcOpsProducer) +
CordaDisconnectPlugin() CordaDisconnectPlugin()
} }
} }
@ -262,15 +240,20 @@ object InteractiveShell {
context.refresh() context.refresh()
this.config = config this.config = config
start(context) start(context)
val rpcOps = { username: String, password: String -> makeRPCConnection(username, password).proxy as InternalCordaRPCOps } startupValidation = lazy {
ops = makeRPCOps(rpcOps, localUserName, localUserPassword) rpcOpsProducer(localUserName, localUserPassword, CordaRPCOps::class.java).let {
return context.getPlugin(ShellFactory::class.java).create(null, CordaSSHAuthInfo(false, ops, rpcConn = it
StdoutANSIProgressRenderer), shellSafety) it.proxy
}
}
// For local shell create an artificial authInfo with super user permissions
val authInfo = CordaSSHAuthInfo(rpcOpsProducer, localUserName, localUserPassword, StdoutANSIProgressRenderer)
return context.getPlugin(ShellFactory::class.java).create(null, authInfo, shellSafety)
} }
} }
fun nodeInfo() = try { fun nodeInfo() = try {
ops.nodeInfo() startupValidation.value.nodeInfo()
} catch (e: UndeclaredThrowableException) { } catch (e: UndeclaredThrowableException) {
throw e.cause ?: e throw e.cause ?: e
} }
@ -572,13 +555,13 @@ object InteractiveShell {
@JvmStatic @JvmStatic
fun runAttachmentTrustInfoView( fun runAttachmentTrustInfoView(
out: RenderPrintWriter, out: RenderPrintWriter,
rpcOps: InternalCordaRPCOps rpcOps: AttachmentTrustInfoRPCOps
): Any { ): Any {
return AttachmentTrustTable(out, rpcOps.attachmentTrustInfos) return AttachmentTrustTable(out, rpcOps.attachmentTrustInfos)
} }
@JvmStatic @JvmStatic
fun runDumpCheckpoints(rpcOps: InternalCordaRPCOps) { fun runDumpCheckpoints(rpcOps: FlowManagerRPCOps) {
rpcOps.dumpCheckpoints() rpcOps.dumpCheckpoints()
} }
@ -682,7 +665,7 @@ object InteractiveShell {
latch.await() latch.await()
// Unsubscribe or we hold up the shutdown // Unsubscribe or we hold up the shutdown
subscription.unsubscribe() subscription.unsubscribe()
rpcConn.forceClose() rpcConn?.forceClose()
onExit.invoke() onExit.invoke()
} catch (e: InterruptedException) { } catch (e: InterruptedException) {
// Cancelled whilst draining flows. So let's carry on from here // Cancelled whilst draining flows. So let's carry on from here
@ -690,7 +673,7 @@ object InteractiveShell {
display { println("...cancelled clean shutdown.") } display { println("...cancelled clean shutdown.") }
} }
} catch (e: Exception) { } catch (e: Exception) {
display { println("RPC failed: ${e.rootCause}", Color.red) } display { println("RPC failed: ${e.rootCause}", Decoration.bold, Color.red) }
} finally { } finally {
InputStreamSerializer.invokeContext = null InputStreamSerializer.invokeContext = null
InputStreamDeserializer.closeAll() InputStreamDeserializer.closeAll()

View File

@ -1,23 +1,24 @@
package net.corda.tools.shell package net.corda.tools.shell
import com.fasterxml.jackson.databind.ObjectMapper import net.corda.core.messaging.RPCOps
import com.fasterxml.jackson.databind.type.TypeFactory
import org.crsh.command.BaseCommand import org.crsh.command.BaseCommand
import org.crsh.shell.impl.command.CRaSHSession import org.crsh.shell.impl.command.CRaSHSession
/** /**
* Simply extends CRaSH BaseCommand to add easy access to the RPC ops class. * Simply extends CRaSH BaseCommand to add easy access to the RPC ops class.
*/ */
open class InteractiveShellCommand : BaseCommand() { internal abstract class InteractiveShellCommand<T : RPCOps> : BaseCommand() {
fun ops() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).rpcOps
abstract val rpcOpsClass: Class<out T>
@Suppress("UNCHECKED_CAST")
fun ops(): T {
val cRaSHSession = context.session as CRaSHSession
val authInfo = cRaSHSession.authInfo as SshAuthInfo
return authInfo.getOrCreateRpcOps(rpcOpsClass)
}
fun ansiProgressRenderer() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).ansiProgressRenderer fun ansiProgressRenderer() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).ansiProgressRenderer
fun objectMapper(classLoader: ClassLoader?): ObjectMapper {
val om = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).yamlInputMapper
if (classLoader != null) {
om.typeFactory = TypeFactory.defaultInstance().withClassLoader(classLoader)
}
return om
}
fun isSsh() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).isSsh fun isSsh() = ((context.session as CRaSHSession).authInfo as CordaSSHAuthInfo).isSsh
} }

View File

@ -0,0 +1,49 @@
package net.corda.tools.shell
import net.corda.client.rpc.CordaRPCClient
import net.corda.client.rpc.CordaRPCClientConfiguration
import net.corda.client.rpc.GracefulReconnect
import net.corda.client.rpc.RPCConnection
import net.corda.client.rpc.internal.RPCClient
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.RPCOps
internal interface RPCOpsProducer {
/**
* Returns [RPCConnection] of underlying proxy. Proxy can be obtained at any time by calling [RPCConnection.proxy]
*/
operator fun <T : RPCOps> invoke(username: String?, credential: String?, rpcOpsClass: Class<T>) : RPCConnection<T>
}
internal class DefaultRPCOpsProducer(private val configuration: ShellConfiguration, private val classLoader: ClassLoader? = null, private val standalone: Boolean) : RPCOpsProducer {
override fun <T : RPCOps> invoke(username: String?, credential: String?, rpcOpsClass: Class<T>): RPCConnection<T> {
return if (rpcOpsClass == CordaRPCOps::class.java) {
// For CordaRPCOps we are using CordaRPCClient
val connection = if (standalone) {
CordaRPCClient(
configuration.hostAndPort,
configuration.ssl,
classLoader
).start(username!!, credential!!, gracefulReconnect = GracefulReconnect())
} else {
CordaRPCClient(
hostAndPort = configuration.hostAndPort,
configuration = CordaRPCClientConfiguration.DEFAULT.copy(
maxReconnectAttempts = 1
),
sslConfiguration = configuration.ssl,
classLoader = classLoader
).start(username!!, credential!!)
}
@Suppress("UNCHECKED_CAST")
connection as RPCConnection<T>
} else {
// For other types "plain" RPCClient is used
val rpcClient = RPCClient<T>(configuration.hostAndPort, configuration.ssl)
val connection = rpcClient.start(rpcOpsClass, username!!, credential!!)
connection
}
}
}

View File

@ -1,20 +0,0 @@
package net.corda.tools.shell
import net.corda.core.internal.messaging.InternalCordaRPCOps
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Proxy
fun makeRPCOps(getCordaRPCOps: (username: String, credential: String) -> InternalCordaRPCOps, username: String, credential: String): InternalCordaRPCOps {
val cordaRPCOps: InternalCordaRPCOps by lazy {
getCordaRPCOps(username, credential)
}
return Proxy.newProxyInstance(InternalCordaRPCOps::class.java.classLoader, arrayOf(InternalCordaRPCOps::class.java)) { _, method, args ->
try {
method.invoke(cordaRPCOps, *(args ?: arrayOf()))
} catch (e: InvocationTargetException) {
// Unpack exception.
throw e.targetException
}
} as InternalCordaRPCOps
}

View File

@ -18,7 +18,7 @@ import net.corda.core.flows.StateMachineRunId
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.openFuture
import net.corda.core.internal.messaging.InternalCordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.FlowProgressHandleImpl import net.corda.core.messaging.FlowProgressHandleImpl
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
@ -42,7 +42,7 @@ import kotlin.test.assertFailsWith
class InteractiveShellTest { class InteractiveShellTest {
lateinit var inputObjectMapper: ObjectMapper lateinit var inputObjectMapper: ObjectMapper
lateinit var cordaRpcOps: InternalCordaRPCOps lateinit var cordaRpcOps: CordaRPCOps
lateinit var invocationContext: InvocationContext<Map<Any, Any>> lateinit var invocationContext: InvocationContext<Map<Any, Any>>
lateinit var printWriter: RenderPrintWriter lateinit var printWriter: RenderPrintWriter