Interact with your Fabric blockchain
Now that you have a fully bootstrapped Fabric environment with a ready to use channel (default-channel
), let's start performing some important operations. For ease of use we will simply use the default-channel
in this documentation.
Please note that this guide is not meant to exhaustively cover every nuance of Fabric or Kaleido. Instead it is strictly focused on the following platform and blockchain operations:
- Uploading a chaincode binary or Node.js zip to Kaleido's smart contract management portal.
- Promoting the chaincode to your Fabric environment.
- Deploying the chaincode onto the
default-channel
. - Confirming the successful instantiation of the chaincode.
- Registering a user identity with the Certificate Authority.
- Performing a CSR (certificate signing request) and enrolling the user with the Certificate Authority.
- Inspecting the user identity object.
- Invoking the deployed chaincode (synchronously and asynchronously)
- Querying the chaincode transaction ID (synchronous)
- Querying the message ID (asynchronous)
- Configuring an event stream (webhook)
- Creating a subscription
Create an App project
We'll start by leveraging Kaleido's Smart Contract Management component. This feature provides a straightforward CI/CD pipe for your smart contract change management and allows you to easily inspect and deploy different versions of chaincodes that have been uploaded to the system. Most importantly though, it greatly simplifies Fabric transaction submission and application development by providing clean RESTful interfaces for interaction with your smart contract methods, all without the need for any client library. No custom programming or blockchain expertise is necessary, and otherwise esoteric features of the protocol such as certificate authority interaction and endorsement signature collection are removed from the equation. Simply upload a precompiled go binary or Node.js zip, and then leverage the /transactions
route in the Fabric API surface to speak to your smart contract. More on this later in the doc.
- Navigate to your business network dashboard and click on the Apps icon in the left navigation pane.
- Select the CREATE APP button within the Apps frame.
- Select the Fabric tile and click NEXT
IMPORTANT: The name of the contract project (e.g.
asset_transfer
) should always follow the naming conventions of Fabric Chaincodes.
- Provide a name for your App project. We'll use
asset_transfer
for this example. - Choose between the Golang Executable or Node.js Project options. Note that this tutorial will make use of the go binary approach.
- Click FINISH to create the project namespace.
- What is generated is an empty Hyperledger Fabric App project namespace with your desired chaincode implementation specified. We now need to provide the smart contract binary or Node.js zip.
Build your chaincode source
The chaincode we will be using in this tutorial comes from the widely used Hyperledger Fabric Samples repository. Specifically, we will be leveraging the go version of the asset-transfer-basic
smart contract.
- If you want to use the precompiled go binary approach, please make sure that you have Go installed on your machine.
- If you are electing for the Node.js zip, no action on your side is required. The compilation will take place on the server side.
For Golang binary chaincodes, build your Golang chaincode implementation and make a note of the resulting binary. You must make sure the target OS for the compiler is set to linux
, and the target architecture is set to amd64
.
From a terminal navigate to the asset-transfer-basic/chaincode-go
subdirectory and build the binary. Below is a sample build command:
GOOS=linux GOARCH=amd64 go build -o assetTransfer.bin
If you prefer to use a Node.js zip, instead navigate to the asset-transfer-basic/chaincode-javascript
and zip the entire project making sure that the node_modules
subfolder is NOT included. It's important that the resulting archive file has a single top-level directory, with all the chaincode resources contained within it.
Create a new version
- Click the CREATE NEW VERSION button in the top right of the app project screen.
- Use the SELECT FILE button and supply the go binary or Node.js zip file.
- DO NOT click on the
require initialization before invocation
option. - Click FINISH
Give the process a few moments. After which you should see a new version within your app project namespace:
Promote the Compilation to an Environment
Now we need to promote the source code to the environment that we previously created.
- Click on the compilation version on your app project screen to view the compilation details.
- In the top right portion of your screen, click Promote to Environment to promote the chaincode to your existing environment.
- Select the environment from the dropdown window.
- Click FINISH.
Deploy the chaincode
After a promotion you are taken to the "Chaincodes" tab within your environment.
- Click the DEPLOY INSTANCE TO CHANNEL button in the top right of this page to deploy.
- Leave the
default-channel
option as the target channel. - Once again, DO NOT click the
require initialization before invocation
. - Click FINISH to kick off the instantiation.
If you are using the go binary option you should see the chaincode transition to an INSTALLED and COMMITTED state in just a few seconds. If you are using a Node.js project, this process can take upwards of ten minutes for the dependencies to be fully installed.
Confirm instantiation
- Navigate to one of your peer nodes and click the Logs tab on the peer details screen.
- You are looking for a line similar to the below to confirm a successful instantiation:
[34m2022-08-17 16:04:09.560 UTC 5d0e INFO[0m [lifecycle] [34;1mupdate[0m -> Chaincode with package ID 'asset_transfer-1.0.0.u0pl22d6zg:a5298976332d840b5e235ebfee6fa67e7a65a23c9acda5d88abc47da3594de3c' now available on channel default-channel for chaincode definition asset_transfer:1.0.0.u0pl22d6zg
The above lets us know that we can proceed to invoke the chaincode and send transactions into the chain.
A word on the Fabric API Surface
Your Fabric environment on Kaleido comes preconfigured with a powerful piece of open source middleware called FireFly FabConnect. This is a RESTful API surface for interacting with Certificate Authorities, invoking chaincodes, and configuring event subscriptions. You can liken FabConnect to a "brain" for all classes of Hyperledger Fabric interaction.
FabConnect is accessible directly from any of your peer nodes.
- Click on one of your peer nodes to the see the node details screen
- In the lefthand navigation click on the REST API Gateway tab to expose the interactive FabConnect API surface.
NOTE: Within the Swagger Interface you will always need to click the "Try it out" button in order to invoke any of the APIs.
Register a user
Let's start by registering a user with the Certificate Authority.
POST
to the/identities
route withname
andtype
arguments.- The
maxEnrollments
andattributes
arguments are optional and we will forgo them in this tutorial:
- You should be returned a secret from the CA in the API response.
Enroll the user
POST
to the/identities/{username}/enroll
route.- Supply the username in the path and the
secret
in the body. Similar to the register call, you can skip the attributes.
- You should be returned a 200 response from the server.
Query the user identity object
- Issue a
GET
on the/identities/{username}
route. - Supply the username in the path.
- This call should return the full identity object including the msp ID, enrollment cert and CA cert.
{
"name": "user1",
"maxEnrollments": -1,
"type": "client",
"affiliation": "",
"attributes": {
"hf.Affiliation": "",
"hf.EnrollmentID": "user1",
"hf.Type": "client"
},
"caname": "u0put7o2ca",
"organization": "u0put7o2ca",
"mspId": "u0put7o2ca",
"enrollmentCert": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNLVENDQWRDZ0F3SUJBZ0lVY3kxTjBWUXNBeDk4c2d6cVZiMTRSUHlXZXprd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakE0TVRZeE9EQTBNREJhRncwegpNakE0TVRNeU1UQTJNREJhTUNFeER6QU5CZ05WQkFzVEJtTnNhV1Z1ZERFT01Bd0dBMVVFQXhNRmRYTmxjakV3CldUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRUEN0Zks4dmlJYTBGNTNwekZSZDJwQXZsaDU3TFAKQXBneHg0ZmZnV2h3VGNraWlmOTR4NkVPZ3MzMnVDRjFnY0VUMjBMOHIvVzNaUE9xalJMNFJSR1JvNEhyTUlIbwpNQTRHQTFVZER3RUIvd1FFQXdJSGdEQU1CZ05WSFJNQkFmOEVBakFBTUIwR0ExVWREZ1FXQkJTcHMvUDE3c0tjCkRLWFJyVGgxdGZPeklPT01WekFmQmdOVkhTTUVHREFXZ0JTZzREL1J3Vm0xVHpyamlXQlc4bEQ5cno5YlFqQXUKQmdOVkhSRUVKekFsZ2lOMU1ITTBhelo2WVdaMUxYVXdlSEY2Y1RaeE5Ya3RabUZpY21sakxXNXZaR1V0TURCWQpCZ2dxQXdRRkJnY0lBUVJNZXlKaGRIUnljeUk2ZXlKb1ppNUJabVpwYkdsaGRHbHZiaUk2SWlJc0ltaG1Ma1Z1CmNtOXNiRzFsYm5SSlJDSTZJblZ6WlhJeElpd2lhR1l1Vkhsd1pTSTZJbU5zYVdWdWRDSjlmVEFLQmdncWhrak8KUFFRREFnTkhBREJFQWlBYmREbUFnNUR1biswR0VGL2ZhVzhWOTVuRWI0WGdXZGJkUThmMElxY1B1QUlnTHpFUgpGNWZrUUg1RzJzbXpZbTA5UE1WRTBqZUg2Uld0dDJNZjhnaXVWa1E9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K",
"caCert": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lVRm9FbEFtTWo0b2o5V1lvMXY3QS96NXN0NWRjd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakE0TVRZeE9EQTBNREJhRncwegpOekE0TVRJeE9EQTBNREJhTUJzeEdUQVhCZ05WQkFNVEVHWmhZbkpwWXkxallTMXpaWEoyWlhJd1dUQVRCZ2NxCmhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBVE5iN0pDaFBoYTArTHJPdkR1SjJDMFJWVHQrZHRPNFpVRnA5R3kKSCtHbGJIWDdWYWZadThzZjhFMlBtUG93eG9UL1N4YzFnNEJsWnFOYUJzM3dkREIxbzBVd1F6QU9CZ05WSFE4QgpBZjhFQkFNQ0FRWXdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFkQmdOVkhRNEVGZ1FVb09BLzBjRlp0VTg2CjQ0bGdWdkpRL2E4L1cwSXdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTGZ6cGx1MUppeHNYR1UvWkx3Z0ZYdlMKcnBXU01CZmRZWGhOZTk5NS8zNytBaUJLcTByTjF6dHpWSi81M0NxU0RWOXRJU1dZSHdCdHBybVVCUTBEMFJhMQpwUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
}
Invoke the deployed chaincode
Please note that this tutorial is using the asset-transfer-basic
chaincode. If you've deployed a different piece of source, just modify your arguments and methods accordingly.
POST
to the/transactions
route.- In the body of the call supply values for
signer
,channel
,chaincode
,function
andargs
. All of these are required. - The argument for the
chaincode
parameter is the name of your chaincode package (e.gasset_transfer
). - The method we are invoking is the
CreateAsset
method. It takes a string array for the following five arguments:id
,color
,size
,owner
andvalue
. - Note that this will be a synchronous transaction.
{
"headers": {
"type": "SendTransaction",
"signer": "user1",
"channel": "default-channel",
"chaincode": "asset_transfer"
},
"func": "CreateAsset",
"args": [
"asset01", "blue", "5", "Nick", "500"
],
"init": false
}
- You should receive back a 200 from the server and a response body that contains a
transactionID
.
{
"headers": {
"id": "c10894a1-19ec-4f9f-7d73-170c84559443",
"type": "TransactionSuccess",
"timeReceived": "2022-08-16T21:19:40.476508469Z",
"timeElapsed": 0.4022088,
"requestOffset": "",
"requestId": ""
},
"blockNumber": 6,
"signerMSP": "u0put7o2ca",
"signer": "user1",
"transactionID": "5701981c2cd9b8c388a27702f5781ebe994a3fc4f297c138f71c5741ccd48901",
"status": "VALID"
}
Query the transaction ID
Querying the transaction ID will return the full transaction receipt containing the proposal hash, nonce, timestamp, endorsement signatures, and other details related to the Fabric transaction.
- Issue a
GET
on the/transactions/{txID}
route. - Supply the
transactionID
in the path and theuser
andchannel
in the body.
{
"headers": {
"timeReceived": "",
"timeElapsed": 0,
"requestOffset": "",
"requestId": ""
},
"result": {
"raw": {
"payload": {
"data": {
"actions": [
{
"header": {
"creator": {
"mspid": "u0put7o2ca",
"id_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNLVENDQWRDZ0F3SUJBZ0lVY3kxTjBWUXNBeDk4c2d6cVZiMTRSUHlXZXprd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakE0TVRZeE9EQTBNREJhRncwegpNakE0TVRNeU1UQTJNREJhTUNFeER6QU5CZ05WQkFzVEJtTnNhV1Z1ZERFT01Bd0dBMVVFQXhNRmRYTmxjakV3CldUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRUEN0Zks4dmlJYTBGNTNwekZSZDJwQXZsaDU3TFAKQXBneHg0ZmZnV2h3VGNraWlmOTR4NkVPZ3MzMnVDRjFnY0VUMjBMOHIvVzNaUE9xalJMNFJSR1JvNEhyTUlIbwpNQTRHQTFVZER3RUIvd1FFQXdJSGdEQU1CZ05WSFJNQkFmOEVBakFBTUIwR0ExVWREZ1FXQkJTcHMvUDE3c0tjCkRLWFJyVGgxdGZPeklPT01WekFmQmdOVkhTTUVHREFXZ0JTZzREL1J3Vm0xVHpyamlXQlc4bEQ5cno5YlFqQXUKQmdOVkhSRUVKekFsZ2lOMU1ITTBhelo2WVdaMUxYVXdlSEY2Y1RaeE5Ya3RabUZpY21sakxXNXZaR1V0TURCWQpCZ2dxQXdRRkJnY0lBUVJNZXlKaGRIUnljeUk2ZXlKb1ppNUJabVpwYkdsaGRHbHZiaUk2SWlJc0ltaG1Ma1Z1CmNtOXNiRzFsYm5SSlJDSTZJblZ6WlhJeElpd2lhR1l1Vkhsd1pTSTZJbU5zYVdWdWRDSjlmVEFLQmdncWhrak8KUFFRREFnTkhBREJFQWlBYmREbUFnNUR1biswR0VGL2ZhVzhWOTVuRWI0WGdXZGJkUThmMElxY1B1QUlnTHpFUgpGNWZrUUg1RzJzbXpZbTA5UE1WRTBqZUg2Uld0dDJNZjhnaXVWa1E9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
},
"nonce": "FbGrr6WG9DIEnwWPY5u4fwt3zZSgOd5e"
},
"payload": {
"action": {
"proposal_response_payload": {
"extension": {
"chaincode_id": {
"name": "asset_transfer",
"version": "1.0.0.u0pl22d6zg"
},
"events": {
"chaincodeId": "asset_transfer",
"transactionId": "5701981c2cd9b8c388a27702f5781ebe994a3fc4f297c138f71c5741ccd48901",
"timestamp": "1660684780606120555",
"eventName": "event1",
"payload": {
"AppraisedValue": 500,
"Color": "blue",
"ID": "asset01",
"Owner": "Nick",
"Size": 5
}
}
},
"proposal_hash": "pc/C9x6nUR5U6yIs7vTdQ3sQ2rGc8JVFgdfAcAsndrE="
}
},
"chaincode_proposal_payload": {
"TransientMap": null,
"input": {
"chaincode_spec": {
"chaincode_id": {
"name": "asset_transfer"
},
"input": {
"args": [
"CreateAsset",
"asset01",
"blue",
"5",
"Nick",
"500"
],
"is_init": false
},
"type": "UNDEFINED"
}
}
}
}
}
]
},
"header": {
"channel_header": {
"channel_id": "default-channel",
"epoch": "0",
"timestamp": 1660684780606120400,
"tx_id": "5701981c2cd9b8c388a27702f5781ebe994a3fc4f297c138f71c5741ccd48901",
"type": "ENDORSER_TRANSACTION",
"version": 0
},
"signature_header": {
"creator": {
"mspid": "u0put7o2ca",
"id_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNLVENDQWRDZ0F3SUJBZ0lVY3kxTjBWUXNBeDk4c2d6cVZiMTRSUHlXZXprd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakE0TVRZeE9EQTBNREJhRncwegpNakE0TVRNeU1UQTJNREJhTUNFeER6QU5CZ05WQkFzVEJtTnNhV1Z1ZERFT01Bd0dBMVVFQXhNRmRYTmxjakV3CldUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRUEN0Zks4dmlJYTBGNTNwekZSZDJwQXZsaDU3TFAKQXBneHg0ZmZnV2h3VGNraWlmOTR4NkVPZ3MzMnVDRjFnY0VUMjBMOHIvVzNaUE9xalJMNFJSR1JvNEhyTUlIbwpNQTRHQTFVZER3RUIvd1FFQXdJSGdEQU1CZ05WSFJNQkFmOEVBakFBTUIwR0ExVWREZ1FXQkJTcHMvUDE3c0tjCkRLWFJyVGgxdGZPeklPT01WekFmQmdOVkhTTUVHREFXZ0JTZzREL1J3Vm0xVHpyamlXQlc4bEQ5cno5YlFqQXUKQmdOVkhSRUVKekFsZ2lOMU1ITTBhelo2WVdaMUxYVXdlSEY2Y1RaeE5Ya3RabUZpY21sakxXNXZaR1V0TURCWQpCZ2dxQXdRRkJnY0lBUVJNZXlKaGRIUnljeUk2ZXlKb1ppNUJabVpwYkdsaGRHbHZiaUk2SWlJc0ltaG1Ma1Z1CmNtOXNiRzFsYm5SSlJDSTZJblZ6WlhJeElpd2lhR1l1Vkhsd1pTSTZJbU5zYVdWdWRDSjlmVEFLQmdncWhrak8KUFFRREFnTkhBREJFQWlBYmREbUFnNUR1biswR0VGL2ZhVzhWOTVuRWI0WGdXZGJkUThmMElxY1B1QUlnTHpFUgpGNWZrUUg1RzJzbXpZbTA5UE1WRTBqZUg2Uld0dDJNZjhnaXVWa1E9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
},
"nonce": "FbGrr6WG9DIEnwWPY5u4fwt3zZSgOd5e"
}
}
},
"signature": "MEQCID3OEY37wc+PcYp0hNqqgwbgbRogw9W8VQE5pArmb7cHAiAMi42rUKkUOIpM2TQHtZ7TNIvPyTx+nKCBPbnJ80OUAg=="
},
"transaction": {
"type": "ENDORSER_TRANSACTION",
"tx_id": "5701981c2cd9b8c388a27702f5781ebe994a3fc4f297c138f71c5741ccd48901",
"nonce": "4662477272365747394449456e77575059357534667774337a5a53674f643565",
"creator": {
"msp_id": "u0put7o2ca",
"cert": "-----BEGIN CERTIFICATE-----\nMIICKTCCAdCgAwIBAgIUcy1N0VQsAx98sgzqVb14RPyWezkwCgYIKoZIzj0EAwIw\nGzEZMBcGA1UEAxMQZmFicmljLWNhLXNlcnZlcjAeFw0yMjA4MTYxODA0MDBaFw0z\nMjA4MTMyMTA2MDBaMCExDzANBgNVBAsTBmNsaWVudDEOMAwGA1UEAxMFdXNlcjEw\nWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQPCtfK8viIa0F53pzFRd2pAvlh57LP\nApgxx4ffgWhwTckiif94x6EOgs32uCF1gcET20L8r/W3ZPOqjRL4RRGRo4HrMIHo\nMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSps/P17sKc\nDKXRrTh1tfOzIOOMVzAfBgNVHSMEGDAWgBSg4D/RwVm1TzrjiWBW8lD9rz9bQjAu\nBgNVHREEJzAlgiN1MHM0azZ6YWZ1LXUweHF6cTZxNXktZmFicmljLW5vZGUtMDBY\nBggqAwQFBgcIAQRMeyJhdHRycyI6eyJoZi5BZmZpbGlhdGlvbiI6IiIsImhmLkVu\ncm9sbG1lbnRJRCI6InVzZXIxIiwiaGYuVHlwZSI6ImNsaWVudCJ9fTAKBggqhkjO\nPQQDAgNHADBEAiAbdDmAg5Dun+0GEF/faW8V95nEb4XgWdbdQ8f0IqcPuAIgLzER\nF5fkQH5G2smzYm09PMVE0jeH6RWtt2Mf8giuVkQ=\n-----END CERTIFICATE-----\n"
},
"status": "",
"signature": "MEQCID3OEY37wc+PcYp0hNqqgwbgbRogw9W8VQE5pArmb7cHAiAMi42rUKkUOIpM2TQHtZ7TNIvPyTx+nKCBPbnJ80OUAg==",
"timestamp": 1660684780606120400,
"actions": [
{
"nonce": "4662477272365747394449456e77575059357534667774337a5a53674f643565",
"creator": {
"msp_id": "u0put7o2ca",
"cert": "-----BEGIN CERTIFICATE-----\nMIICKTCCAdCgAwIBAgIUcy1N0VQsAx98sgzqVb14RPyWezkwCgYIKoZIzj0EAwIw\nGzEZMBcGA1UEAxMQZmFicmljLWNhLXNlcnZlcjAeFw0yMjA4MTYxODA0MDBaFw0z\nMjA4MTMyMTA2MDBaMCExDzANBgNVBAsTBmNsaWVudDEOMAwGA1UEAxMFdXNlcjEw\nWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQPCtfK8viIa0F53pzFRd2pAvlh57LP\nApgxx4ffgWhwTckiif94x6EOgs32uCF1gcET20L8r/W3ZPOqjRL4RRGRo4HrMIHo\nMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSps/P17sKc\nDKXRrTh1tfOzIOOMVzAfBgNVHSMEGDAWgBSg4D/RwVm1TzrjiWBW8lD9rz9bQjAu\nBgNVHREEJzAlgiN1MHM0azZ6YWZ1LXUweHF6cTZxNXktZmFicmljLW5vZGUtMDBY\nBggqAwQFBgcIAQRMeyJhdHRycyI6eyJoZi5BZmZpbGlhdGlvbiI6IiIsImhmLkVu\ncm9sbG1lbnRJRCI6InVzZXIxIiwiaGYuVHlwZSI6ImNsaWVudCJ9fTAKBggqhkjO\nPQQDAgNHADBEAiAbdDmAg5Dun+0GEF/faW8V95nEb4XgWdbdQ8f0IqcPuAIgLzER\nF5fkQH5G2smzYm09PMVE0jeH6RWtt2Mf8giuVkQ=\n-----END CERTIFICATE-----\n"
},
"transient_map": null,
"chaincode_id": {
"name": "asset_transfer",
"version": "1.0.0.u0pl22d6zg"
},
"input": {
"args": [
"CreateAsset",
"asset01",
"blue",
"5",
"Nick",
"500"
],
"is_init": false
},
"proposal_hash": "70632f433978366e5552355536794973377654645133735132724763384a5646676466416341736e6472453d",
"event": {
"chaincodeId": "asset_transfer",
"transactionId": "5701981c2cd9b8c388a27702f5781ebe994a3fc4f297c138f71c5741ccd48901",
"timestamp": "1660684780606120555",
"eventName": "event1",
"payload": {
"AppraisedValue": 500,
"Color": "blue",
"ID": "asset01",
"Owner": "Nick",
"Size": 5
}
}
}
]
}
}
}
Response headers
content-type: application/json
Responses
Code Description Links
200
Transaction retrieved
Media type
application/json
Controls Accept header.
Example Value
Schema
{
"raw": {
"payload": {
"data": {
"actions": [
{
"header": {
"creator": {
"mspid": "string",
"id_bytes": "string"
},
"nonce": "string"
},
"payload": {
"action": {
"proposal_response_payload": {
"extension": {
"chaincode_id": {
"name": "string",
"version": "string"
},
"events": {
"chaincodeId": "string",
"transactionId": "string",
"timestamp": "string",
"eventName": "string",
"payload": "string"
}
},
"proposal_hash": "string"
}
},
"chaincode_proposal_payload": {
"TransientMap": {},
"input": {
"chaincode_spec": {
"chaincode_id": {
"name": "string",
"version": "string"
},
"input": {
"args": [
"string"
],
"is_init": true
}
}
}
}
}
}
]
},
"header": {
"channel_header": {
"channel_id": "string",
"epoch": "string",
"timestamp": 0,
"tx_id": "string",
"type": "string",
"version": 0
},
"signature_header": {
"creator": {
"mspid": "string",
"id_bytes": "string"
},
"nonce": "string"
}
}
},
"siganture": "string"
},
"transaction": {
"type": "string",
"tx_id": "string",
"nonce": "string",
"creator": {
"msp_id": "string",
"cert": "string"
},
"status": "string",
"signature": "string",
"timestamp": 0,
"actions": [
{
"nonce": "string",
"creator": {
"msp_id": "string",
"cert": "string"
},
"transient_map": {},
"chaincode_id": {
"name": "string",
"version": "string"
},
"input": {
"args": [
"string"
],
"is_init": true
},
"proposal_hash": "string",
"event": {
"chaincodeId": "string",
"transactionId": "string",
"timestamp": "string",
"eventName": "string",
"payload": "string"
}
}
]
}
}
Invoke the chaincode in async mode
FabConnect provides two different transaction submission modes: sync
and async
. A sync
submission will return the transactionID
directly in the response. An async
submission will return a unique receipt ID from the underlying Apache Kafka topic that we can query against to retrieve the transactionID
.
POST
once more to the/transactions
route with all of the required values and arguments. NOTE: If you're using theasset-transfer-basic
chaincode, you'll need to modify theid
field for the asset. Otherwise the chaincode will throw an error stating that the "asset already exists."- Set the
fly-sync
query parameter tofalse
. - You should receive a 200 from the server and a unique message id.
Query the async message ID
Once you have a message id from an async transaction submission, you simply need to query the receipt store to learn about the associated transaction ID.
- Issue a
GET
on the/receipts/{receiptId}
route. - Supply the receipt ID in the path.
- This should return a similar response body to that you received with a synchronous submission.
{
"_id": "676ab476-7354-489c-62d0-1619e6cee9ff",
"blockNumber": 7,
"headers": {
"id": "e3ea6978-fd5a-421b-6c66-9069a25507e6",
"requestId": "676ab476-7354-489c-62d0-1619e6cee9ff",
"requestOffset": "",
"timeElapsed": 0.289308109,
"timeReceived": "2022-08-16T21:30:28.225459342Z",
"type": "TransactionSuccess"
},
"receivedAt": 1660685428514,
"signer": "user1",
"signerMSP": "u0put7o2ca",
"status": "VALID",
"transactionID": "c080ffa537722d79707814c6e774657853b0d9149949e291697105a379407d2e"
}
Create an Event Stream
The FabConnect middleware component also supports the dynamic streaming of events to your backend application. You just need to tell Kaleido the type of event stream configuration (webhook or websocket) and then corresponding info (http endpoint or topic) where you want the data to be delivered. We will go with a webhook example in this tutorial.
POST
to the/eventstreams
route.- There are a bunch of free subdomain services available to test this out. Webhook.site and Request Catcher are two particularly convenient and easy options.
- Below is an example body object for a webhook configuration.
{
"name": "AssetTransferEvents",
"type": "webhook",
"webhook": {
"url": "https://at123.requestcatcher.com/test",
"tlsSkipVerifyHost": "true"
}
}
- The response body will contain an event stream ID. Make note of this value as it is a pointer to the configuration you just created and is required for a subscription.
{
"created": "2022-08-16T21:44:36Z",
"id": "es-8ae9c78a-b02f-44b8-7fa9-bde360813baf",
"name": "AssetTransferEvents",
"path": "/eventstreams/es-8ae9c78a-b02f-44b8-7fa9-bde360813baf",
"suspended": false,
"type": "webhook",
"batchSize": 1,
"batchTimeoutMS": 5000,
"errorHandling": "skip",
"blockedRetryDelaySec": 30,
"webhook": {
"url": "https://at123.requestcatcher.com/test",
"tlsSkipHostVerify": false,
"requestTimeoutSec": 120
},
"timestamps": false,
"timestampCacheSize": 1000
}
Create a subscription
Now that you've taught Kaleido about where you want events to be delivered, you next need to tell the system about what types of events you want to be delivered. This tutorial will take every transaction from the default-channel
, however you will notice the ability to very easily curate events by filtering against the chaincodeId
and eventFilter
parameters.
POST
to the/subscriptions
route.- In the body of the call supply values for
name
,stream
,channel
,signer
,fromBlock
,payloadType
andblockType
.
{
"name": "AllEvents",
"stream": "es-8ae9c78a-b02f-44b8-7fa9-bde360813baf",
"channel": "default-channel",
"signer": "user1",
"fromBlock": "0",
"payloadType": "json",
"filter": {
"blockType": "tx",
"chaincodeId": "",
"eventFilter": ""
}
}
You should then start seeing events piped to your configured endpoint.
Troubleshooting
This content is coming soon...