gava
gava

Reputation: 79

Hyperledger Fabric: check if the transaction has been committed to the ledger

I have three client application that uses respectively the Java, Node.js and Go SDKs to interact with my blockchain Fabric. Using them, I can query and update the ledger successfully.

Now I want to measure the latency during update of the ledger. So, I was thinking to do it taking a timestamp before the submission of the request and another one after that the transaction has been successfully committed to the ledger, and then compute the difference.

My problem is that I can't find any complete documentation about SDK's API for Java, Go and Node.js, so I don't know if, when the method of submission returns, I can consider the transaction as correctly commited to the ledger.

This are my three client's code. Java:

NetworkConfig ccp = NetworkConfig.fromJsonFile(networkConfigPath.toFile());
// initialize default cryptosuite and setup the client
CryptoSuite cryptoSuite = CryptoSuite.Factory.getCryptoSuite();
HFClient client = HFClient.createNewInstance();
client.setCryptoSuite(cryptoSuite);

Channel channel = client.loadChannelFromConfig(ccp.getChannelNames().iterator().next(), ccp);
channel.initialize();

TransactionProposalRequest transactionProposal = client.newTransactionProposalRequest();
// build chaincode id providing the chaincode name
ChaincodeID mychaincodeID = ChaincodeID.newBuilder().setName("mychaincode").build();
transactionProposal.setChaincodeID(mychaincodeID);
// calling chaincode function
transactionProposal.setFcn("mymethod");
transactionProposal.setArgs("a1");

Collection<ProposalResponse> res = channel.sendTransactionProposal(transactionProposal);
channel.sendTransaction(res);

Node.js:

const gateway = new Gateway();
await gateway.connect(ccp, { wallet: wallet, identity: userName, discovery: { enabled: false } });

// Get the network (channel) our contract is deployed to.
const network = await gateway.getNetwork('mychannel');

// Get the contract from the network.
const contract = network.getContract('mychaincode');

const result = await contract.submitTransaction('mymethod', 'a1');

Go:

sdk, err := fabsdk.New(config.FromFile(configFile))
if err != nil {
    fmt.Printf("failed to create SDK: %v\n", err)
    return
}
fmt.Println("SDK created")

// Prepare channel client context using client context
clientChannelContext := sdk.ChannelContext(channelID, fabsdk.WithUser(userName), fabsdk.WithOrg(orgName))
// ChannelClient is used to query and execute transactions
client, err := channel.New(clientChannelContext)
if err != nil {
    fmt.Printf("failed to create new channel client: %v\n", err)
    return
}
fmt.Println("channel client created")

response, err := client.Execute(channel.Request{ChaincodeID: ccID, Fcn: "mymethod", Args: [][]byte{[]byte("a1")}}, channel.WithRetry(retry.DefaultChannelOpts))
if err != nil {
    fmt.Printf("failed to execute the invoke function: %v\n", err)
} else {
    fmt.Println("Proposal responses: ")
    for _, element := range response.Responses {
        fmt.Printf("Endorser: %s Status: %d ChaincodeStatus: %d\n", element.Endorser, element.Status, element.ChaincodeStatus)
    }
    fmt.Println("chaincode transaction completed: " + string(response.Payload))
}
// Close SDK
sdk.Close()

These codes work. My problem is: Can I am sure that after the lines

channel.sendTransaction(res)

(in Java)

const result = await contract.submitTransaction('mymethod', 'a1');

(in Node.js)

response, err := client.Execute(channel.Request{ChaincodeID: ccID, Fcn: "mymethod", Args: [][]byte{[]byte("a1")}}, channel.WithRetry(retry.DefaultChannelOpts))

(in Go) the transaction has been committed to the ledger?

I have only found that on docs:

"Submit a transaction to the ledger. The transaction function name will be evaluated on the endorsing peers and then submitted to the ordering service for committing to the ledger." for submitTransaction in Node.js at https://fabric-sdk-node.github.io/release-1.4/module-fabric-network.Contract.html#submitTransaction__anchor

and

"Execute prepares and executes transaction using request and optional request options" for Execute in Go at https://godoc.org/github.com/hyperledger/fabric-sdk-go/pkg/client/channel#Client.Execute

For Java I can't find documentation... and I am not sure also about Node.js and Go.

Upvotes: 3

Views: 3209

Answers (3)

Rodolfo Leal
Rodolfo Leal

Reputation: 537

I think the better way to perform that is to use Fabric EventHub,that way you register a listener and receives a event when it's committed. Using queries can lead you to deal inconsistent results and to perform retries for delayed transactions.

The code snippet below can be used with NodeSDK and I think will be useful, further examples and documentation can be found here: https://fabric-sdk-node.github.io/release-1.4/tutorial-listening-to-events.html

var options = {
    wallet_path: path.join(__dirname, './creds'),
    user_id: 'PeerAdmin',
    channel_id: 'mychannel',
    chaincode_id: 'fabcar',
    peer_url: 'grpc://localhost:7051',
    event_url: 'grpc://localhost:7053',
    orderer_url: 'grpc://localhost:7050'
};



    let eh = client.newEventHub();
    eh.setPeerAddr(options.event_url);
    eh.connect();


    let txPromise = new Promise((resolve, reject) => {
        let handle = setTimeout(() => {
            eh.disconnect();
            reject();
        }, 30000);

        eh.registerTxEvent(transactionID, (tx, code) => {
            clearTimeout(handle);
            eh.unregisterTxEvent(transactionID);
            eh.disconnect();

            if (code !== 'VALID') {
                console.error(
                    'The transaction was invalid, code = ' + code);
                reject();
            } else {
                console.log(
                    'The transaction has been committed on peer ' +
                    eh._ep._endpoint.addr);
                resolve();
            }
        });
    });
    eventPromises.push(txPromise);

Upvotes: 1

Rodolfo Leal
Rodolfo Leal

Reputation: 537

One more answer to help who maybe wants this feature from CLI.

By default the CLI return success when the order receives the transaction.

To wait until the commit on peer chaincode invoke commnad add the flag --waitForEvent

That way the cli will wait for the commit events from peers.

Hope that helps.

Upvotes: 2

gava
gava

Reputation: 79

I think I solved. In each client application, I tried adding a query to the ledger the line after the request. The result of this test is that the Node.js and Go version works well: after

const result = await contract.submitTransaction('mymethod', 'a1');

and

response, err := client.Execute(channel.Request{ChaincodeID: ccID, Fcn: "mymethod", Args: [][]byte{[]byte("a1")}}, channel.WithRetry(retry.DefaultChannelOpts))

the query result is ok, I get the correct new value. So this means that the ledger is correctly updated after the method execution.

For the Java version I solved in this way:

NetworkConfig ccp = NetworkConfig.fromJsonFile(networkConfigPath.toFile());
// initialize default cryptosuite and setup the client
CryptoSuite cryptoSuite = CryptoSuite.Factory.getCryptoSuite();
HFClient client = HFClient.createNewInstance();
client.setCryptoSuite(cryptoSuite);

Channel channel = client.loadChannelFromConfig(ccp.getChannelNames().iterator().next(), ccp);
channel.initialize();

TransactionProposalRequest transactionProposal = client.newTransactionProposalRequest();
// build chaincode id providing the chaincode name
ChaincodeID mychaincodeID = ChaincodeID.newBuilder().setName("mychaincode").build();
transactionProposal.setChaincodeID(mychaincodeID);
// calling chaincode function
transactionProposal.setFcn("mymethod");
transactionProposal.setArgs("a1");

Collection<ProposalResponse> res = channel.sendTransactionProposal(transactionProposal);
CompletableFuture<TransactionEvent> cf = channel.sendTransaction(res);
TransactionEvent te = cf.get();

logger.info("Status: " + te.isValid());
logger.info("Committed the transaction with transactionID + " + te.getTransactionID());

Hope it can be helpful. Of course, if someone has comments they are well accepted.

Upvotes: 1

Related Questions