[CORDA-3436] Allow CorDapps access to node diagnostic information (#5834)

* [CORDA-3436] Allow CorDapps access to node diagnostic information

* [CORDA-3436] Fix API breakages

* [CORDA-3436] Improve documentation around diagnostics service

* [CORDA-3436] Remove CorDapps from the diagnostics information

* [CORDA-3436] Silence detekt warning
This commit is contained in:
James Higgs 2020-01-09 11:18:32 +00:00 committed by Anthony Keenan
parent e0eac8fa0d
commit d0543d7270
11 changed files with 149 additions and 22 deletions

View File

@ -3,6 +3,19 @@ package net.corda.core.cordapp
import net.corda.core.crypto.SecureHash
import net.corda.core.serialization.CordaSerializable
/**
* A [CordappInfo] describes a single CorDapp currently installed on the node
*
* @param type A description of what sort of CorDapp this is - either a contract, workflow, or a combination.
* @param name The name of the JAR file that defines the CorDapp
* @param shortName The name of the CorDapp
* @param minimumPlatformVersion The minimum platform version the node must be at for the CorDapp to run
* @param targetPlatformVersion The target platform version this CorDapp has been tested against
* @param version The version of this CorDapp
* @param vendor The vendor of this CorDapp
* @param licence The name of the licence this CorDapp is released under
* @param jarHash The hash of the JAR file that defines this CorDapp
*/
@CordaSerializable
data class CordappInfo(val type: String,
val name: String,

View File

@ -3,6 +3,17 @@ package net.corda.core.node
import net.corda.core.cordapp.CordappInfo
import net.corda.core.serialization.CordaSerializable
/**
* A [NodeDiagnosticInfo] holds information about the current node version.
* @param version The current node version string, e.g. 4.3, 4.4-SNAPSHOT. Note that this string is effectively freeform, and so should only
* be used for providing diagnostic information. It should not be used to make functionality decisions (the platformVersion
* is a better fit for this).
* @param revision The git commit hash this node was built from
* @param platformVersion The platform version of this node. This number represents a released API version, and should be used to make
* functionality decisions (e.g. enabling an app feature only if an underlying platform feature exists)
* @param vendor The vendor of this node
* @param cordapps A list of CorDapps currently installed on this node
*/
@CordaSerializable
data class NodeDiagnosticInfo(val version: String,
val revision: String,

View File

@ -11,6 +11,7 @@ import net.corda.core.crypto.SignatureMetadata
import net.corda.core.crypto.TransactionSignature
import net.corda.core.flows.ContractUpgradeFlow
import net.corda.core.node.services.*
import net.corda.core.node.services.diagnostics.DiagnosticsService
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.transactions.FilteredTransaction
import net.corda.core.transactions.LedgerTransaction
@ -157,6 +158,13 @@ interface ServiceHub : ServicesForResolution {
*/
val networkMapCache: NetworkMapCache
/**
* The [DiagnosticsService] provides diagnostic level information about the node, including the current version of the node, and the
* CorDapps currently installed on the node. Note that this information should be used to provide diagnostics only, and no functional
* decisions should be made based on this.
*/
val diagnosticsService: DiagnosticsService
/**
* INTERNAL. DO NOT USE.
* @suppress

View File

@ -0,0 +1,17 @@
package net.corda.core.node.services.diagnostics
import net.corda.core.DoNotImplement
/**
* A [DiagnosticsService] provides APIs that allow CorDapps to query information about the node that CorDapp is currently running on.
*/
@DoNotImplement
interface DiagnosticsService {
/**
* Retrieve information about the current node version.
*
* @return [NodeVersionInfo] A structure holding information about the current node version.
*/
fun nodeVersionInfo() : NodeVersionInfo
}

View File

@ -0,0 +1,15 @@
package net.corda.core.node.services.diagnostics
/**
* Version info about the node. Note that this data should be used for diagnostics purposes only - it is unsafe to rely on this for
* functional decisions.
*
* @param releaseVersion The release version string of the node, e.g. 4.3, 4.4-SNAPSHOT.
* @param revision The git commit hash this node was built from
* @param platformVersion The platform version of this node, representing the released API version.
* @param vendor The vendor of this node
*/
data class NodeVersionInfo(val releaseVersion: String,
val revision: String,
val platformVersion: Int,
val vendor: String)

View File

@ -19,6 +19,11 @@ various services the node provides. The services offered by the ``ServiceHub`` a
* Other information about the node
* ``ServiceHub.clock``
* Provides access to the nodes internal time and date
* ``ServiceHub.diagnosticsService``
* Provides diagnostic information about the node, including the node version and currently running apps. Note that this data should be
used for diagnostic purposes ONLY
* ``ServiceHub.contractUpgradeService``
* Provides functionality for secure contract upgrades
Additional, ``ServiceHub`` exposes the following properties:

View File

@ -53,6 +53,7 @@ import net.corda.core.node.ServiceHub
import net.corda.core.node.ServicesForResolution
import net.corda.core.node.services.ContractUpgradeService
import net.corda.core.node.services.CordaService
import net.corda.core.node.services.diagnostics.DiagnosticsService
import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.KeyManagementService
import net.corda.core.node.services.TransactionVerifierService
@ -99,6 +100,7 @@ import net.corda.node.services.config.rpc.NodeRpcOptions
import net.corda.node.services.config.shell.determineUnsafeUsers
import net.corda.node.services.config.shell.toShellConfig
import net.corda.node.services.config.shouldInitCrashShell
import net.corda.node.services.diagnostics.NodeDiagnosticsService
import net.corda.node.services.events.NodeSchedulerService
import net.corda.node.services.events.ScheduledActivityObserver
import net.corda.node.services.identity.PersistentIdentityService
@ -269,6 +271,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
@Suppress("LeakingThis")
val networkParametersStorage = makeNetworkParametersStorage()
val cordappProvider = CordappProviderImpl(cordappLoader, CordappConfigFileProvider(configuration.cordappDirectories), attachments).tokenize()
val diagnosticsService = NodeDiagnosticsService().tokenize()
val pkToIdCache = PublicKeyToOwningIdentityCacheImpl(database, cacheFactory)
@Suppress("LeakingThis")
val keyManagementService = makeKeyManagementService(identityService).tokenize()
@ -1125,6 +1128,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
override val cacheFactory: NamedCacheFactory get() = this@AbstractNode.cacheFactory
override val networkParametersService: NetworkParametersStorage get() = this@AbstractNode.networkParametersStorage
override val attachmentTrustCalculator: AttachmentTrustCalculator get() = this@AbstractNode.attachmentTrustCalculator
override val diagnosticsService: DiagnosticsService get() = this@AbstractNode.diagnosticsService
private lateinit var _myInfo: NodeInfo
override val myInfo: NodeInfo get() = _myInfo

View File

@ -194,29 +194,31 @@ internal class CordaRPCOpsImpl(
}
override fun nodeDiagnosticInfo(): NodeDiagnosticInfo {
val versionInfo = services.diagnosticsService.nodeVersionInfo()
val cordapps = services.cordappProvider.cordapps
.filter { !it.jarPath.toString().endsWith("corda-core-${CordaVersion.releaseVersion}.jar") }
.map {
CordappInfo(
type = when (it.info) {
is Cordapp.Info.Contract -> "Contract CorDapp"
is Cordapp.Info.Workflow -> "Workflow CorDapp"
else -> "CorDapp"
},
name = it.name,
shortName = it.info.shortName,
minimumPlatformVersion = it.minimumPlatformVersion,
targetPlatformVersion = it.targetPlatformVersion,
version = it.info.version,
vendor = it.info.vendor,
licence = it.info.licence,
jarHash = it.jarHash)
}
return NodeDiagnosticInfo(
version = CordaVersion.releaseVersion,
revision = CordaVersion.revision,
platformVersion = CordaVersion.platformVersion,
vendor = CordaVersion.vendor,
cordapps = services.cordappProvider.cordapps
.filter { !it.jarPath.toString().endsWith("corda-core-${CordaVersion.releaseVersion}.jar") }
.map {
CordappInfo(
type = when (it.info) {
is Cordapp.Info.Contract -> "Contract CorDapp"
is Cordapp.Info.Workflow -> "Workflow CorDapp"
else -> "CorDapp"
},
name = it.name,
shortName = it.info.shortName,
minimumPlatformVersion = it.minimumPlatformVersion,
targetPlatformVersion = it.targetPlatformVersion,
version = it.info.version,
vendor = it.info.vendor,
licence = it.info.licence,
jarHash = it.jarHash)
}
version = versionInfo.releaseVersion,
revision = versionInfo.revision,
platformVersion = versionInfo.platformVersion,
vendor = versionInfo.vendor,
cordapps = cordapps
)
}

View File

@ -0,0 +1,22 @@
package net.corda.node.services.diagnostics
import net.corda.common.logging.CordaVersion
import net.corda.core.cordapp.Cordapp
import net.corda.core.cordapp.CordappInfo
import net.corda.core.node.NodeDiagnosticInfo
import net.corda.core.node.services.diagnostics.DiagnosticsService
import net.corda.core.node.services.diagnostics.NodeVersionInfo
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.node.internal.cordapp.CordappProviderInternal
class NodeDiagnosticsService : DiagnosticsService, SingletonSerializeAsToken() {
override fun nodeVersionInfo(): NodeVersionInfo {
return NodeVersionInfo(
releaseVersion = CordaVersion.releaseVersion,
revision = CordaVersion.revision,
platformVersion = CordaVersion.platformVersion,
vendor = CordaVersion.vendor
)
}
}

View File

@ -0,0 +1,27 @@
package net.corda.node.services.diagnostics
import net.corda.common.logging.CordaVersion
import net.corda.core.contracts.ContractClassName
import net.corda.core.cordapp.Cordapp
import net.corda.core.cordapp.CordappContext
import net.corda.core.cordapp.CordappInfo
import net.corda.core.flows.FlowLogic
import net.corda.core.internal.cordapp.CordappImpl
import net.corda.core.node.services.AttachmentId
import net.corda.node.internal.cordapp.CordappProviderInternal
import org.junit.Test
import kotlin.test.assertEquals
class NodeDiagnosticsServiceTest {
private val diagnosticsService = NodeDiagnosticsService()
@Test
fun `platform version info correctly returned from diagnostics service`() {
val info = diagnosticsService.nodeVersionInfo()
assertEquals(CordaVersion.releaseVersion, info.releaseVersion)
assertEquals(CordaVersion.revision, info.revision)
assertEquals(CordaVersion.platformVersion, info.platformVersion)
assertEquals(CordaVersion.vendor, info.vendor)
}
}

View File

@ -18,6 +18,7 @@ import net.corda.core.messaging.FlowProgressHandle
import net.corda.core.messaging.StateMachineTransactionMapping
import net.corda.core.node.*
import net.corda.core.node.services.*
import net.corda.core.node.services.diagnostics.DiagnosticsService
import net.corda.core.node.services.vault.CordaTransactionSupport
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.transactions.SignedTransaction
@ -26,6 +27,7 @@ import net.corda.node.VersionInfo
import net.corda.node.internal.ServicesForResolutionImpl
import net.corda.node.internal.cordapp.JarScanningCordappLoader
import net.corda.node.services.api.*
import net.corda.node.services.diagnostics.NodeDiagnosticsService
import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.node.services.identity.PersistentIdentityService
import net.corda.node.services.keys.BasicHSMKeyManagementService
@ -429,6 +431,7 @@ open class MockServices private constructor(
}
override val cordappProvider: CordappProvider get() = mockCordappProvider
override var networkParametersService: NetworkParametersService = MockNetworkParametersStorage(initialNetworkParameters)
override val diagnosticsService: DiagnosticsService = NodeDiagnosticsService()
protected val servicesForResolution: ServicesForResolution
get() = ServicesForResolutionImpl(identityService, attachments, cordappProvider, networkParametersService, validatedTransactions)