Skip to content

Commit

Permalink
feat(corda-connector): node diagnostics endpoint hyperledger-cacti#623
Browse files Browse the repository at this point in the history
The Corda RPC proxy has a diagnostics method that we
exposed here as an endpoint for our own connector as well.
This will help tremendously in debugging things when
people are working with the corda connector
(speaking from experience here).

Fixes hyperledger-cacti#623

Signed-off-by: Peter Somogyvari <peter.somogyvari@accenture.com>
  • Loading branch information
petermetz committed Mar 10, 2021
1 parent 2bb0d74 commit 83d472b
Show file tree
Hide file tree
Showing 13 changed files with 755 additions and 12 deletions.
88 changes: 88 additions & 0 deletions packages/cactus-plugin-ledger-connector-corda/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,92 @@ From the project root:

```sh
DOCKER_BUILDKIT=1 docker build ./packages/cactus-plugin-ledger-connector-corda/src/main-server/ -t cccs
```

## Example NodeDiagnosticInfo JSON Response

```json
{
"version": "4.6",
"revision": "85e387ea730d9be7d6dc2b23caba1ee18305af74",
"platformVersion": 8,
"vendor": "Corda Open Source",
"cordapps": [
{
"type": "Workflow CorDapp",
"name": "workflows-1.0",
"shortName": "Obligation Flows",
"minimumPlatformVersion": 8,
"targetPlatformVersion": 8,
"version": "1",
"vendor": "Corda Open Source",
"licence": "Apache License, Version 2.0",
"jarHash": {
"bytes": "Vf9MllnrC7vrWxrlDE94OzPMZW7At1HhTETL/XjiAmc=",
"offset": 0,
"size": 32
}
},
{
"type": "CorDapp",
"name": "corda-confidential-identities-4.6",
"shortName": "corda-confidential-identities-4.6",
"minimumPlatformVersion": 1,
"targetPlatformVersion": 1,
"version": "Unknown",
"vendor": "Unknown",
"licence": "Unknown",
"jarHash": {
"bytes": "nqBwqHJMbLW80hmRbKEYk0eAknFiX8N40LKuGsD0bPo=",
"offset": 0,
"size": 32
}
},
{
"type": "Contract CorDapp",
"name": "corda-finance-contracts-4.6",
"shortName": "Corda Finance Demo",
"minimumPlatformVersion": 1,
"targetPlatformVersion": 8,
"version": "1",
"vendor": "R3",
"licence": "Open Source (Apache 2)",
"jarHash": {
"bytes": "a43Q/GJG6JKTZzq3U80P8L1DWWcB/D+Pl5uitEtAeQQ=",
"offset": 0,
"size": 32
}
},
{
"type": "Workflow CorDapp",
"name": "corda-finance-workflows-4.6",
"shortName": "Corda Finance Demo",
"minimumPlatformVersion": 1,
"targetPlatformVersion": 8,
"version": "1",
"vendor": "R3",
"licence": "Open Source (Apache 2)",
"jarHash": {
"bytes": "wXdD4Iy50RaWzPp7n9s1xwf4K4MB8eA1nmhPquTMvxg=",
"offset": 0,
"size": 32
}
},
{
"type": "Contract CorDapp",
"name": "contracts-1.0",
"shortName": "Obligation Contracts",
"minimumPlatformVersion": 8,
"targetPlatformVersion": 8,
"version": "1",
"vendor": "Corda Open Source",
"licence": "Apache License, Version 2.0",
"jarHash": {
"bytes": "grTZzN71Cpxw6rZe/U5SB6/ehl99B6VQ1+ZJEx1rixs=",
"offset": 0,
"size": 32
}
}
]
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/api/
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/api/ApiPluginLedgerConnectorCordaService.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/api/ApiUtil.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/CordaX500Name.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/CordappInfo.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/DeployContractJarsBadRequestV1Response.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/DeployContractJarsSuccessV1Response.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/DeployContractJarsV1Request.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/DiagnoseNodeV1Request.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/DiagnoseNodeV1Response.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/FlowInvocationType.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/InvokeContractV1Request.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/InvokeContractV1Response.kt
Expand All @@ -18,8 +21,10 @@ src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/mode
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/ListFlowsV1Request.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/ListFlowsV1Response.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/NetworkHostAndPort.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/NodeDiagnosticInfo.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/NodeInfo.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/Party.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/PublicKey.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/SHA256.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/model/X500Principal.kt
src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package org.hyperledger.cactus.plugin.ledger.connector.corda.server.api
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.DeployContractJarsBadRequestV1Response
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.DeployContractJarsSuccessV1Response
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.DeployContractJarsV1Request
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.DiagnoseNodeV1Request
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.DiagnoseNodeV1Response
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.InvokeContractV1Request
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.InvokeContractV1Response
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.ListFlowsV1Request
Expand Down Expand Up @@ -46,6 +48,17 @@ class ApiPluginLedgerConnectorCordaController(@Autowired(required = true) val se
}


@PostMapping(
value = ["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-corda/diagnose-node"],
produces = ["application/json"],
consumes = ["application/json"]
)
fun diagnoseNodeV1( @Valid @RequestBody(required = false) diagnoseNodeV1Request: DiagnoseNodeV1Request?
): ResponseEntity<DiagnoseNodeV1Response> {
return ResponseEntity(service.diagnoseNodeV1(diagnoseNodeV1Request), HttpStatus.valueOf(200))
}


@PostMapping(
value = ["/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-corda/invoke-contract"],
produces = ["application/json"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package org.hyperledger.cactus.plugin.ledger.connector.corda.server.api
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.DeployContractJarsBadRequestV1Response
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.DeployContractJarsSuccessV1Response
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.DeployContractJarsV1Request
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.DiagnoseNodeV1Request
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.DiagnoseNodeV1Response
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.InvokeContractV1Request
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.InvokeContractV1Response
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.ListFlowsV1Request
Expand All @@ -12,6 +14,8 @@ interface ApiPluginLedgerConnectorCordaService {

fun deployContractJarsV1(deployContractJarsV1Request: DeployContractJarsV1Request?): DeployContractJarsSuccessV1Response

fun diagnoseNodeV1(diagnoseNodeV1Request: DiagnoseNodeV1Request?): DiagnoseNodeV1Response

fun invokeContractV1(invokeContractV1Request: InvokeContractV1Request?): InvokeContractV1Response

fun listFlowsV1(listFlowsV1Request: ListFlowsV1Request?): ListFlowsV1Response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ package org.hyperledger.cactus.plugin.ledger.connector.corda.server.impl
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.ObjectWriter
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import org.springframework.stereotype.Service
import net.corda.core.flows.FlowLogic
import net.corda.core.messaging.CordaRPCOps;
import net.corda.core.messaging.FlowProgressHandle
import net.corda.core.node.NodeDiagnosticInfo
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.loggerFor
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.api.ApiPluginLedgerConnectorCordaService
Expand All @@ -30,6 +31,14 @@ class ApiPluginLedgerConnectorCordaServiceImpl(

companion object {

// FIXME: do not recreate the mapper for every service implementation instance that we create...
val mapper: ObjectMapper = jacksonObjectMapper()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES)
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)

val writer: ObjectWriter = mapper.writer()

val jcl: JarClassLoader = JarClassLoader(ApiPluginLedgerConnectorCordaServiceImpl::class.java.classLoader)

val logger = loggerFor<ApiPluginLedgerConnectorCordaServiceImpl>()
Expand Down Expand Up @@ -191,6 +200,19 @@ class ApiPluginLedgerConnectorCordaServiceImpl(
}
}

override fun diagnoseNodeV1(diagnoseNodeV1Request: DiagnoseNodeV1Request?): DiagnoseNodeV1Response {
val reader = mapper.readerFor(object : TypeReference<org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.NodeDiagnosticInfo?>() {})

val nodeDiagnosticInfoCorda = rpc.proxy.nodeDiagnosticInfo()

val json = writer.writeValueAsString(nodeDiagnosticInfoCorda)
logger.debug("NodeDiagnosticInfo JSON=\n{}", json)

val nodeDiagnosticInfoCactus = reader.readValue<org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.NodeDiagnosticInfo>(json)
logger.debug("Responding with marshalled org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.NodeDiagnosticInfo: {}", nodeDiagnosticInfoCactus)
return DiagnoseNodeV1Response(nodeDiagnosticInfo = nodeDiagnosticInfoCactus)
}

override fun invokeContractV1(invokeContractV1Request: InvokeContractV1Request?): InvokeContractV1Response {
Objects.requireNonNull(invokeContractV1Request, "InvokeContractV1Request must be non-null!")
return dynamicInvoke(rpc.proxy, invokeContractV1Request!!)
Expand All @@ -202,20 +224,11 @@ class ApiPluginLedgerConnectorCordaServiceImpl(
}

override fun networkMapV1(body: Any?): List<NodeInfo> {

// FIXME: do not recreate the mapper for every request that we receive...
val mapper = jacksonObjectMapper()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES)
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)

val writer = mapper.writer()
val reader = mapper.readerFor(object : TypeReference<List<NodeInfo?>?>() {})


val networkMapSnapshot = rpc.proxy.networkMapSnapshot()
val networkMapJson = writer.writeValueAsString(networkMapSnapshot)
logger.debug("networkMapSnapshot=\n{}", networkMapJson)
logger.trace("networkMapSnapshot=\n{}", networkMapJson)

val nodeInfoList = reader.readValue<List<NodeInfo>>(networkMapJson)
logger.info("Returning {} NodeInfo elements in response.", nodeInfoList.size)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.hyperledger.cactus.plugin.ledger.connector.corda.server.model

import java.util.Objects
import com.fasterxml.jackson.annotation.JsonProperty
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.SHA256
import javax.validation.constraints.DecimalMax
import javax.validation.constraints.DecimalMin
import javax.validation.constraints.Max
import javax.validation.constraints.Min
import javax.validation.constraints.NotNull
import javax.validation.constraints.Pattern
import javax.validation.constraints.Size
import javax.validation.Valid

/**
* A CordappInfo describes a single CorDapp currently installed on the node
* @param jarHash
* @param licence The name of the licence this CorDapp is released under
* @param minimumPlatformVersion The minimum platform version the node must be at for the CorDapp to run
* @param name The name of the JAR file that defines the CorDapp
* @param shortName The name of the CorDapp
* @param targetPlatformVersion The target platform version this CorDapp has been tested against
* @param type A description of what sort of CorDapp this is - either a contract, workflow, or a combination.
* @param vendor The vendor of this CorDapp
* @param version The version of this CorDapp
*/
data class CordappInfo(

@get:NotNull
@field:Valid
@field:JsonProperty("jarHash") val jarHash: SHA256,

@get:NotNull
@field:JsonProperty("licence") val licence: kotlin.String,

@get:NotNull
@field:JsonProperty("minimumPlatformVersion") val minimumPlatformVersion: kotlin.Int,

@get:NotNull
@field:JsonProperty("name") val name: kotlin.String,

@get:NotNull
@field:JsonProperty("shortName") val shortName: kotlin.String,

@get:NotNull
@field:JsonProperty("targetPlatformVersion") val targetPlatformVersion: kotlin.Int,

@get:NotNull
@field:JsonProperty("type") val type: kotlin.String,

@get:NotNull
@field:JsonProperty("vendor") val vendor: kotlin.String,

@get:NotNull
@field:JsonProperty("version") val version: kotlin.String
) {

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.hyperledger.cactus.plugin.ledger.connector.corda.server.model

import java.util.Objects
import com.fasterxml.jackson.annotation.JsonProperty
import javax.validation.constraints.DecimalMax
import javax.validation.constraints.DecimalMin
import javax.validation.constraints.Max
import javax.validation.constraints.Min
import javax.validation.constraints.NotNull
import javax.validation.constraints.Pattern
import javax.validation.constraints.Size
import javax.validation.Valid

/**
*
* @param nodeIds Optional property specifying which Corda Node should be the one being diagnosed in case the Connector has multiple connections established for different nodes (which is not yet a supported feature, but we want to keep this possibility open for the future).
*/
data class DiagnoseNodeV1Request(

@get:Size(min=0,max=1024)
@field:JsonProperty("nodeIds") val nodeIds: kotlin.collections.List<kotlin.String>? = null
) {

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.hyperledger.cactus.plugin.ledger.connector.corda.server.model

import java.util.Objects
import com.fasterxml.jackson.annotation.JsonProperty
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.NodeDiagnosticInfo
import javax.validation.constraints.DecimalMax
import javax.validation.constraints.DecimalMin
import javax.validation.constraints.Max
import javax.validation.constraints.Min
import javax.validation.constraints.NotNull
import javax.validation.constraints.Pattern
import javax.validation.constraints.Size
import javax.validation.Valid

/**
*
* @param nodeDiagnosticInfo
*/
data class DiagnoseNodeV1Response(

@get:NotNull
@field:Valid
@field:JsonProperty("nodeDiagnosticInfo") val nodeDiagnosticInfo: NodeDiagnosticInfo
) {

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.hyperledger.cactus.plugin.ledger.connector.corda.server.model

import java.util.Objects
import com.fasterxml.jackson.annotation.JsonProperty
import org.hyperledger.cactus.plugin.ledger.connector.corda.server.model.CordappInfo
import javax.validation.constraints.DecimalMax
import javax.validation.constraints.DecimalMin
import javax.validation.constraints.Max
import javax.validation.constraints.Min
import javax.validation.constraints.NotNull
import javax.validation.constraints.Pattern
import javax.validation.constraints.Size
import javax.validation.Valid

/**
* A NodeDiagnosticInfo holds information about the current node version.
* @param cordapps A list of CorDapps currently installed on this node
* @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 revision The git commit hash this node was built from
* @param vendor The vendor of this node
* @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).
*/
data class NodeDiagnosticInfo(

@get:NotNull
@field:Valid
@get:Size(min=0,max=4096)
@field:JsonProperty("cordapps") val cordapps: kotlin.collections.List<CordappInfo>,

@get:NotNull
@field:JsonProperty("platformVersion") val platformVersion: kotlin.Int,

@get:NotNull
@field:JsonProperty("revision") val revision: kotlin.String,

@get:NotNull
@field:JsonProperty("vendor") val vendor: kotlin.String,

@get:NotNull
@field:JsonProperty("version") val version: kotlin.String
) {

}

Loading

0 comments on commit 83d472b

Please sign in to comment.