mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 15:35:09 +00:00
For some signing implementations, such as ed25519, a non-default hash implementation must be specified when creating the Gateway connection in client applications. Rather than relying on the default hash algorithm, it is probably good practice in general to specify an algorithm that is compatible with your signing implementation. This change explicitly specifies the hash algorithm to raise visibility of the option to select the hash algorithm. Signed-off-by: Mark S. Lewis <Mark.S.Lewis@outlook.com>
175 lines
6.8 KiB
Java
175 lines
6.8 KiB
Java
/*
|
|
* Copyright IBM Corp. All Rights Reserved.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import com.google.gson.Gson;
|
|
import com.google.gson.GsonBuilder;
|
|
import com.google.gson.JsonParser;
|
|
import io.grpc.Status;
|
|
import org.hyperledger.fabric.client.ChaincodeEvent;
|
|
import org.hyperledger.fabric.client.CloseableIterator;
|
|
import org.hyperledger.fabric.client.CommitException;
|
|
import org.hyperledger.fabric.client.CommitStatusException;
|
|
import org.hyperledger.fabric.client.Contract;
|
|
import org.hyperledger.fabric.client.EndorseException;
|
|
import org.hyperledger.fabric.client.Gateway;
|
|
import org.hyperledger.fabric.client.GatewayRuntimeException;
|
|
import org.hyperledger.fabric.client.Hash;
|
|
import org.hyperledger.fabric.client.Network;
|
|
import org.hyperledger.fabric.client.SubmitException;
|
|
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.time.Instant;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
public final class App implements AutoCloseable {
|
|
private static final String channelName = "mychannel";
|
|
private static final String chaincodeName = "events";
|
|
|
|
private final Network network;
|
|
private final Contract contract;
|
|
private final String assetId = "asset" + Instant.now().toEpochMilli();
|
|
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
|
private final ExecutorService executor = Executors.newCachedThreadPool();
|
|
|
|
public static void main(final String[] args) throws Exception {
|
|
var grpcChannel = Connections.newGrpcConnection();
|
|
var builder = Gateway.newInstance()
|
|
.identity(Connections.newIdentity())
|
|
.signer(Connections.newSigner())
|
|
.hash(Hash.SHA256)
|
|
.connection(grpcChannel)
|
|
.evaluateOptions(options -> options.withDeadlineAfter(5, TimeUnit.SECONDS))
|
|
.endorseOptions(options -> options.withDeadlineAfter(15, TimeUnit.SECONDS))
|
|
.submitOptions(options -> options.withDeadlineAfter(5, TimeUnit.SECONDS))
|
|
.commitStatusOptions(options -> options.withDeadlineAfter(1, TimeUnit.MINUTES));
|
|
|
|
try (var gateway = builder.connect(); var app = new App(gateway)) {
|
|
app.run();
|
|
} finally {
|
|
grpcChannel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
|
|
}
|
|
}
|
|
|
|
public App(final Gateway gateway) {
|
|
network = gateway.getNetwork(channelName);
|
|
contract = network.getContract(chaincodeName);
|
|
}
|
|
|
|
public void run() throws EndorseException, SubmitException, CommitStatusException, CommitException {
|
|
// Listen for events emitted by subsequent transactions, stopping when the try-with-resources block exits
|
|
try (var eventSession = startChaincodeEventListening()) {
|
|
var firstBlockNumber = createAsset();
|
|
updateAsset();
|
|
transferAsset();
|
|
deleteAsset();
|
|
|
|
// Replay events from the block containing the first transaction
|
|
replayChaincodeEvents(firstBlockNumber);
|
|
}
|
|
}
|
|
|
|
private CloseableIterator<ChaincodeEvent> startChaincodeEventListening() {
|
|
System.out.println("\n*** Start chaincode event listening");
|
|
|
|
var eventIter = network.getChaincodeEvents(chaincodeName);
|
|
executor.execute(() -> readEvents(eventIter));
|
|
|
|
return eventIter;
|
|
}
|
|
|
|
private void readEvents(final CloseableIterator<ChaincodeEvent> eventIter) {
|
|
try {
|
|
eventIter.forEachRemaining(event -> {
|
|
var payload = prettyJson(event.getPayload());
|
|
System.out.println("\n<-- Chaincode event received: " + event.getEventName() + " - " + payload);
|
|
});
|
|
} catch (GatewayRuntimeException e) {
|
|
if (e.getStatus().getCode() != Status.Code.CANCELLED) {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
private String prettyJson(final byte[] json) {
|
|
return prettyJson(new String(json, StandardCharsets.UTF_8));
|
|
}
|
|
|
|
private String prettyJson(final String json) {
|
|
var parsedJson = JsonParser.parseString(json);
|
|
return gson.toJson(parsedJson);
|
|
}
|
|
|
|
private long createAsset() throws EndorseException, SubmitException, CommitStatusException {
|
|
System.out.println("\n--> Submit transaction: CreateAsset, " + assetId + " owned by Sam with appraised value 100");
|
|
|
|
var commit = contract.newProposal("CreateAsset")
|
|
.addArguments(assetId, "blue", "10", "Sam", "100")
|
|
.build()
|
|
.endorse()
|
|
.submitAsync();
|
|
|
|
var status = commit.getStatus();
|
|
if (!status.isSuccessful()) {
|
|
throw new RuntimeException("failed to commit transaction with status code " + status.getCode());
|
|
}
|
|
|
|
System.out.println("\n*** CreateAsset committed successfully");
|
|
|
|
return status.getBlockNumber();
|
|
}
|
|
|
|
private void updateAsset() throws EndorseException, SubmitException, CommitStatusException, CommitException {
|
|
System.out.println("\n--> Submit transaction: UpdateAsset, " + assetId + " update appraised value to 200");
|
|
|
|
contract.submitTransaction("UpdateAsset", assetId, "blue", "10", "Sam", "200");
|
|
|
|
System.out.println("\n*** UpdateAsset committed successfully");
|
|
}
|
|
|
|
private void transferAsset() throws EndorseException, SubmitException, CommitStatusException, CommitException {
|
|
System.out.println("\n--> Submit transaction: TransferAsset, " + assetId + " to Mary");
|
|
|
|
contract.submitTransaction("TransferAsset", assetId, "Mary");
|
|
|
|
System.out.println("\n*** TransferAsset committed successfully");
|
|
}
|
|
|
|
private void deleteAsset() throws EndorseException, SubmitException, CommitStatusException, CommitException {
|
|
System.out.println("\n--> Submit transaction: DeleteAsset, " + assetId);
|
|
|
|
contract.submitTransaction("DeleteAsset", assetId);
|
|
|
|
System.out.println("\n*** DeleteAsset committed successfully");
|
|
}
|
|
|
|
private void replayChaincodeEvents(final long startBlock) {
|
|
System.out.println("\n*** Start chaincode event replay");
|
|
|
|
var request = network.newChaincodeEventsRequest(chaincodeName)
|
|
.startBlock(startBlock)
|
|
.build();
|
|
|
|
try (var eventIter = request.getEvents()) {
|
|
while (eventIter.hasNext()) {
|
|
var event = eventIter.next();
|
|
var payload = prettyJson(event.getPayload());
|
|
System.out.println("\n<-- Chaincode event replayed: " + event.getEventName() + " - " + payload);
|
|
|
|
if (event.getEventName().equals("DeleteAsset")) {
|
|
// Reached the last submitted transaction so break to close the iterator and stop listening for events
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void close() throws Exception {
|
|
executor.shutdownNow();
|
|
}
|
|
}
|