ported rc02 updates from kotlin
This commit is contained in:
parent
d64a21107d
commit
7d65a63f8d
@ -3,7 +3,7 @@
|
||||
## Note: This cut of CSDE is work in progress and has not been released yet, hence may not function as expected.
|
||||
|
||||
|
||||
To help make the process of prototyping Cordapps on Developer Preview 2 more straight forward we have developed the Cordapp Standard Development Environment (CSDE).
|
||||
To help make the process of prototyping Cordapps on Corda 5 beta releases more straight forward we have developed the Cordapp Standard Development Environment (CSDE).
|
||||
|
||||
The CSDE is obtained by cloning this CSDE-Cordapp-Template-java to your local machine. The CSDE provides:
|
||||
|
||||
@ -22,3 +22,5 @@ The CSDE is obtained by cloning this CSDE-Cordapp-Template-java to your local ma
|
||||
Note, the CSDE is experimental, we may or may not release it as part of Corda 5.0, in part based on developer feedback using it.
|
||||
|
||||
To find out how to use the CSDE please refer to the getting started section in the Corda 5 Developer Preview 2 documentation at https://docs.r3.com/
|
||||
|
||||
(Note, to use the CSDE you must have installed the Corda CLI, make sure the version matches the version of Corda)
|
@ -26,6 +26,9 @@ allprojects {
|
||||
repositories {
|
||||
// All dependencies are held in Maven Central
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = "$artifactoryContextUrl/"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(Test).configureEach {
|
||||
|
@ -4,8 +4,16 @@ plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
def constants = new Properties()
|
||||
file("$rootDir/../gradle.properties").withInputStream { InputStream input -> constants.load(input) }
|
||||
def artifactoryContextUrl = constants.getProperty('artifactoryContextUrl')
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
maven {
|
||||
url = "$artifactoryContextUrl/"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -1,4 +1,4 @@
|
||||
jacksonVersion = 2.13.4
|
||||
unirestVersion=3.13.10
|
||||
|
||||
cordaApiVersion=5.0.0.524-Fox1.1
|
||||
cordaApiVersion=5.0.0.665-Gecko-RC02
|
||||
|
@ -19,7 +19,8 @@ import com.r3.csde.DeployCPIsHelper
|
||||
import com.r3.csde.BuildCPIsHelper
|
||||
import com.r3.csde.ProjectUtils
|
||||
import com.r3.csde.CordaStatusQueries
|
||||
import com.r3.csde.CreateAndRegisterVNodesHelper
|
||||
import com.r3.csde.VNodesHelper
|
||||
import com.r3.csde.NetworkConfig
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
@ -113,6 +114,8 @@ def projectContext = new ProjectContext(project,
|
||||
cordaNotaryPluginsVersion
|
||||
)
|
||||
|
||||
def networkConfig = new NetworkConfig("config/static-network-config.json")
|
||||
|
||||
def utils = new ProjectUtils()
|
||||
|
||||
// Initiate workspace folder
|
||||
@ -183,7 +186,7 @@ tasks.register('listCPIs') {
|
||||
|
||||
// Build CPI tasks
|
||||
|
||||
def buildCPIsHelper = new BuildCPIsHelper(projectContext)
|
||||
def buildCPIsHelper = new BuildCPIsHelper(projectContext, networkConfig)
|
||||
|
||||
tasks.register("1-createGroupPolicy") {
|
||||
group = cordappGroup
|
||||
@ -224,7 +227,7 @@ tasks.register('3-buildCPIs') {
|
||||
|
||||
def deployCPIsHelper = new DeployCPIsHelper(projectContext)
|
||||
|
||||
tasks.register("4-deployCPIs") {
|
||||
tasks.register('4-deployCPIs') {
|
||||
group = cordappGroup
|
||||
dependsOn('3-buildCPIs')
|
||||
doLast {
|
||||
@ -232,23 +235,14 @@ tasks.register("4-deployCPIs") {
|
||||
}
|
||||
}
|
||||
|
||||
// create and register Vnodes Tasks
|
||||
// Setup VNodes tasks
|
||||
|
||||
def createAndRegisterVNodesHelper = new CreateAndRegisterVNodesHelper(projectContext)
|
||||
def vNodesHelper = new VNodesHelper(projectContext, networkConfig )
|
||||
|
||||
tasks.register("5-createAndRegVNodes") {
|
||||
tasks.register('5-vNodeSetup') {
|
||||
group = cordappGroup
|
||||
dependsOn('4-deployCPIs')
|
||||
doLast {
|
||||
createAndRegisterVNodesHelper.createAndRegVNodes()
|
||||
vNodesHelper.vNodesSetup()
|
||||
}
|
||||
}
|
||||
|
||||
// Empty task, just acts as the Task user entry point task.
|
||||
tasks.register('quickDeployCordapp') {
|
||||
group = cordappGroup
|
||||
dependsOn("5-createAndRegVNodes")
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -2,26 +2,32 @@ package com.r3.csde;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
// todo: This class needs refactoring, see https://r3-cev.atlassian.net/browse/CORE-11624
|
||||
public class BuildCPIsHelper {
|
||||
|
||||
public ProjectContext pc;
|
||||
public ProjectUtils utils;
|
||||
public BuildCPIsHelper(ProjectContext _pc) {
|
||||
|
||||
public NetworkConfig config;
|
||||
public BuildCPIsHelper(ProjectContext _pc, NetworkConfig _config) {
|
||||
pc = _pc;
|
||||
utils = new ProjectUtils(pc);
|
||||
config = _config;
|
||||
}
|
||||
|
||||
public void createGroupPolicy() throws IOException {
|
||||
|
||||
File groupPolicyFile = new File(String.format("%s/GroupPolicy.json", pc.devEnvWorkspace));
|
||||
File devnetFile = new File(String.format("%s/config/dev-net.json", pc.project.getRootDir()));
|
||||
File devnetFile = new File(pc.project.getRootDir() + "/" + config.getConfigFilePath());
|
||||
|
||||
|
||||
if (!groupPolicyFile.exists() || groupPolicyFile.lastModified() < devnetFile.lastModified()) {
|
||||
|
||||
pc.out.println("createGroupPolicy: Creating a GroupPolicy");
|
||||
|
||||
LinkedList<String> configX500Ids = utils.getConfigX500Ids(pc.X500ConfigFile);
|
||||
List<String> configX500Ids = config.getX500Names();
|
||||
LinkedList<String> commandList = new LinkedList<>();
|
||||
|
||||
commandList.add(String.format("%s/java", pc.javaBinDir));
|
||||
@ -192,6 +198,8 @@ public class BuildCPIsHelper {
|
||||
commandList.add("--key");
|
||||
commandList.add("my-signing-key"); // todo: should be passed as context property
|
||||
|
||||
|
||||
|
||||
ProcessBuilder pb = new ProcessBuilder(commandList);
|
||||
pb.redirectErrorStream(true);
|
||||
Process proc = pb.start();
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.r3.csde;
|
||||
|
||||
import kong.unirest.Unirest;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -9,7 +11,7 @@ import java.util.Scanner;
|
||||
/**
|
||||
* Manages Bringing corda up, testing for liveness and taking corda down
|
||||
*/
|
||||
|
||||
// todo: This class needs refactoring, see https://r3-cev.atlassian.net/browse/CORE-11624
|
||||
public class CordaLifeCycleHelper {
|
||||
|
||||
ProjectContext pc;
|
||||
@ -18,15 +20,16 @@ public class CordaLifeCycleHelper {
|
||||
public CordaLifeCycleHelper(ProjectContext _pc) {
|
||||
pc = _pc;
|
||||
utils = new ProjectUtils(pc);
|
||||
Unirest.config().verifySsl(false);
|
||||
}
|
||||
|
||||
|
||||
public void startCorda() throws IOException {
|
||||
PrintStream pidStore = new PrintStream(new FileOutputStream(pc.cordaPidCache));
|
||||
File combinedWorkerJar = pc.project.getConfigurations().getByName("combinedWorker").getSingleFile();
|
||||
|
||||
// Manual version of the command to start postgres (for reference):
|
||||
// docker run -d --rm -p5432:5432 --name CSDEpostgresql -e POSTGRES_DB=cordacluster -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=password postgres:latest
|
||||
|
||||
// todo: make consistent with other ProcessBuilder set ups (use cmdArray)
|
||||
new ProcessBuilder(
|
||||
"docker",
|
||||
"run", "-d", "--rm",
|
||||
@ -36,37 +39,33 @@ public class CordaLifeCycleHelper {
|
||||
"-e", "POSTGRES_USER=postgres",
|
||||
"-e", "POSTGRES_PASSWORD=password",
|
||||
"postgres:latest").start();
|
||||
// todo: is there a better way of doing this - ie poll for readiness
|
||||
|
||||
// todo: we should poll for readiness not wait 10 seconds, see https://r3-cev.atlassian.net/browse/CORE-11626
|
||||
utils.rpcWait(10000);
|
||||
|
||||
ProcessBuilder procBuild = new ProcessBuilder(pc.javaBinDir + "/java",
|
||||
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005",
|
||||
"-DsecurityMangerEnabled=false",
|
||||
"-Dlog4j.configurationFile=" + pc.project.getRootDir() + "/config/log4j2.xml",
|
||||
"-Dco.paralleluniverse.fibers.verifyInstrumentation=true",
|
||||
"-jar",
|
||||
combinedWorkerJar.toString(),
|
||||
"--instanceId=0",
|
||||
"--instance-id=0",
|
||||
"-mbus.busType=DATABASE",
|
||||
"-spassphrase=password",
|
||||
"-ssalt=salt",
|
||||
"-spassphrase=password",
|
||||
"-ssalt=salt",
|
||||
"-ddatabase.user=user",
|
||||
"-ddatabase.pass=password",
|
||||
"-ddatabase.jdbc.url=jdbc:postgresql://localhost:5432/cordacluster",
|
||||
"-ddatabase.jdbc.directory="+pc.JDBCDir);
|
||||
|
||||
|
||||
procBuild.redirectErrorStream(true);
|
||||
Process proc = procBuild.start();
|
||||
pidStore.print(proc.pid());
|
||||
pc.out.println("Corda Process-id="+proc.pid());
|
||||
proc.getInputStream().transferTo(pc.out);
|
||||
|
||||
// todo: should poll for readiness before returning
|
||||
// Chris comment - We probably do not want to poll for readiness here.
|
||||
// The combined-worker takes serveral minutes to come up.
|
||||
// It might be better to warn the user of that and have the readiness detection and polling logic used in other tasks involved in creating v-nodes and deploying the CPI.
|
||||
// Matt comment - I'm not sure I agree, we need to investigate
|
||||
|
||||
// todo: we should poll for readiness before completing the startCorda task, see https://r3-cev.atlassian.net/browse/CORE-11625
|
||||
}
|
||||
|
||||
|
||||
|
@ -6,6 +6,7 @@ import kong.unirest.json.JSONArray;
|
||||
import kong.unirest.json.JSONObject;
|
||||
import kong.unirest.HttpResponse;
|
||||
|
||||
// todo: This class needs refactoring, see https://r3-cev.atlassian.net/browse/CORE-11624
|
||||
public class CordaStatusQueries {
|
||||
|
||||
ProjectContext pc;
|
||||
|
@ -1,289 +0,0 @@
|
||||
package com.r3.csde;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import kong.unirest.HttpResponse;
|
||||
import kong.unirest.JsonNode;
|
||||
import kong.unirest.Unirest;
|
||||
import kong.unirest.json.JSONArray;
|
||||
import kong.unirest.json.JSONObject;
|
||||
import net.corda.v5.base.types.MemberX500Name;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static java.net.HttpURLConnection.*;
|
||||
|
||||
public class CreateAndRegisterVNodesHelper {
|
||||
|
||||
ProjectContext pc;
|
||||
ProjectUtils utils;
|
||||
CordaStatusQueries queries;
|
||||
|
||||
public CreateAndRegisterVNodesHelper(ProjectContext _pc) {
|
||||
pc = _pc;
|
||||
queries = new CordaStatusQueries(pc);
|
||||
utils = new ProjectUtils(pc);
|
||||
}
|
||||
|
||||
public void createAndRegVNodes() throws IOException, CsdeException, ConfigurationException {
|
||||
Unirest.config().verifySsl(false);
|
||||
String appCpiCheckSum = getLastCPIUploadChkSum( pc.CPIUploadStatusFName );
|
||||
String notaryCpiCheckSum = getLastCPIUploadChkSum( pc.CPIUploadStatusFName, "-NotaryServer" );
|
||||
|
||||
LinkedList<String> x500Ids = utils.getConfigX500Ids(pc.X500ConfigFile);
|
||||
|
||||
// For each identity check that it already exists.
|
||||
Set<MemberX500Name> existingX500 = new HashSet<>();
|
||||
HttpResponse<JsonNode> vnodeListResponse = queries.getVNodeInfo();
|
||||
|
||||
JSONArray virtualNodesJson = (JSONArray) vnodeListResponse.getBody().getObject().get("virtualNodes");
|
||||
for(Object o: virtualNodesJson){
|
||||
if(o instanceof JSONObject) {
|
||||
JSONObject idObj = ((JSONObject) o).getJSONObject("holdingIdentity");
|
||||
String x500id = (String) idObj.get("x500Name");
|
||||
existingX500.add(MemberX500Name.parse( x500id) );
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, CompletableFuture<HttpResponse<JsonNode>>> responses = new LinkedHashMap<>();
|
||||
|
||||
// Create the VNodes
|
||||
for(String x500id: x500Ids) {
|
||||
if(!existingX500.contains(MemberX500Name.parse(x500id) )) {
|
||||
String cpiCheckSum = getNotaryRepresentatives().containsKey(x500id) ? notaryCpiCheckSum : appCpiCheckSum;
|
||||
|
||||
pc.out.println("Creating VNode for x500id=\"" + x500id + "\" cpi checksum=" + cpiCheckSum);
|
||||
responses.put(x500id, Unirest
|
||||
.post(pc.baseURL + "/api/v1/virtualnode")
|
||||
.body("{ \"request\" : { \"cpiFileChecksum\": " + cpiCheckSum + ", \"x500Name\": \"" + x500id + "\" } }")
|
||||
.basicAuth(pc.rpcUser, pc.rpcPasswd)
|
||||
.asJsonAsync()
|
||||
);
|
||||
}
|
||||
else {
|
||||
pc.out.println("Not creating a vnode for \"" + x500id + "\", vnode already exists.");
|
||||
}
|
||||
}
|
||||
|
||||
pc.out.println("Waiting for VNode creation results...");
|
||||
|
||||
for (Map.Entry<String, CompletableFuture<HttpResponse<JsonNode>>> response: responses.entrySet()) {
|
||||
try {
|
||||
HttpResponse<JsonNode> jsonNode = response.getValue().get();
|
||||
// need to check this and report errors.
|
||||
// 200/HTTP_OK - OK
|
||||
// 409/HTTP_CONFLICT - Vnode already exists
|
||||
// 500/HTTP_INTERNAL_ERROR
|
||||
// - Can mean that the request timed out.
|
||||
// - However, the cluster may still have created the V-node successfully, so we want to poll later.
|
||||
pc.out.println("Vnode creation end point status:" + jsonNode.getStatus());
|
||||
switch(jsonNode.getStatus()) {
|
||||
case HTTP_OK: break;
|
||||
case HTTP_CONFLICT: break;
|
||||
case HTTP_INTERNAL_ERROR: break;
|
||||
default:
|
||||
utils.reportError(jsonNode);
|
||||
}
|
||||
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
throw new CsdeException("Unexpected exception while waiting for response to " +
|
||||
"membership submission for holding identity" + response.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> OKHoldingX500AndShortIds = pollForVNodeShortHoldingHashIds(x500Ids, 60, 5000);
|
||||
|
||||
// Register the VNodes
|
||||
responses.clear();
|
||||
|
||||
for(String okId: OKHoldingX500AndShortIds.keySet()) {
|
||||
responses.put(okId, Unirest
|
||||
.post(pc.baseURL + "/api/v1/membership/" + OKHoldingX500AndShortIds.get(okId))
|
||||
.body(getMemberRegistrationBody(okId))
|
||||
.basicAuth(pc.rpcUser, pc.rpcPasswd)
|
||||
.asJsonAsync( response ->
|
||||
pc.out.println("Vnode membership submission for \"" + okId + "\"" +
|
||||
System.lineSeparator() + response.getBody().toPrettyString()))
|
||||
);
|
||||
}
|
||||
|
||||
pc.out.println("Vnode membership requests submitted, waiting for acknowledgement from MGM...");
|
||||
|
||||
for (Map.Entry<String, CompletableFuture<HttpResponse<JsonNode>>> response: responses.entrySet()) {
|
||||
try {
|
||||
response.getValue().get();
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
throw new CsdeException("Unexpected exception while waiting for response to " +
|
||||
"membership submission for holding identity" + response.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
pollForCompleteMembershipRegistration(OKHoldingX500AndShortIds);
|
||||
}
|
||||
|
||||
public String getLastCPIUploadChkSum(@NotNull String CPIUploadStatusFName) throws IOException, NullPointerException {
|
||||
return getLastCPIUploadChkSum(CPIUploadStatusFName, "");
|
||||
}
|
||||
|
||||
public String getLastCPIUploadChkSum(@NotNull String CPIUploadStatusFName,
|
||||
String uploadStatusQualifier) throws IOException, NullPointerException {
|
||||
|
||||
String qualifiedCPIUploadStatusFName =
|
||||
CPIUploadStatusFName.replace(".json", uploadStatusQualifier + ".json");
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
FileInputStream in = new FileInputStream(qualifiedCPIUploadStatusFName);
|
||||
com.fasterxml.jackson.databind.JsonNode jsonNode = mapper.readTree(in);
|
||||
|
||||
String checksum = jsonNode.get("cpiFileChecksum").toString();
|
||||
if(checksum == null || checksum.equals("null")) {
|
||||
throw new NullPointerException("Missing cpiFileChecksum in file " +
|
||||
qualifiedCPIUploadStatusFName + " with contents:" + jsonNode);
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
// KV pairs of representative x500 name and corresponding notary service x500 name
|
||||
public Map<String, String> getNotaryRepresentatives() throws IOException, ConfigurationException {
|
||||
if (pc.notaryRepresentatives == null) {
|
||||
pc.notaryRepresentatives = new HashMap<>();
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
FileInputStream in = new FileInputStream(pc.X500ConfigFile);
|
||||
com.fasterxml.jackson.databind.JsonNode jsonNode = mapper.readTree(in);
|
||||
|
||||
List<String> identities = utils.getConfigX500Ids(pc.X500ConfigFile);
|
||||
|
||||
for (com.fasterxml.jackson.databind.JsonNode notary : jsonNode.get("notaries")) {
|
||||
|
||||
String svcX500Id = utils.jsonNodeToString(notary.get("serviceX500Name"));
|
||||
|
||||
com.fasterxml.jackson.databind.JsonNode repsForThisService = notary.get("representatives");
|
||||
|
||||
if (repsForThisService.isEmpty()) {
|
||||
throw new ConfigurationException(
|
||||
"Notary service \"" + svcX500Id + "\" must have at least one representative.");
|
||||
} else if (repsForThisService.size() > 1) {
|
||||
// Temporary restriction while the MGM only supports a 1-1 association
|
||||
throw new ConfigurationException(
|
||||
"Notary service \"" + svcX500Id + "\" can only have a single representative at this time.");
|
||||
}
|
||||
|
||||
for (com.fasterxml.jackson.databind.JsonNode representative : repsForThisService) {
|
||||
|
||||
String repAsString = utils.jsonNodeToString(representative);
|
||||
|
||||
if (identities.contains(repAsString)) {
|
||||
pc.notaryRepresentatives.put(repAsString, svcX500Id);
|
||||
} else {
|
||||
throw new ConfigurationException(
|
||||
"Notary representative \"" + repAsString + "\" is not a valid identity");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pc.notaryRepresentatives;
|
||||
}
|
||||
|
||||
private String getMemberRegistrationBody(String memberX500Name) throws ConfigurationException, IOException {
|
||||
Map<String, String> notaryReps = getNotaryRepresentatives();
|
||||
|
||||
String context = "\"corda.key.scheme\" : \"CORDA.ECDSA.SECP256R1\"" + (
|
||||
notaryReps.containsKey(memberX500Name)
|
||||
? ", \"corda.roles.0\" : \"notary\", " +
|
||||
"\"corda.notary.service.name\" : \"" + notaryReps.get(memberX500Name) + "\", " +
|
||||
// This will need revisiting in the long term when additional protocols are added, and will
|
||||
// need to be specified in config. We will also need to review the hard-coded name once
|
||||
// notary plugin selection logic is re-instated in CORE-7248.
|
||||
"\"corda.notary.service.plugin\" : \"net.corda.notary.NonValidatingNotary\""
|
||||
: ""
|
||||
);
|
||||
|
||||
return "{ \"memberRegistrationRequest\": { \"action\": \"requestJoin\", \"context\": { " + context + " } } }";
|
||||
}
|
||||
|
||||
|
||||
Map<String, String> pollForVNodeShortHoldingHashIds(List<String> x500Ids, int retryCount, int coolDownMs ) throws CsdeException {
|
||||
HashMap<String, String> x500NameToShortHashes = new HashMap<>();
|
||||
Set<String> vnodesToCheck = new HashSet<String>(x500Ids);
|
||||
while(!vnodesToCheck.isEmpty() && retryCount-- > 0) {
|
||||
utils.rpcWait(coolDownMs);
|
||||
kong.unirest.json.JSONArray virtualNodes = (JSONArray) queries.getVNodeInfo().getBody().getObject().get("virtualNodes");
|
||||
Map<String, String> vnodesMap = new HashMap<String, String>();
|
||||
for (Object virtualNode : virtualNodes) {
|
||||
if (virtualNode instanceof JSONObject) {
|
||||
JSONObject idObj = ((JSONObject) virtualNode).getJSONObject("holdingIdentity");
|
||||
vnodesMap.put(idObj.get("x500Name").toString(), idObj.get("shortHash").toString());
|
||||
}
|
||||
}
|
||||
for(String x500Name: vnodesToCheck) {
|
||||
if(vnodesMap.containsKey(x500Name)) {
|
||||
x500NameToShortHashes.put(x500Name, vnodesMap.get(x500Name));
|
||||
}
|
||||
}
|
||||
vnodesMap.keySet().forEach(vnodesToCheck::remove);
|
||||
}
|
||||
if(!vnodesToCheck.isEmpty()) {
|
||||
throw new CsdeException("VNode creation timed out. Not all expected vnodes were reported as created:" + vnodesToCheck.toString());
|
||||
}
|
||||
return x500NameToShortHashes;
|
||||
}
|
||||
|
||||
private void pollForCompleteMembershipRegistration(Map<String, String> X500ToShortIdHash) throws CsdeException {
|
||||
HashSet<String> vnodesToCheck = new HashSet<String>(X500ToShortIdHash.keySet());
|
||||
LinkedList<String> approved = new LinkedList<String>();
|
||||
while (!vnodesToCheck.isEmpty()) {
|
||||
utils.rpcWait(2000);
|
||||
approved.clear();
|
||||
for (String vnodeX500 : vnodesToCheck) {
|
||||
try {
|
||||
pc.out.println("Checking membership registration progress for v-node '" + vnodeX500 + "':");
|
||||
HttpResponse<JsonNode> statusResponse = Unirest
|
||||
.get(pc.baseURL + "/api/v1/membership/" + X500ToShortIdHash.get(vnodeX500) + "/")
|
||||
.basicAuth(pc.rpcUser, pc.rpcPasswd)
|
||||
.asJson();
|
||||
if (isMembershipRegComplete(statusResponse)) {
|
||||
approved.add(vnodeX500);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new CsdeException("Error when registering V-Node '" + vnodeX500 + "'", e);
|
||||
}
|
||||
}
|
||||
approved.forEach(vnodesToCheck::remove);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean isMembershipRegComplete(HttpResponse<JsonNode> response) throws CsdeException {
|
||||
if(response.getStatus() == HTTP_OK) {
|
||||
JsonNode responseBody = response.getBody();
|
||||
pc.out.println(responseBody.toPrettyString());
|
||||
if(responseBody.getArray().length() > 0) {
|
||||
JSONObject memRegStatusInfo = (JSONObject) responseBody
|
||||
.getArray()
|
||||
.getJSONObject(0);
|
||||
String memRegStatus = memRegStatusInfo.get("registrationStatus").toString();
|
||||
if (memRegStatus.equals("DECLINED")) {
|
||||
throw new CsdeException("V-Node membership registration declined by Corda");
|
||||
}
|
||||
return memRegStatus.equals("APPROVED");
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
utils.reportError(response);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -5,15 +5,14 @@ import kong.unirest.Unirest;
|
||||
import kong.unirest.json.JSONArray;
|
||||
import kong.unirest.json.JSONObject;
|
||||
import kong.unirest.HttpResponse;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
|
||||
import static java.net.HttpURLConnection.HTTP_OK;
|
||||
|
||||
// todo: This class needs refactoring, see https://r3-cev.atlassian.net/browse/CORE-11624
|
||||
public class DeployCPIsHelper {
|
||||
|
||||
public DeployCPIsHelper() {
|
||||
|
40
buildSrc/src/main/java/com/r3/csde/NetworkConfig.java
Normal file
40
buildSrc/src/main/java/com/r3/csde/NetworkConfig.java
Normal file
@ -0,0 +1,40 @@
|
||||
package com.r3.csde;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* This class reads the network config from the json file and makes it available as a list of VNodes.
|
||||
*/
|
||||
public class NetworkConfig {
|
||||
|
||||
private List<VNode> vNodes;
|
||||
private String configFilePath;
|
||||
|
||||
public NetworkConfig(String _configFilePath) throws CsdeException {
|
||||
configFilePath = _configFilePath;
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
try {
|
||||
FileInputStream in = new FileInputStream(configFilePath);
|
||||
vNodes = mapper.readValue(in, new TypeReference<List<VNode>>() {
|
||||
});
|
||||
} catch (Exception e) {
|
||||
throw new CsdeException("Failed to read static network configuration file, with exception: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
String getConfigFilePath() { return configFilePath; }
|
||||
|
||||
List<VNode> getVNodes() { return vNodes; }
|
||||
|
||||
List<String> getX500Names() {
|
||||
return vNodes.stream().map(vn -> vn.getX500Name()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package com.r3.csde;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.Map;
|
||||
|
||||
@ -14,8 +13,9 @@ public class ProjectContext {
|
||||
int retryWaitMs = 1000;
|
||||
PrintStream out = System.out;
|
||||
String CPIUploadStatusBaseName = "CPIFileStatus.json";
|
||||
String NotaryCPIUploadBaseName = "CPIFileStatus-NotaryServer.json";
|
||||
String CPIUploadStatusFName;
|
||||
String X500ConfigFile = "config/dev-net.json";
|
||||
String NotaryCPIUploadStatusFName;
|
||||
String javaBinDir;
|
||||
String cordaPidCache = "CordaPIDCache.dat";
|
||||
String dbContainerName;
|
||||
@ -67,6 +67,7 @@ public class ProjectContext {
|
||||
dbContainerName = inDbContainerName;
|
||||
JDBCDir = inJDBCDir;
|
||||
CPIUploadStatusFName = workspaceDir + "/" + CPIUploadStatusBaseName;
|
||||
NotaryCPIUploadStatusFName = workspaceDir + "/" + NotaryCPIUploadBaseName;
|
||||
signingCertAlias = inSigningCertAlias;
|
||||
signingCertFName = inSigningCertFName;
|
||||
keystoreAlias = inKeystoreAlias;
|
||||
|
@ -1,25 +1,19 @@
|
||||
package com.r3.csde;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import kong.unirest.HttpResponse;
|
||||
import kong.unirest.JsonNode;
|
||||
import kong.unirest.Unirest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import static java.lang.Thread.sleep;
|
||||
|
||||
// todo: This class needs refactoring, see https://r3-cev.atlassian.net/browse/CORE-11624
|
||||
public class ProjectUtils {
|
||||
|
||||
ProjectContext pc;
|
||||
|
||||
ProjectUtils(ProjectContext _pc) {
|
||||
pc = _pc;
|
||||
}
|
||||
|
||||
|
||||
void rpcWait(int millis) {
|
||||
try {
|
||||
sleep(millis);
|
||||
@ -29,36 +23,7 @@ public class ProjectUtils {
|
||||
}
|
||||
}
|
||||
|
||||
void rpcWait() {
|
||||
rpcWait( pc.retryWaitMs);
|
||||
}
|
||||
|
||||
public LinkedList<String> getConfigX500Ids(String configFile) throws IOException {
|
||||
LinkedList<String> x500Ids = new LinkedList<>();
|
||||
// com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
|
||||
FileInputStream in = new FileInputStream(configFile);
|
||||
com.fasterxml.jackson.databind.JsonNode jsonNode = mapper.readTree(in);
|
||||
for( com.fasterxml.jackson.databind.JsonNode identity: jsonNode.get("identities")) {
|
||||
x500Ids.add(jsonNodeToString(identity));
|
||||
}
|
||||
return x500Ids;
|
||||
}
|
||||
|
||||
public String jsonNodeToString(com.fasterxml.jackson.databind.JsonNode jsonNode) {
|
||||
String jsonString = jsonNode.toString();
|
||||
return jsonString.substring(1, jsonString.length()-1);
|
||||
}
|
||||
|
||||
public void downloadFile(String url, String targetPath) {
|
||||
Unirest.get(url)
|
||||
.asFile(targetPath)
|
||||
.getBody();
|
||||
}
|
||||
|
||||
public void reportError(@NotNull HttpResponse<JsonNode> response) throws CsdeException {
|
||||
public void reportError(HttpResponse<JsonNode> response) throws CsdeException {
|
||||
|
||||
pc.out.println("*** *** ***");
|
||||
pc.out.println("Unexpected response from Corda");
|
||||
|
25
buildSrc/src/main/java/com/r3/csde/VNode.java
Normal file
25
buildSrc/src/main/java/com/r3/csde/VNode.java
Normal file
@ -0,0 +1,25 @@
|
||||
package com.r3.csde;
|
||||
|
||||
|
||||
/**
|
||||
* This class is a representation of a Vnode used to express the vNodes required on the network.
|
||||
*/
|
||||
|
||||
public class VNode {
|
||||
private String x500Name;
|
||||
private String cpi;
|
||||
|
||||
private String serviceX500Name;
|
||||
|
||||
public VNode() { }
|
||||
|
||||
public String getX500Name(){ return x500Name; }
|
||||
public void setX500Name(String _x500Name) { x500Name = _x500Name; }
|
||||
|
||||
public String getCpi() { return cpi; }
|
||||
public void setCpi(String _cpi) { cpi = _cpi; }
|
||||
|
||||
public String getServiceX500Name() { return serviceX500Name; }
|
||||
public void setServiceX500Name(String _name) { serviceX500Name = _name; }
|
||||
|
||||
}
|
288
buildSrc/src/main/java/com/r3/csde/VNodesHelper.java
Normal file
288
buildSrc/src/main/java/com/r3/csde/VNodesHelper.java
Normal file
@ -0,0 +1,288 @@
|
||||
package com.r3.csde;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.r3.csde.dtos.*;
|
||||
import kong.unirest.HttpResponse;
|
||||
import kong.unirest.JsonNode;
|
||||
import kong.unirest.Unirest;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import static java.net.HttpURLConnection.HTTP_OK;
|
||||
|
||||
/**
|
||||
* The VNodesHelper class is used to create and register the Vnodes specified in the static-network-config.json file.
|
||||
*/
|
||||
|
||||
public class VNodesHelper {
|
||||
private ProjectContext pc;
|
||||
private ProjectUtils utils;
|
||||
private NetworkConfig config;
|
||||
private ObjectMapper mapper;
|
||||
|
||||
public VNodesHelper(ProjectContext _pc, NetworkConfig _config) {
|
||||
pc = _pc;
|
||||
utils = new ProjectUtils(pc);
|
||||
config = _config;
|
||||
Unirest.config().verifySsl(false);
|
||||
mapper = new ObjectMapper();
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for setting up vnodes, called from the 5-vNodeSetup task in csde.gradle
|
||||
*/
|
||||
public void vNodesSetup() throws CsdeException {
|
||||
List<VNode> requiredNodes = config.getVNodes();
|
||||
createVNodes(requiredNodes);
|
||||
registerVNodes(requiredNodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates vnodes specified in the config if they don't already exist.
|
||||
* @param requiredNodes represents the list of VNodes as specified in the network Config json file (static-network-config.json)
|
||||
*/
|
||||
private void createVNodes(List<VNode> requiredNodes) throws CsdeException {
|
||||
|
||||
// Get existing Nodes.
|
||||
List<VirtualNodeInfoDTO> existingVNodes = getExistingNodes();
|
||||
|
||||
// Check if each required vnode already exist, if not create it.
|
||||
for (VNode vn : requiredNodes) {
|
||||
// Match on x500 and cpi name
|
||||
List<VirtualNodeInfoDTO> matches = existingVNodes
|
||||
.stream()
|
||||
.filter(existing ->
|
||||
existing.getHoldingIdentity().getX500Name().equals( vn.getX500Name()) &&
|
||||
existing.getCpiIdentifier().getCpiName().equals(vn.getCpi()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (matches.size() == 0) {
|
||||
createVNode(vn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of the virtual nodes which have already been created.
|
||||
* @return a list of the virtual nodes which have already been created.
|
||||
*/
|
||||
private List<VirtualNodeInfoDTO> getExistingNodes () throws CsdeException {
|
||||
|
||||
HttpResponse<JsonNode> response = Unirest.get(pc.baseURL + "/api/v1/virtualnode")
|
||||
.basicAuth(pc.rpcUser, pc.rpcPasswd)
|
||||
.asJson();
|
||||
|
||||
if (response.getStatus() != HTTP_OK) {
|
||||
throw new CsdeException("Failed to get Existing vNodes, response status: "+ response.getStatus());
|
||||
}
|
||||
|
||||
try {
|
||||
return mapper.readValue(response.getBody().toString(), VirtualNodesDTO.class).getVirtualNodes();
|
||||
} catch (Exception e){
|
||||
throw new CsdeException("Failed to get Existing vNodes with exception: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a vnode on the Corda cluster from the VNode info.
|
||||
* @param vNode represents a virtual node using VNode Class.
|
||||
*/
|
||||
private void createVNode(VNode vNode) throws CsdeException {
|
||||
|
||||
pc.out.println("Creating virtual node for "+ vNode.getX500Name());
|
||||
|
||||
// Reads the current CPIFileChecksum value and checks it has been uploaded.
|
||||
String cpiCheckSum = getCpiCheckSum(vNode);
|
||||
if (!checkCpiUploaded(cpiCheckSum)) throw new CsdeException("Cpi " + cpiCheckSum + " not uploaded.");
|
||||
|
||||
// Creates the vnode on Cluster
|
||||
HttpResponse<JsonNode> response = Unirest.post(pc.baseURL + "/api/v1/virtualnode")
|
||||
.body("{ \"request\" : { \"cpiFileChecksum\": \"" + cpiCheckSum + "\", \"x500Name\": \"" + vNode.getX500Name() + "\" } }")
|
||||
.basicAuth(pc.rpcUser, pc.rpcPasswd)
|
||||
.asJson();
|
||||
|
||||
if (response.getStatus() != HTTP_OK) {
|
||||
throw new CsdeException("Creation of virtual node failed with response status: " + response.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the latest CPI checksums from file.
|
||||
* @param vNode represents a virtual node using VNode Class.
|
||||
*/
|
||||
private String getCpiCheckSum(VNode vNode) throws CsdeException {
|
||||
|
||||
try {
|
||||
String file = (vNode.getServiceX500Name() == null) ? pc.CPIUploadStatusFName : pc.NotaryCPIUploadStatusFName;
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
CPIFileStatusDTO statusDTO = mapper.readValue(in, CPIFileStatusDTO.class);
|
||||
return statusDTO.getCpiFileChecksum().toString();
|
||||
} catch (Exception e) {
|
||||
throw new CsdeException("Failed to read CPI checksum from file, with error: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkCpiUploaded(String cpiCheckSum) throws CsdeException {
|
||||
|
||||
HttpResponse<JsonNode> response = Unirest.get(pc.baseURL + "/api/v1/cpi")
|
||||
.basicAuth(pc.rpcUser, pc.rpcPasswd)
|
||||
.asJson();
|
||||
|
||||
if (response.getStatus() != HTTP_OK) {
|
||||
throw new CsdeException("Failed to check cpis, response status: " + response.getStatus());
|
||||
}
|
||||
|
||||
try {
|
||||
GetCPIsResponseDTO cpisResponse = mapper.readValue(
|
||||
response.getBody().toString(), GetCPIsResponseDTO.class);
|
||||
|
||||
for (CpiMetadataDTO cpi: cpisResponse.getCpis()) {
|
||||
if (Objects.equals(cpi.getCpiFileChecksum(), cpiCheckSum)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Returns false if no cpis were returned or the cpiCheckSum didnt' match ay results.
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
throw new CsdeException("Failed to check cpis with exception: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the required virtual nodes have been registered and if not registers them.
|
||||
* @param requiredNodes represents the list of VNodes as specified in the network Config json file (static-network-config.json)
|
||||
*/
|
||||
private void registerVNodes(List<VNode> requiredNodes) throws CsdeException {
|
||||
|
||||
// There appears to be a delay between the successful post /virtualnodes synchronous call and the
|
||||
// vnodes being returned in the GET /virtualnodes call. Putting a thread wait here as a quick fix
|
||||
// as this will move to async mechanism post beta2.
|
||||
utils.rpcWait(2000);
|
||||
List<VirtualNodeInfoDTO> existingVNodes = getExistingNodes();
|
||||
|
||||
for (VNode vn: requiredNodes) {
|
||||
// Match on x500 and cpi name
|
||||
List<VirtualNodeInfoDTO> matches = existingVNodes
|
||||
.stream()
|
||||
.filter(existing ->
|
||||
existing.getHoldingIdentity().getX500Name().equals( vn.getX500Name()) &&
|
||||
existing.getCpiIdentifier().getCpiName().equals(vn.getCpi()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (matches.size() == 0) {
|
||||
throw new CsdeException("Registration failed because virtual node for '" + vn.getX500Name() + "' not found.");
|
||||
} else if (matches.size() >1 ) {
|
||||
throw new CsdeException(("Registration failed because more than virtual node for '" + vn.getX500Name() + "'"));
|
||||
}
|
||||
|
||||
String shortHash = matches.get(0).getHoldingIdentity().getShortHash();
|
||||
|
||||
if (!checkVNodeIsRegistered(shortHash)) {
|
||||
registerVnode(vn, shortHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a virtual node.
|
||||
* @param vnode represents the vnode to be registered.
|
||||
* @param shortHash the shortHash of the virtual node to register.
|
||||
*/
|
||||
private void registerVnode(VNode vnode, String shortHash) throws CsdeException {
|
||||
|
||||
pc.out.println("Registering vNode: " + vnode.getX500Name() + " with shortHash: " + shortHash);
|
||||
|
||||
// Configure the registration body (notary vs non notary)
|
||||
String registrationBody;
|
||||
if (vnode.getServiceX500Name() == null) {
|
||||
registrationBody =
|
||||
"{ \"action\" : \"requestJoin\"," +
|
||||
" \"context\" : {" +
|
||||
" \"corda.key.scheme\" : \"CORDA.ECDSA.SECP256R1\" } }";
|
||||
} else {
|
||||
registrationBody =
|
||||
"{ \"action\" : \"requestJoin\"," +
|
||||
" \"context\" : {" +
|
||||
" \"corda.key.scheme\" : \"CORDA.ECDSA.SECP256R1\", " +
|
||||
" \"corda.roles.0\" : \"notary\", " +
|
||||
" \"corda.notary.service.name\" : \"" + vnode.getServiceX500Name() + "\", " +
|
||||
" \"corda.notary.service.plugin\" : \"net.corda.notary.NonValidatingNotary\" } }";
|
||||
}
|
||||
|
||||
HttpResponse<JsonNode> response = Unirest.post(pc.baseURL + "/api/v1/membership/" + shortHash)
|
||||
.body(registrationBody)
|
||||
.basicAuth(pc.rpcUser, pc.rpcPasswd)
|
||||
.asJson();
|
||||
|
||||
if (response.getStatus() == HTTP_OK) {
|
||||
pc.out.println("Membership requested for node " + shortHash);
|
||||
} else {
|
||||
throw new CsdeException("Failed to register virtual node " + shortHash + ", response status: " + response.getStatus() );
|
||||
}
|
||||
|
||||
// wait until Vnode registered
|
||||
pollForRegistration(shortHash, 30000, 1000);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a virtual node with given shortHash has been registered
|
||||
* @param shortHash shortHash of the node which is being checked.
|
||||
* @return returns true if the vnode is registered
|
||||
*/
|
||||
private boolean checkVNodeIsRegistered(String shortHash) throws CsdeException {
|
||||
|
||||
// Queries registration status for vnode.
|
||||
HttpResponse<JsonNode> response = Unirest.get(pc.baseURL + "/api/v1/membership/" + shortHash)
|
||||
.basicAuth(pc.rpcUser, pc.rpcPasswd)
|
||||
.asJson();
|
||||
|
||||
if (response.getStatus() != HTTP_OK)
|
||||
throw new CsdeException("Failed to check registration status for virtual node '" + shortHash +
|
||||
"' response status: " + response.getStatus());
|
||||
|
||||
try {
|
||||
// If the response body is not empty check all previous requests for an "APPROVED"
|
||||
if (!response.getBody().getArray().isEmpty()) {
|
||||
List<RegistrationRequestProgressDTO> requests = mapper.readValue(
|
||||
response.getBody().toString(), new TypeReference<>() {
|
||||
});
|
||||
for (RegistrationRequestProgressDTO request : requests) {
|
||||
if (Objects.equals(request.getRegistrationStatus(), "APPROVED")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Returns false if array was empty or "APPROVED" wasn't found
|
||||
return false;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new CsdeException("Failed to check registration status for " + shortHash +
|
||||
" with exception " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls for registration of a vnode
|
||||
* @param shortHash short hash of the node which is being poled for
|
||||
* @param duration the number of milliseconds before the pollign times out
|
||||
* @param cooldown the number of milliseconds in between poll attempts.
|
||||
*/
|
||||
private void pollForRegistration(String shortHash, int duration, int cooldown) throws CsdeException {
|
||||
|
||||
int timer = 0;
|
||||
while (timer < duration ) {
|
||||
if (checkVNodeIsRegistered(shortHash)){
|
||||
pc.out.println("Vnode " + shortHash +" registered.");
|
||||
return;
|
||||
}
|
||||
utils.rpcWait(cooldown);
|
||||
timer += cooldown;
|
||||
}
|
||||
throw new CsdeException("Vnode " + shortHash + " failed to register in " + duration + " milliseconds");
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.r3.csde.dtos;
|
||||
|
||||
public class CPIFileStatusDTO {
|
||||
private String status;
|
||||
private String cpiFileChecksum;
|
||||
|
||||
public CPIFileStatusDTO() {}
|
||||
|
||||
public String getStatus() { return status; }
|
||||
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
|
||||
public String getCpiFileChecksum() { return cpiFileChecksum; }
|
||||
|
||||
public void setCpiFileChecksum(String cpiFileChecksum) { this.cpiFileChecksum = cpiFileChecksum; }
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.r3.csde.dtos;
|
||||
|
||||
public class CpiIdentifierDTO {
|
||||
|
||||
// Note, these DTOs don't cover all returned values, just the ones required for CSDE
|
||||
private String cpiName;
|
||||
private String cpiVersion;
|
||||
private String signerSummaryHash;
|
||||
|
||||
public CpiIdentifierDTO() { }
|
||||
|
||||
public String getCpiName() { return cpiName; }
|
||||
|
||||
public void setCpiName(String cpiName) { this.cpiName = cpiName; }
|
||||
|
||||
public String getCpiVersion() { return cpiVersion; }
|
||||
|
||||
public void setCpiVersion(String cpiVersion) { this.cpiVersion = cpiVersion; }
|
||||
|
||||
public String getSignerSummaryHash() { return signerSummaryHash; }
|
||||
|
||||
public void setSignerSummaryHash(String signerSummaryHash) { this.signerSummaryHash = signerSummaryHash; }
|
||||
}
|
17
buildSrc/src/main/java/com/r3/csde/dtos/CpiMetadataDTO.java
Normal file
17
buildSrc/src/main/java/com/r3/csde/dtos/CpiMetadataDTO.java
Normal file
@ -0,0 +1,17 @@
|
||||
package com.r3.csde.dtos;
|
||||
|
||||
public class CpiMetadataDTO {
|
||||
// Note, these DTOs don't cover all returned values, just the ones required for CSDE.
|
||||
private String cpiFileChecksum;
|
||||
private CpiIdentifierDTO id;
|
||||
|
||||
public CpiMetadataDTO() {}
|
||||
|
||||
public String getCpiFileChecksum() { return cpiFileChecksum; }
|
||||
|
||||
public void setCpiFileChecksum(String cpiFileChecksum) { this.cpiFileChecksum = cpiFileChecksum; }
|
||||
|
||||
public CpiIdentifierDTO getId() { return id; }
|
||||
|
||||
public void setId(CpiIdentifierDTO id) { this.id = id; }
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.r3.csde.dtos;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GetCPIsResponseDTO {
|
||||
|
||||
private List<CpiMetadataDTO> cpis;
|
||||
|
||||
public GetCPIsResponseDTO() {}
|
||||
|
||||
public List<CpiMetadataDTO> getCpis() { return cpis; }
|
||||
|
||||
public void setCpis(List<CpiMetadataDTO> cpis) { this.cpis = cpis; }
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.r3.csde.dtos;
|
||||
|
||||
public class HoldingIdentityDTO {
|
||||
// Note, these DTOs don't cover all returned values, just the ones required for CSDE.
|
||||
private String fullHash;
|
||||
private String groupId;
|
||||
private String shortHash;
|
||||
private String x500Name;
|
||||
|
||||
public HoldingIdentityDTO() {}
|
||||
|
||||
public String getFullHash() { return fullHash; }
|
||||
|
||||
public void setFullHash(String fullHash) { this.fullHash = fullHash; }
|
||||
|
||||
public String getGroupId() { return groupId; }
|
||||
|
||||
public void setGroupID(String groupID) { this.groupId = groupId; }
|
||||
|
||||
public String getShortHash() { return shortHash; }
|
||||
|
||||
public void setShortHash(String shortHash) { this.shortHash = shortHash; }
|
||||
|
||||
public String getX500Name() { return x500Name; }
|
||||
|
||||
public void setX500Name(String x500Name) { this.x500Name = x500Name; }
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.r3.csde.dtos;
|
||||
|
||||
public class RegistrationRequestProgressDTO {
|
||||
// Note, these DTOs don't cover all returned values, just the ones required for CSDE.
|
||||
private String registrationStatus;
|
||||
private String reason;
|
||||
|
||||
public RegistrationRequestProgressDTO() {}
|
||||
|
||||
public String getRegistrationStatus() { return registrationStatus; }
|
||||
|
||||
public void setRegistrationStatus(String registrationStatus) { this.registrationStatus = registrationStatus; }
|
||||
|
||||
public String getReason() { return reason; }
|
||||
|
||||
public void setReason(String reason) { this.reason = reason; }
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.r3.csde.dtos;
|
||||
|
||||
public class VirtualNodeInfoDTO {
|
||||
// Note, these DTOs don't cover all returned values, just the ones required for CSDE.
|
||||
private HoldingIdentityDTO holdingIdentity;
|
||||
private CpiIdentifierDTO cpiIdentifier;
|
||||
|
||||
public VirtualNodeInfoDTO() {}
|
||||
|
||||
public HoldingIdentityDTO getHoldingIdentity() { return holdingIdentity; }
|
||||
|
||||
public void setHoldingIdentity(HoldingIdentityDTO holdingIdentity) { this.holdingIdentity = holdingIdentity; }
|
||||
|
||||
public CpiIdentifierDTO getCpiIdentifier() { return cpiIdentifier; }
|
||||
|
||||
public void setCpiIdentifier(CpiIdentifierDTO cpiIdentifier) { this.cpiIdentifier = cpiIdentifier; }
|
||||
}
|
14
buildSrc/src/main/java/com/r3/csde/dtos/VirtualNodesDTO.java
Normal file
14
buildSrc/src/main/java/com/r3/csde/dtos/VirtualNodesDTO.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.r3.csde.dtos;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class VirtualNodesDTO {
|
||||
|
||||
private List<VirtualNodeInfoDTO> virtualNodes;
|
||||
|
||||
public VirtualNodesDTO() {}
|
||||
|
||||
public List<VirtualNodeInfoDTO> getVirtualNodes() { return virtualNodes; }
|
||||
|
||||
public void setVirtualNodes(List<VirtualNodeInfoDTO> virtualNodes) { this.virtualNodes = virtualNodes; }
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
{
|
||||
"identities" : [
|
||||
"CN=Alice, OU=Test Dept, O=R3, L=London, C=GB",
|
||||
"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB",
|
||||
"CN=Charlie, OU=Test Dept, O=R3, L=London, C=GB",
|
||||
"CN=NotaryRep1, OU=Test Dept, O=R3, L=London, C=GB"],
|
||||
"notaries" : [
|
||||
{
|
||||
"serviceX500Name": "CN=NotaryService, OU=Test Dept, O=R3, L=London, C=GB",
|
||||
"representatives": ["CN=NotaryRep1, OU=Test Dept, O=R3, L=London, C=GB"]
|
||||
}]
|
||||
}
|
51
config/log4j2.xml
Normal file
51
config/log4j2.xml
Normal file
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="INFO">
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} %X - %msg%n"/>
|
||||
</Console>
|
||||
|
||||
<RollingFile name="App"
|
||||
fileName="logs/corda.log"
|
||||
filePattern="logs/corda.%d{MM-dd-yyyy}.%i.log"
|
||||
ignoreExceptions="false">
|
||||
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} %X - %msg%n"/>
|
||||
<Policies>
|
||||
<OnStartupTriggeringPolicy />
|
||||
<TimeBasedTriggeringPolicy />
|
||||
<SizeBasedTriggeringPolicy size="10 MB" />
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy>
|
||||
<Delete basePath="logs/">
|
||||
<IfFileName glob="logs/corda.*.log">
|
||||
<IfAny>
|
||||
<IfAccumulatedFileSize exceeds="500 MB" />
|
||||
<IfAccumulatedFileCount exceeds="10" />
|
||||
</IfAny>
|
||||
</IfFileName>
|
||||
<IfLastModified age="7d" />
|
||||
</Delete>
|
||||
</DefaultRolloverStrategy>
|
||||
</RollingFile>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<logger name="Console">
|
||||
<AppenderRef ref="Console" level="info"/>
|
||||
</logger>
|
||||
|
||||
<!-- log warn only for these 3rd party libs -->
|
||||
<Logger name="com.zaxxer.hikari" level="warn" />
|
||||
<Logger name="io.javalin.Javalin" level="warn" />
|
||||
<Logger name="org.apache.aries.spifly" level="warn" />
|
||||
<Logger name="org.apache.kafka" level="warn" />
|
||||
<Logger name="org.eclipse.jetty" level="warn" />
|
||||
<Logger name="org.hibernate" level="warn" />
|
||||
|
||||
<!-- default to warn only for OSGi logging -->
|
||||
<Logger name="net.corda.osgi.framework.OSGiFrameworkWrap" level="warn" />
|
||||
|
||||
<root level="debug">
|
||||
<AppenderRef ref="App" level="info"/>
|
||||
</root>
|
||||
</Loggers>
|
||||
</Configuration>
|
23
config/static-network-config.json
Normal file
23
config/static-network-config.json
Normal file
@ -0,0 +1,23 @@
|
||||
[
|
||||
{
|
||||
"x500Name" : "CN=Alice, OU=Test Dept, O=R3, L=London, C=GB",
|
||||
"cpi" : "cpi name"
|
||||
},
|
||||
{
|
||||
"x500Name" : "CN=Bob, OU=Test Dept, O=R3, L=London, C=GB",
|
||||
"cpi" : "cpi name"
|
||||
},
|
||||
{
|
||||
"x500Name" : "CN=Charlie, OU=Test Dept, O=R3, L=London, C=GB",
|
||||
"cpi" : "cpi name"
|
||||
},
|
||||
{
|
||||
"x500Name" : "CN=Dave, OU=Test Dept, O=R3, L=London, C=GB",
|
||||
"cpi" : "cpi name"
|
||||
},
|
||||
{
|
||||
"x500Name" : "CN=NotaryRep1, OU=Test Dept, O=R3, L=London, C=GB",
|
||||
"cpi" : "CSDE Notary Server CPI",
|
||||
"serviceX500Name": "CN=NotaryService, OU=Test Dept, O=R3, L=London, C=GB"
|
||||
}
|
||||
]
|
@ -13,12 +13,8 @@ plugins {
|
||||
// A cordaProvided declaration is required for anything that we use that the Corda API provides.
|
||||
// This is required to allow us to build CorDapp modules as OSGi bundles that CPI and CPB files are built on.
|
||||
dependencies {
|
||||
// We need a version of kotlin-stdlib-jdk8 built as an OSGi bundle, this is "kotlin-stdlib-jdk8-osgi".
|
||||
// R3 builds kotlin-stdlib-jdk8-osgi from Kotlin's kotlin-stdlib-jdk8.
|
||||
// NB:
|
||||
// Kotlin's kotlin-osgi-bundle does not provide all of the Kotlin API that is required,
|
||||
// There is no kotlin-stdlib-jdk11, but one is not needed even though we are targetting Java 11.
|
||||
cordaProvided 'net.corda.kotlin:kotlin-stdlib-jdk8-osgi'
|
||||
|
||||
cordaProvided 'org.jetbrains.kotlin:kotlin-osgi-bundle'
|
||||
|
||||
// Declare a "platform" so that we use the correct set of dependency versions for the version of the
|
||||
// Corda API specified.
|
||||
@ -45,8 +41,9 @@ dependencies {
|
||||
|
||||
// This are shared so should be here.
|
||||
// Dependencies Required By Test Tooling
|
||||
testImplementation "net.corda:corda-simulator-api:$simulatorVersion"
|
||||
testRuntimeOnly "net.corda:corda-simulator-runtime:$simulatorVersion"
|
||||
// Todo: these are commented out as the simulator UTXO work has not been merged into Gecko yet.
|
||||
// testImplementation "net.corda:corda-simulator-api:$simulatorVersion"
|
||||
// testRuntimeOnly "net.corda:corda-simulator-runtime:$simulatorVersion"
|
||||
|
||||
// 3rd party libraries
|
||||
// Required
|
||||
|
@ -4,18 +4,10 @@ import com.r3.developers.csdetemplate.utxoexample.states.ChatState;
|
||||
import net.corda.v5.base.exceptions.CordaRuntimeException;
|
||||
import net.corda.v5.ledger.utxo.Command;
|
||||
import net.corda.v5.ledger.utxo.Contract;
|
||||
import net.corda.v5.ledger.utxo.ContractState;
|
||||
import net.corda.v5.ledger.utxo.StateAndRef;
|
||||
import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Objects.*;
|
||||
|
||||
public class ChatContract implements Contract {
|
||||
|
||||
|
@ -2,11 +2,9 @@ package com.r3.developers.csdetemplate.utxoexample.states;
|
||||
|
||||
import com.r3.developers.csdetemplate.utxoexample.contracts.ChatContract;
|
||||
import net.corda.v5.base.annotations.ConstructorForDeserialization;
|
||||
import net.corda.v5.base.annotations.CordaSerializable;
|
||||
import net.corda.v5.base.types.MemberX500Name;
|
||||
import net.corda.v5.ledger.utxo.BelongsToContract;
|
||||
import net.corda.v5.ledger.utxo.ContractState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.*;
|
||||
|
@ -2,15 +2,15 @@ kotlin.code.style=official
|
||||
|
||||
# Specify the version of the Corda-API to use.
|
||||
# This needs to match the version supported by the Corda Cluster the CorDapp will run on.
|
||||
cordaApiVersion=5.0.0.524-Fox1.1
|
||||
cordaApiVersion=5.0.0.665-Gecko-RC02
|
||||
|
||||
# Settings For Development Utilities
|
||||
combinedWorkerVersion=5.0.0.0-Fox1.1
|
||||
simulatorVersion=5.0.0.0-Fox1.1
|
||||
combinedWorkerVersion=5.0.0.0-Gecko-RC02
|
||||
simulatorVersion=5.0.0.0-Gecko-RC02
|
||||
|
||||
# Specify the version of the notary plugins to use.
|
||||
# Currently packaged as part of corda-runtime-os, so should be set to a corda-runtime-os version.
|
||||
cordaNotaryPluginsVersion=5.0.0.0-Fox1.1
|
||||
cordaNotaryPluginsVersion=5.0.0.0-Gecko-RC02
|
||||
|
||||
# Specify the version of the cordapp-cpb and cordapp-cpk plugins
|
||||
cordaPluginsVersion=7.0.1
|
||||
@ -39,3 +39,6 @@ cordaRpcPasswd=admin
|
||||
devEnvWorkspace=workspace
|
||||
dbContainerName=CSDEpostgresql
|
||||
|
||||
# R3 internal repository
|
||||
#S3 version for HC and RC versions published by CD/CD team
|
||||
artifactoryContextUrl=https://staging.download.corda.net/maven/20ede3c6-29c0-11ed-966d-b7c36748b9f6-Gecko-RC02/
|
@ -3,6 +3,9 @@ pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = "$artifactoryContextUrl/"
|
||||
}
|
||||
}
|
||||
|
||||
// The plugin dependencies with versions of the plugins congruent with the specified CorDapp plugin version,
|
||||
|
@ -15,12 +15,7 @@ dependencies {
|
||||
// From other subprojects:
|
||||
cordapp project(':contracts')
|
||||
|
||||
// We need a version of kotlin-stdlib-jdk8 built as an OSGi bundle, this is "kotlin-stdlib-jdk8-osgi".
|
||||
// R3 builds kotlin-stdlib-jdk8-osgi from Kotlin's kotlin-stdlib-jdk8.
|
||||
// NB:
|
||||
// Kotlin's kotlin-osgi-bundle does not provide all of the Kotlin API that is required,
|
||||
// There is no kotlin-stdlib-jdk11, but one is not needed even though we are targetting Java 11.
|
||||
cordaProvided 'net.corda.kotlin:kotlin-stdlib-jdk8-osgi'
|
||||
cordaProvided 'org.jetbrains.kotlin:kotlin-osgi-bundle'
|
||||
|
||||
// Declare a "platform" so that we use the correct set of dependency versions for the version of the
|
||||
// Corda API specified.
|
||||
@ -47,8 +42,9 @@ dependencies {
|
||||
|
||||
// This are shared so should be here.
|
||||
// Dependencies Required By Test Tooling
|
||||
testImplementation "net.corda:corda-simulator-api:$simulatorVersion"
|
||||
testRuntimeOnly "net.corda:corda-simulator-runtime:$simulatorVersion"
|
||||
// Todo: these are commented out as the simulator UTXO work has not been merged into Gecko yet.
|
||||
// testImplementation "net.corda:corda-simulator-api:$simulatorVersion"
|
||||
// testRuntimeOnly "net.corda:corda-simulator-runtime:$simulatorVersion"
|
||||
|
||||
// 3rd party libraries
|
||||
// Required
|
||||
|
@ -7,7 +7,6 @@ import net.corda.v5.application.messaging.FlowMessaging;
|
||||
import net.corda.v5.application.messaging.FlowSession;
|
||||
import net.corda.v5.base.annotations.Suspendable;
|
||||
import net.corda.v5.base.types.MemberX500Name;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -16,7 +15,7 @@ import org.slf4j.LoggerFactory;
|
||||
// to link the two sides of the flow together they need to have the same protocol.
|
||||
@InitiatingFlow(protocol = "my-first-flow")
|
||||
// MyFirstFlow should inherit from RPCStartableFlow, which tells Corda it can be started via an RPC call
|
||||
public class MyFirstFlow implements RPCStartableFlow {
|
||||
public class MyFirstFlow implements ClientStartableFlow {
|
||||
|
||||
// Log messages from the flows for debugging.
|
||||
private final static Logger log = LoggerFactory.getLogger(MyFirstFlow.class);
|
||||
@ -43,10 +42,10 @@ public class MyFirstFlow implements RPCStartableFlow {
|
||||
// When a flow is invoked its call() method is called.
|
||||
// Call() methods must be marked as @Suspendable, this allows Corda to pause mid-execution to wait
|
||||
// for a response from the other flows and services.
|
||||
@NotNull
|
||||
|
||||
@Suspendable
|
||||
@Override
|
||||
public String call(RPCRequestData requestBody) {
|
||||
public String call(ClientRequestBody requestBody) {
|
||||
|
||||
// Follow what happens in the console or logs.
|
||||
log.info("MFF: MyFirstFlow.call() called");
|
||||
|
@ -28,7 +28,7 @@ import java.util.UUID;
|
||||
import static java.util.Objects.*;
|
||||
|
||||
// See Chat CorDapp Design section of the getting started docs for a description of this flow.
|
||||
public class CreateNewChatFlow implements RPCStartableFlow {
|
||||
public class CreateNewChatFlow implements ClientStartableFlow {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(CreateNewChatFlow.class);
|
||||
|
||||
@ -52,7 +52,7 @@ public class CreateNewChatFlow implements RPCStartableFlow {
|
||||
|
||||
@Suspendable
|
||||
@Override
|
||||
public String call( RPCRequestData requestBody) {
|
||||
public String call( ClientRequestBody requestBody) {
|
||||
|
||||
log.info("CreateNewChatFlow.call() called");
|
||||
|
||||
@ -101,10 +101,10 @@ public class CreateNewChatFlow implements RPCStartableFlow {
|
||||
.addCommand(new ChatContract.Create())
|
||||
.addSignatories(chatState.getParticipants());
|
||||
|
||||
// Convert the transaction builder to a UTXOSignedTransaction and sign with this Vnode's first Ledger key.
|
||||
// Note, toSignedTransaction() is currently a placeholder method, hence being marked as deprecated.
|
||||
@SuppressWarnings("DEPRECATION")
|
||||
UtxoSignedTransaction signedTransaction = txBuilder.toSignedTransaction(myInfo.getLedgerKeys().get(0));
|
||||
// Convert the transaction builder to a UTXOSignedTransaction. Verifies the content of the
|
||||
// UtxoTransactionBuilder and signs the transaction with any required signatories that belong to
|
||||
// the current node.
|
||||
UtxoSignedTransaction signedTransaction = txBuilder.toSignedTransaction();
|
||||
|
||||
// Call FinalizeChatSubFlow which will finalise the transaction.
|
||||
// If successful the flow will return a String of the created transaction id,
|
||||
@ -124,7 +124,7 @@ RequestBody for triggering the flow via http-rpc:
|
||||
{
|
||||
"clientRequestId": "create-1",
|
||||
"flowClassName": "com.r3.developers.csdetemplate.utxoexample.workflows.CreateNewChatFlow",
|
||||
"requestData": {
|
||||
"requestBody": {
|
||||
"chatName":"Chat with Bob",
|
||||
"otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB",
|
||||
"message": "Hello Bob"
|
||||
|
@ -1,9 +1,9 @@
|
||||
package com.r3.developers.csdetemplate.utxoexample.workflows;
|
||||
|
||||
import com.r3.developers.csdetemplate.utxoexample.states.ChatState;
|
||||
import net.corda.v5.application.flows.ClientRequestBody;
|
||||
import net.corda.v5.application.flows.ClientStartableFlow;
|
||||
import net.corda.v5.application.flows.CordaInject;
|
||||
import net.corda.v5.application.flows.RPCRequestData;
|
||||
import net.corda.v5.application.flows.RPCStartableFlow;
|
||||
import net.corda.v5.application.marshalling.JsonMarshallingService;
|
||||
import net.corda.v5.base.annotations.Suspendable;
|
||||
import net.corda.v5.base.exceptions.CordaRuntimeException;
|
||||
@ -19,7 +19,7 @@ import static java.util.Objects.*;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
// See Chat CorDapp Design section of the getting started docs for a description of this flow.
|
||||
public class GetChatFlow implements RPCStartableFlow {
|
||||
public class GetChatFlow implements ClientStartableFlow {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(GetChatFlow.class);
|
||||
|
||||
@ -32,7 +32,7 @@ public class GetChatFlow implements RPCStartableFlow {
|
||||
|
||||
@Override
|
||||
@Suspendable
|
||||
public String call(RPCRequestData requestBody) {
|
||||
public String call(ClientRequestBody requestBody) {
|
||||
|
||||
// Obtain the deserialized input arguments to the flow from the requestBody.
|
||||
GetChatFlowArgs flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, GetChatFlowArgs.class);
|
||||
@ -70,7 +70,7 @@ public class GetChatFlow implements RPCStartableFlow {
|
||||
while (moreBackchain) {
|
||||
|
||||
// Obtain the transaction id from the current StateAndRef and fetch the transaction from the vault.
|
||||
SecureHash transactionId = currentStateAndRef.getRef().getTransactionHash();
|
||||
SecureHash transactionId = currentStateAndRef.getRef().getTransactionId();
|
||||
UtxoLedgerTransaction transaction = requireNonNull(
|
||||
ledgerService.findLedgerTransaction(transactionId),
|
||||
"Transaction " + transactionId + " not found."
|
||||
@ -110,7 +110,7 @@ RequestBody for triggering the flow via http-rpc:
|
||||
{
|
||||
"clientRequestId": "get-1",
|
||||
"flowClassName": "com.r3.developers.csdetemplate.utxoexample.workflows.GetChatFlow",
|
||||
"requestData": {
|
||||
"requestBody": {
|
||||
"id":"** fill in id **",
|
||||
"numberOfRecords":"4"
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
package com.r3.developers.csdetemplate.utxoexample.workflows;
|
||||
|
||||
import com.r3.developers.csdetemplate.utxoexample.states.ChatState;
|
||||
import net.corda.v5.application.flows.ClientRequestBody;
|
||||
import net.corda.v5.application.flows.ClientStartableFlow;
|
||||
import net.corda.v5.application.flows.CordaInject;
|
||||
import net.corda.v5.application.flows.RPCRequestData;
|
||||
import net.corda.v5.application.flows.RPCStartableFlow;
|
||||
import net.corda.v5.application.marshalling.JsonMarshallingService;
|
||||
import net.corda.v5.base.annotations.Suspendable;
|
||||
import net.corda.v5.ledger.utxo.StateAndRef;
|
||||
@ -15,7 +15,7 @@ import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
// See Chat CorDapp Design section of the getting started docs for a description of this flow.
|
||||
public class ListChatsFlow implements RPCStartableFlow{
|
||||
public class ListChatsFlow implements ClientStartableFlow {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(ListChatsFlow.class);
|
||||
|
||||
@ -28,7 +28,7 @@ public class ListChatsFlow implements RPCStartableFlow{
|
||||
|
||||
@Suspendable
|
||||
@Override
|
||||
public String call(RPCRequestData requestBody) {
|
||||
public String call(ClientRequestBody requestBody) {
|
||||
|
||||
log.info("ListChatsFlow.call() called");
|
||||
|
||||
@ -53,6 +53,6 @@ RequestBody for triggering the flow via http-rpc:
|
||||
{
|
||||
"clientRequestId": "list-1",
|
||||
"flowClassName": "com.r3.developers.csdetemplate.utxoexample.workflows.ListChatsFlow",
|
||||
"requestData": {}
|
||||
"requestBody": {}
|
||||
}
|
||||
*/
|
@ -2,10 +2,10 @@ package com.r3.developers.csdetemplate.utxoexample.workflows;
|
||||
|
||||
import com.r3.developers.csdetemplate.utxoexample.contracts.ChatContract;
|
||||
import com.r3.developers.csdetemplate.utxoexample.states.ChatState;
|
||||
import net.corda.v5.application.flows.ClientRequestBody;
|
||||
import net.corda.v5.application.flows.ClientStartableFlow;
|
||||
import net.corda.v5.application.flows.CordaInject;
|
||||
import net.corda.v5.application.flows.FlowEngine;
|
||||
import net.corda.v5.application.flows.RPCRequestData;
|
||||
import net.corda.v5.application.flows.RPCStartableFlow;
|
||||
import net.corda.v5.application.marshalling.JsonMarshallingService;
|
||||
import net.corda.v5.application.membership.MemberLookup;
|
||||
import net.corda.v5.base.annotations.Suspendable;
|
||||
@ -26,7 +26,7 @@ import static java.util.Objects.*;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
// See Chat CorDapp Design section of the getting started docs for a description of this flow.
|
||||
public class UpdateChatFlow implements RPCStartableFlow {
|
||||
public class UpdateChatFlow implements ClientStartableFlow {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(UpdateChatFlow.class);
|
||||
|
||||
@ -46,7 +46,7 @@ public class UpdateChatFlow implements RPCStartableFlow {
|
||||
|
||||
@Suspendable
|
||||
@Override
|
||||
public String call(RPCRequestData requestBody) {
|
||||
public String call(ClientRequestBody requestBody) {
|
||||
|
||||
log.info("UpdateNewChatFlow.call() called");
|
||||
|
||||
@ -87,10 +87,10 @@ public class UpdateChatFlow implements RPCStartableFlow {
|
||||
.addCommand(new ChatContract.Update())
|
||||
.addSignatories(newChatState.getParticipants());
|
||||
|
||||
// Convert the transaction builder to a UtxoSignedTransaction and sign with this Vnode's first Ledger key.
|
||||
// Note, toSignedTransaction() is currently a placeholder method, hence being marked as deprecated.
|
||||
@SuppressWarnings("DEPRECATION")
|
||||
UtxoSignedTransaction signedTransaction = txBuilder.toSignedTransaction(myInfo.getLedgerKeys().get(0));
|
||||
// Convert the transaction builder to a UTXOSignedTransaction. Verifies the content of the
|
||||
// UtxoTransactionBuilder and signs the transaction with any required signatories that belong to
|
||||
// the current node.
|
||||
UtxoSignedTransaction signedTransaction = txBuilder.toSignedTransaction();
|
||||
|
||||
// Call FinalizeChatSubFlow which will finalise the transaction.
|
||||
// If successful the flow will return a String of the created transaction id,
|
||||
@ -112,7 +112,7 @@ RequestBody for triggering the flow via http-rpc:
|
||||
{
|
||||
"clientRequestId": "update-1",
|
||||
"flowClassName": "com.r3.developers.csdetemplate.utxoexample.workflows.UpdateChatFlow",
|
||||
"requestData": {
|
||||
"requestBody": {
|
||||
"id":" ** fill in id **",
|
||||
"message": "How are you today?"
|
||||
}
|
||||
|
@ -1,41 +1,41 @@
|
||||
package com.r3.developers.csdetemplate.flowexample.workflows;
|
||||
|
||||
import net.corda.simulator.RequestData;
|
||||
import net.corda.simulator.SimulatedVirtualNode;
|
||||
import net.corda.simulator.Simulator;
|
||||
import net.corda.v5.base.types.MemberX500Name;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class MyFirstFlowTest {
|
||||
// Names picked to match the corda network in config/dev-net.json
|
||||
private MemberX500Name aliceX500 = MemberX500Name.parse("CN=Alice, OU=Test Dept, O=R3, L=London, C=GB");
|
||||
private MemberX500Name bobX500 = MemberX500Name.parse("CN=Bob, OU=Test Dept, O=R3, L=London, C=GB");
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void test_that_MyFirstFLow_returns_correct_message() {
|
||||
// Instantiate an instance of the simulator.
|
||||
Simulator simulator = new Simulator();
|
||||
|
||||
// Create Alice's and Bob's virtual nodes, including the classes of the flows which will be registered on each node.
|
||||
// Don't assign Bob's virtual node to a value. You don't need it for this particular test.
|
||||
SimulatedVirtualNode aliceVN = simulator.createVirtualNode(aliceX500, MyFirstFlow.class);
|
||||
simulator.createVirtualNode(bobX500, MyFirstFlowResponder.class);
|
||||
|
||||
// Create an instance of the MyFirstFlowStartArgs which contains the request arguments for starting the flow.
|
||||
MyFirstFlowStartArgs myFirstFlowStartArgs = new MyFirstFlowStartArgs(bobX500);
|
||||
|
||||
// Create a requestData object.
|
||||
RequestData requestData = RequestData.Companion.create(
|
||||
"request no 1", // A unique reference for the instance of the flow request.
|
||||
MyFirstFlow.class, // The name of the flow class which is to be started.
|
||||
myFirstFlowStartArgs // The object which contains the start arguments of the flow.
|
||||
);
|
||||
|
||||
// Call the flow on Alice's virtual node and capture the response.
|
||||
String flowResponse = aliceVN.callFlow(requestData);
|
||||
|
||||
// Check that the flow has returned the expected string.
|
||||
assert(flowResponse.equals("Hello Alice, best wishes from Bob"));
|
||||
}
|
||||
}
|
||||
//package com.r3.developers.csdetemplate.flowexample.workflows;
|
||||
//
|
||||
//import net.corda.simulator.RequestData;
|
||||
//import net.corda.simulator.SimulatedVirtualNode;
|
||||
//import net.corda.simulator.Simulator;
|
||||
//import net.corda.v5.base.types.MemberX500Name;
|
||||
//import org.junit.jupiter.api.Test;
|
||||
//
|
||||
//class MyFirstFlowTest {
|
||||
// // Names picked to match the corda network in config/dev-net.json
|
||||
// private MemberX500Name aliceX500 = MemberX500Name.parse("CN=Alice, OU=Test Dept, O=R3, L=London, C=GB");
|
||||
// private MemberX500Name bobX500 = MemberX500Name.parse("CN=Bob, OU=Test Dept, O=R3, L=London, C=GB");
|
||||
//
|
||||
// @Test
|
||||
// @SuppressWarnings("unchecked")
|
||||
// public void test_that_MyFirstFLow_returns_correct_message() {
|
||||
// // Instantiate an instance of the simulator.
|
||||
// Simulator simulator = new Simulator();
|
||||
//
|
||||
// // Create Alice's and Bob's virtual nodes, including the classes of the flows which will be registered on each node.
|
||||
// // Don't assign Bob's virtual node to a value. You don't need it for this particular test.
|
||||
// SimulatedVirtualNode aliceVN = simulator.createVirtualNode(aliceX500, MyFirstFlow.class);
|
||||
// simulator.createVirtualNode(bobX500, MyFirstFlowResponder.class);
|
||||
//
|
||||
// // Create an instance of the MyFirstFlowStartArgs which contains the request arguments for starting the flow.
|
||||
// MyFirstFlowStartArgs myFirstFlowStartArgs = new MyFirstFlowStartArgs(bobX500);
|
||||
//
|
||||
// // Create a requestData object.
|
||||
// RequestData requestData = RequestData.Companion.create(
|
||||
// "request no 1", // A unique reference for the instance of the flow request.
|
||||
// MyFirstFlow.class, // The name of the flow class which is to be started.
|
||||
// myFirstFlowStartArgs // The object which contains the start arguments of the flow.
|
||||
// );
|
||||
//
|
||||
// // Call the flow on Alice's virtual node and capture the response.
|
||||
// String flowResponse = aliceVN.callFlow(requestData);
|
||||
//
|
||||
// // Check that the flow has returned the expected string.
|
||||
// assert(flowResponse.equals("Hello Alice, best wishes from Bob"));
|
||||
// }
|
||||
//}
|
||||
|
Loading…
Reference in New Issue
Block a user