From c57f10f5f6aedff77d39ce25f41c8e5f2e6da6c4 Mon Sep 17 00:00:00 2001 From: andrew-coleman Date: Wed, 24 Jul 2019 10:17:55 +0100 Subject: [PATCH] FGJ-4 commercial paper client sample commercial paper sample for Java Gateway SDK (client app) Change-Id: I80c6b9dbc36631004903244a20e6a492138c7751 Signed-off-by: andrew-coleman --- commercial-paper/.classpath | 23 ++++ commercial-paper/.gitignore | 2 + .../org/example/ledger/api/Field.java | 5 + .../org/example/ledger/api/Property.java | 10 ++ .../org/example/ledger/api/State.java | 125 ++++++++++++++++++ .../org/digibank/AddToWallet.java | 44 ++++++ .../application-java/org/digibank/Buy.java | 62 +++++++++ .../application-java/org/digibank/Redeem.java | 62 +++++++++ .../digibank/gateway/networkConnection.yaml | 3 + .../org/magnetocorp/AddToWallet.java | 44 ++++++ .../org/magnetocorp/Issue.java | 62 +++++++++ .../gateway/networkConnection.yaml | 3 + .../org/papernet/CommercialPaper.java | 76 +++++++++++ commercial-paper/pom.xml | 33 +++++ 14 files changed, 554 insertions(+) create mode 100644 commercial-paper/.classpath create mode 100644 commercial-paper/ledger-java-api/org/example/ledger/api/Field.java create mode 100644 commercial-paper/ledger-java-api/org/example/ledger/api/Property.java create mode 100644 commercial-paper/ledger-java-api/org/example/ledger/api/State.java create mode 100644 commercial-paper/organization/digibank/application-java/org/digibank/AddToWallet.java create mode 100644 commercial-paper/organization/digibank/application-java/org/digibank/Buy.java create mode 100644 commercial-paper/organization/digibank/application-java/org/digibank/Redeem.java create mode 100644 commercial-paper/organization/magnetocorp/application-java/org/magnetocorp/AddToWallet.java create mode 100644 commercial-paper/organization/magnetocorp/application-java/org/magnetocorp/Issue.java create mode 100644 commercial-paper/organization/shared/lib-java/org/papernet/CommercialPaper.java create mode 100644 commercial-paper/pom.xml diff --git a/commercial-paper/.classpath b/commercial-paper/.classpath new file mode 100644 index 00000000..a27a7889 --- /dev/null +++ b/commercial-paper/.classpath @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/commercial-paper/.gitignore b/commercial-paper/.gitignore index 14c40052..3ed60ea0 100644 --- a/commercial-paper/.gitignore +++ b/commercial-paper/.gitignore @@ -6,3 +6,5 @@ organization/digibank/contract/node_modules/ organization/digibank/identity/user/ package-lock.json .vscode +/target/ +bin/ diff --git a/commercial-paper/ledger-java-api/org/example/ledger/api/Field.java b/commercial-paper/ledger-java-api/org/example/ledger/api/Field.java new file mode 100644 index 00000000..25d81f67 --- /dev/null +++ b/commercial-paper/ledger-java-api/org/example/ledger/api/Field.java @@ -0,0 +1,5 @@ +package org.example.ledger.api; + +public @interface Field { + public String name() default ""; +} diff --git a/commercial-paper/ledger-java-api/org/example/ledger/api/Property.java b/commercial-paper/ledger-java-api/org/example/ledger/api/Property.java new file mode 100644 index 00000000..5453d4f4 --- /dev/null +++ b/commercial-paper/ledger-java-api/org/example/ledger/api/Property.java @@ -0,0 +1,10 @@ +package org.example.ledger.api; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(value=RetentionPolicy.RUNTIME) +public @interface Property { + public String name() default ""; + public boolean key() default false; +} diff --git a/commercial-paper/ledger-java-api/org/example/ledger/api/State.java b/commercial-paper/ledger-java-api/org/example/ledger/api/State.java new file mode 100644 index 00000000..5b43ab3c --- /dev/null +++ b/commercial-paper/ledger-java-api/org/example/ledger/api/State.java @@ -0,0 +1,125 @@ +/* +SPDX-License-Identifier: Apache-2.0 +*/ + +package org.example.ledger.api; + +import java.io.StringReader; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonNumber; +import javax.json.JsonObject; +import javax.json.JsonReader; +import javax.json.JsonString; +import javax.json.JsonValue; + +public class State { + private final String key; + private final Map properties; + + protected final Supplier keyBuilder(String[] parts) { + return () -> Arrays.asList(parts).stream().map(part -> getProperty(part)).collect(Collectors.joining(":")); + }; + + public State(byte[] buffer) { + this.properties = deserialize(buffer); + List keyParts = new ArrayList(); + for (Field field: getStateFields()) { + field.setAccessible(true); + try { + String value = properties.get(getPropertyName(field)); + field.set(this, value); + boolean isKey = field.getAnnotation(Property.class).key(); + if(isKey) { + keyParts.add(value); + } + } catch (IllegalArgumentException | IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + key = String.join(":", keyParts); + System.out.println(key); + } + + private static Map deserialize(byte[] buffer) { + JsonObject json = toJSON(buffer); + Map result = json.entrySet().stream().collect( + Collectors.toMap(e -> e.getKey(), e -> jsonString(e.getValue()))); + return result; + } + + private static String jsonString(JsonValue value) { + if(value.getValueType() == JsonValue.ValueType.STRING) { + return ((JsonString)value).getString(); + } + return value.toString(); + } + + private static JsonObject toJSON(byte[] buffer) { + String payload = new String(buffer); + String payloadData; + try (JsonReader reader = Json.createReader(new StringReader(payload))) { + JsonObject json = reader.readObject(); + JsonArray arr = json.getJsonArray("data"); + byte[] codepoints = new byte[arr.size()]; + for(int i = 0; i < arr.size(); i++) { + codepoints[i] = (byte) ((JsonNumber)arr.get(i)).intValue(); + } + payloadData = new String(codepoints); + } + try (JsonReader reader = Json.createReader(new StringReader(payloadData))) { + JsonObject json = reader.readObject(); + return json; + } + } + + private static String getPropertyName(java.lang.reflect.Field field) { + String name = field.getAnnotation(Property.class).name(); + if (name.isEmpty()) { + name = field.getName(); + } + return name; + } + + private String getProperty(String key) { + return properties.get(key); + } + + private List getStateFields() { + Class clazz = this.getClass().asSubclass(this.getClass()); + return Arrays.asList(clazz.getDeclaredFields()) + .stream() + .filter(field -> field.isAnnotationPresent(Property.class)) + .collect(Collectors.toList()); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getName()); + builder.append(" ["); + for (Field field: getStateFields()) { + field.setAccessible(true); + builder.append(field.getName()); + builder.append("="); + try { + builder.append(field.get(this)); + } catch (IllegalArgumentException | IllegalAccessException e) { + // ignore for now + } + builder.append(", "); + } + builder.setCharAt(builder.length() - 2, ']'); + return builder.toString(); + } + +} diff --git a/commercial-paper/organization/digibank/application-java/org/digibank/AddToWallet.java b/commercial-paper/organization/digibank/application-java/org/digibank/AddToWallet.java new file mode 100644 index 00000000..702ed058 --- /dev/null +++ b/commercial-paper/organization/digibank/application-java/org/digibank/AddToWallet.java @@ -0,0 +1,44 @@ +/* +SPDX-License-Identifier: Apache-2.0 +*/ + +package org.digibank; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.hyperledger.fabric.gateway.GatewayException; +import org.hyperledger.fabric.gateway.Wallet; +import org.hyperledger.fabric.gateway.Wallet.Identity; + +public class AddToWallet { + + public static void main(String[] args) { + try { + // A wallet stores a collection of identities + Path walletPath = Paths.get("organization", "digibank", "identity", "user", "balaji", "wallet"); + Wallet wallet = Wallet.createFileSystemWallet(walletPath); + + // Location of credentials to be stored in the wallet + Path credentialPath = Paths.get("..", "basic-network", "crypto-config", + "peerOrganizations", "org1.example.com", "users", "Admin@org1.example.com", "msp"); + Path certificatePem = credentialPath.resolve(Paths.get("signcerts", + "Admin@org1.example.com-cert.pem")); + Path privateKey = credentialPath.resolve(Paths.get("keystore", + "cd96d5260ad4757551ed4a5a991e62130f8008a0bf996e4e4b84cd097a747fec_sk")); + + // Load credentials into wallet + String identityLabel = "Admin@org1.example.com"; + Identity identity = Identity.createIdentity("Org1MSP", Files.newBufferedReader(certificatePem), Files.newBufferedReader(privateKey)); + + wallet.put(identityLabel, identity); + + } catch (IOException | GatewayException e) { + System.err.println("Error adding to wallet"); + e.printStackTrace(); + } + } + +} diff --git a/commercial-paper/organization/digibank/application-java/org/digibank/Buy.java b/commercial-paper/organization/digibank/application-java/org/digibank/Buy.java new file mode 100644 index 00000000..0dc0a306 --- /dev/null +++ b/commercial-paper/organization/digibank/application-java/org/digibank/Buy.java @@ -0,0 +1,62 @@ +/* +SPDX-License-Identifier: Apache-2.0 +*/ + +package org.digibank; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.TimeoutException; + +import org.hyperledger.fabric.gateway.Contract; +import org.hyperledger.fabric.gateway.Gateway; +import org.hyperledger.fabric.gateway.GatewayException; +import org.hyperledger.fabric.gateway.Network; +import org.hyperledger.fabric.gateway.Wallet; +import org.papernet.CommercialPaper; + +public class Buy { + + public static void main(String[] args) { + Gateway.Builder builder = Gateway.createBuilder(); + + try { + // A wallet stores a collection of identities + Path walletPath = Paths.get("organization", "digibank", "identity", "user", "balaji", "wallet"); + Wallet wallet = Wallet.createFileSystemWallet(walletPath); + + String userName = "Admin@org1.example.com"; + + Path connectionProfile = Paths.get("organization", "digibank", "gateway", "networkConnection.yaml"); + + // Set connection options on the gateway builder + builder.identity(wallet, userName).networkConfig(connectionProfile).discovery(false); + + // Connect to gateway using application specified parameters + try(Gateway gateway = builder.connect()) { + + // Access PaperNet network + System.out.println("Use network channel: mychannel."); + Network network = gateway.getNetwork("mychannel"); + + // Get addressability to commercial paper contract + System.out.println("Use org.papernet.commercialpaper smart contract."); + Contract contract = network.getContract("papercontract", "org.papernet.commercialpaper"); + + // Buy commercial paper + System.out.println("Submit commercial paper buy transaction."); + byte[] response = contract.submitTransaction("buy", "MagnetoCorp", "00001", "MagnetoCorp", "DigiBank", "4900000", "2020-05-31"); + + // Process response + System.out.println("Process buy transaction response."); + CommercialPaper paper = CommercialPaper.create(response); + System.out.println(paper); + } + } catch (GatewayException | IOException | TimeoutException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +} diff --git a/commercial-paper/organization/digibank/application-java/org/digibank/Redeem.java b/commercial-paper/organization/digibank/application-java/org/digibank/Redeem.java new file mode 100644 index 00000000..b96876d6 --- /dev/null +++ b/commercial-paper/organization/digibank/application-java/org/digibank/Redeem.java @@ -0,0 +1,62 @@ +/* +SPDX-License-Identifier: Apache-2.0 +*/ + +package org.digibank; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.TimeoutException; + +import org.hyperledger.fabric.gateway.Contract; +import org.hyperledger.fabric.gateway.Gateway; +import org.hyperledger.fabric.gateway.GatewayException; +import org.hyperledger.fabric.gateway.Network; +import org.hyperledger.fabric.gateway.Wallet; +import org.papernet.CommercialPaper; + +public class Redeem { + + public static void main(String[] args) { + Gateway.Builder builder = Gateway.createBuilder(); + + try { + // A wallet stores a collection of identities + Path walletPath = Paths.get("organization", "digibank", "identity", "user", "balaji", "wallet"); + Wallet wallet = Wallet.createFileSystemWallet(walletPath); + + String userName = "Admin@org1.example.com"; + + Path connectionProfile = Paths.get("organization", "digibank", "gateway", "networkConnection.yaml"); + + // Set connection options on the gateway builder + builder.identity(wallet, userName).networkConfig(connectionProfile).discovery(false); + + // Connect to gateway using application specified parameters + try(Gateway gateway = builder.connect()) { + + // Access PaperNet network + System.out.println("Use network channel: mychannel."); + Network network = gateway.getNetwork("mychannel"); + + // Get addressability to commercial paper contract + System.out.println("Use org.papernet.commercialpaper smart contract."); + Contract contract = network.getContract("papercontract", "org.papernet.commercialpaper"); + + // Redeem commercial paper + System.out.println("Submit commercial paper redeem transaction."); + byte[] response = contract.submitTransaction("redeem", "MagnetoCorp", "00001", "DigiBank", "2020-11-30"); + + // Process response + System.out.println("Process redeem transaction response."); + CommercialPaper paper = CommercialPaper.create(response); + System.out.println(paper); + } + } catch (GatewayException | IOException | TimeoutException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +} diff --git a/commercial-paper/organization/digibank/gateway/networkConnection.yaml b/commercial-paper/organization/digibank/gateway/networkConnection.yaml index db50f318..8da20358 100644 --- a/commercial-paper/organization/digibank/gateway/networkConnection.yaml +++ b/commercial-paper/organization/digibank/gateway/networkConnection.yaml @@ -26,6 +26,9 @@ description: "The basic network" # version: "1.0" +client: + organization: Org1 + # # [Optional]. But most apps would have this section so that channel objects can be constructed # based on the content below. If an app is creating channels, then it likely will not need this diff --git a/commercial-paper/organization/magnetocorp/application-java/org/magnetocorp/AddToWallet.java b/commercial-paper/organization/magnetocorp/application-java/org/magnetocorp/AddToWallet.java new file mode 100644 index 00000000..dba3e5e3 --- /dev/null +++ b/commercial-paper/organization/magnetocorp/application-java/org/magnetocorp/AddToWallet.java @@ -0,0 +1,44 @@ +/* +SPDX-License-Identifier: Apache-2.0 +*/ + +package org.magnetocorp; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.hyperledger.fabric.gateway.GatewayException; +import org.hyperledger.fabric.gateway.Wallet; +import org.hyperledger.fabric.gateway.Wallet.Identity; + +public class AddToWallet { + + public static void main(String[] args) { + try { + // A wallet stores a collection of identities + Path walletPath = Paths.get("organization", "magnetocorp", "identity", "user", "isabella", "wallet"); + Wallet wallet = Wallet.createFileSystemWallet(walletPath); + + // Location of credentials to be stored in the wallet + Path credentialPath = Paths.get("..", "basic-network", "crypto-config", + "peerOrganizations", "org1.example.com", "users", "User1@org1.example.com", "msp"); + Path certificatePem = credentialPath.resolve(Paths.get("signcerts", + "User1@org1.example.com-cert.pem")); + Path privateKey = credentialPath.resolve(Paths.get("keystore", + "c75bd6911aca808941c3557ee7c97e90f3952e379497dc55eb903f31b50abc83_sk")); + + // Load credentials into wallet + String identityLabel = "User1@org1.example.com"; + Identity identity = Identity.createIdentity("Org1MSP", Files.newBufferedReader(certificatePem), Files.newBufferedReader(privateKey)); + + wallet.put(identityLabel, identity); + + } catch (IOException | GatewayException e) { + System.err.println("Error adding to wallet"); + e.printStackTrace(); + } + } + +} diff --git a/commercial-paper/organization/magnetocorp/application-java/org/magnetocorp/Issue.java b/commercial-paper/organization/magnetocorp/application-java/org/magnetocorp/Issue.java new file mode 100644 index 00000000..e196b782 --- /dev/null +++ b/commercial-paper/organization/magnetocorp/application-java/org/magnetocorp/Issue.java @@ -0,0 +1,62 @@ +/* +SPDX-License-Identifier: Apache-2.0 +*/ + +package org.magnetocorp; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.TimeoutException; + +import org.hyperledger.fabric.gateway.Contract; +import org.hyperledger.fabric.gateway.Gateway; +import org.hyperledger.fabric.gateway.GatewayException; +import org.hyperledger.fabric.gateway.Network; +import org.hyperledger.fabric.gateway.Wallet; +import org.papernet.CommercialPaper; + +public class Issue { + + public static void main(String[] args) { + Gateway.Builder builder = Gateway.createBuilder(); + + try { + // A wallet stores a collection of identities + Path walletPath = Paths.get("organization", "magnetocorp", "identity", "user", "isabella", "wallet"); + Wallet wallet = Wallet.createFileSystemWallet(walletPath); + + String userName = "User1@org1.example.com"; + + Path connectionProfile = Paths.get("organization", "magnetocorp", "gateway", "networkConnection.yaml"); + + // Set connection options on the gateway builder + builder.identity(wallet, userName).networkConfig(connectionProfile).discovery(false); + + // Connect to gateway using application specified parameters + try(Gateway gateway = builder.connect()) { + + // Access PaperNet network + System.out.println("Use network channel: mychannel."); + Network network = gateway.getNetwork("mychannel"); + + // Get addressability to commercial paper contract + System.out.println("Use org.papernet.commercialpaper smart contract."); + Contract contract = network.getContract("papercontract", "org.papernet.commercialpaper"); + + // Issue commercial paper + System.out.println("Submit commercial paper issue transaction."); + byte[] response = contract.submitTransaction("issue", "MagnetoCorp", "00001", "2020-05-31", "2020-11-30", "5000000"); + + // Process response + System.out.println("Process issue transaction response."); + CommercialPaper paper = CommercialPaper.create(response); + System.out.println(paper); + } + } catch (GatewayException | IOException | TimeoutException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +} diff --git a/commercial-paper/organization/magnetocorp/gateway/networkConnection.yaml b/commercial-paper/organization/magnetocorp/gateway/networkConnection.yaml index db50f318..8da20358 100644 --- a/commercial-paper/organization/magnetocorp/gateway/networkConnection.yaml +++ b/commercial-paper/organization/magnetocorp/gateway/networkConnection.yaml @@ -26,6 +26,9 @@ description: "The basic network" # version: "1.0" +client: + organization: Org1 + # # [Optional]. But most apps would have this section so that channel objects can be constructed # based on the content below. If an app is creating channels, then it likely will not need this diff --git a/commercial-paper/organization/shared/lib-java/org/papernet/CommercialPaper.java b/commercial-paper/organization/shared/lib-java/org/papernet/CommercialPaper.java new file mode 100644 index 00000000..7fb3016c --- /dev/null +++ b/commercial-paper/organization/shared/lib-java/org/papernet/CommercialPaper.java @@ -0,0 +1,76 @@ +package org.papernet; + +import org.example.ledger.api.Property; +import org.example.ledger.api.State; + +public class CommercialPaper extends State { + private static final String ISSUED = "1"; + private static final String TRADING = "2"; + private static final String REDEEMED = "3"; + + @Property(key=true) + private String issuer; + + @Property + private String owner; + + @Property(key=true) + private String paperNumber; + + @Property + private String issueDateTime; + + @Property + private String maturityDateTime; + + @Property + private String faceValue; + + @Property(name="currentState") + private String status; + + private CommercialPaper(byte[] buffer) { + super(buffer); + } + + public static CommercialPaper create(byte[] buffer) { + return new CommercialPaper(buffer); + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getIssuer() { + return issuer; + } + + public void setIssued() { + status = ISSUED; + } + + public void setTrading() { + status = TRADING; + } + + public void setRedeemed() { + status = REDEEMED; + } + + public boolean isIssued() { + return status == ISSUED; + } + + public boolean isTrading() { + return status == TRADING; + } + + public boolean isRedeemed() { + return status == REDEEMED; + } + +} diff --git a/commercial-paper/pom.xml b/commercial-paper/pom.xml new file mode 100644 index 00000000..f67d8f6c --- /dev/null +++ b/commercial-paper/pom.xml @@ -0,0 +1,33 @@ + + 4.0.0 + commercial-paper + commercial-paper + 0.0.1-SNAPSHOT + + organization/digibank/application-java + + + maven-compiler-plugin + 3.8.0 + + 1.8 + 1.8 + + + + + + + hyperledger + Hyperledger Nexus + https://nexus.hyperledger.org/content/repositories/snapshots + + + + + org.hyperledger.fabric + fabric-gateway-java + 1.4.0-SNAPSHOT + + + \ No newline at end of file