This tutorial shows how to create, build, install and test chaincode using the Fabric-Private-chaincode(FPC) framework. This assumes familiarity with the concepts and the programming model in Hyperledger Fabric v2.0.
Refer to package shim for the GoDoc for shim interface provided by Fabric. The FPC programming model for chaincode provides a simpler version of the shim SDK provided by Hyperledger Fabric in Go and node.js. FPC provides a C++ interface to access its state variables and transaction context through shim.h. The standard commands are similar to the ones in Fabric. To ensure confidentiality of the arguments passed to the chaincode, the arguments are transparently encrypted while using FPC SDK.
Regarding management functionally such as chaincode installation and alike, plus refer to FPC Management API document for details.
This example illustrates a simple usecase where the chaincode is used to store a single asset, asset1
in the ledger and then retrieve the latest value of asset1
. Here are the steps to accomplish this:
- Develop chaincode
- Launch Fabric network
- Install and instantiate chaincode on the peer
- Invoke transactions (
storeAsset
andretrieveAsset
) - Shut down the network
Please refer to Architecture and Components for more details of involved components.
This tutorial presumes that the repository in https://github.com/hyperledger-labs/fabric-private-chaincode has been installed as per README.md in FPC-INSTALL-DIR
which is in the $GOPATH
folder.
- Create a folder named
helloworld
inFPC-INSTALL-DIR/examples
.
cd $GOPATH/src/github.com/hyperledger-labs/fabric-private-chaincode/examples
mkdir helloworld
cd helloworld
touch helloworld_cc.cpp
- Add the necessary includes and a preliminary version of
invoke
function. The result of the transaction is returned inresponse
.ctx
represents the transaction context. The function body illustrates another way to get invocation parameters, similar to functions provided in go shim.
#include "shim.h"
#include "logging.h"
#include <string>
// implements chaincode logic for invoke
int invoke(
uint8_t* response,
uint32_t max_response_len,
uint32_t* actual_response_len,
shim_ctx_ptr_t ctx)
{
std::string function_name;
std::vector<std::string> params;
get_func_and_params(function_name, params, ctx);
return 0;
}
Let us add the first transaction, storeAsset
which simply saves the value of an asset by calling put_state
method defined in shim.h
.
LOG_DEBUG
sends log messages to the file /tmp/fpc-extbuilder.${date_time}.{chaincode_name}/chaincode.log
(if the environment variable SGX_BUILD
is set to DEBUG
).
#define OK "OK"
// Add asset_name, value to ledger
std::string storeAsset(std::string asset_name, int value, shim_ctx_ptr_t ctx)
{
LOG_DEBUG("HelloworldCC: +++ storeAsset +++");
put_state(asset_name.c_str(), (uint8_t*)&value, sizeof(int), ctx);
return OK;
}
Similarly, let us add the next transaction, retrieveAsset
which reads the value of an asset by calling get_state
method defined in shim.h.
#define NOT_FOUND "Asset not found"
#define MAX_VALUE_SIZE 1024
// Get value set for asset_name in ledger
std::string retrieveAsset(std::string asset_name, shim_ctx_ptr_t ctx)
{
std::string result;
LOG_DEBUG("HelloworldCC: +++ retrieveAsset +++");
uint32_t asset_bytes_len = 0;
uint8_t asset_bytes[MAX_VALUE_SIZE];
get_state(asset_name.c_str(), asset_bytes, sizeof(asset_bytes), &asset_bytes_len, ctx);
// check if asset_name exists
if (asset_bytes_len > 0)
{
// asset exists; return value
result = asset_name + ":" + std::to_string((int)(*asset_bytes));
}
else
{
// asset does not exist
result = NOT_FOUND;
}
return result;
}
Modify the invoke
method to invoke the appropriate function depending on the function name passed in args
and return response
.
#define OK "OK"
#define NOT_FOUND "Asset not found"
#define MAX_VALUE_SIZE 1024
// implements chaincode logic for invoke
int invoke(
uint8_t* response,
uint32_t max_response_len,
uint32_t* actual_response_len,
shim_ctx_ptr_t ctx)
{
LOG_DEBUG("HelloworldCC: +++ Executing helloworld chaincode invocation +++");
std::string function_name;
std::vector<std::string> params;
get_func_and_params(function_name, params, ctx);
std::string asset_name = params[0];
std::string result;
if (function_name == "storeAsset")
{
int value = std::stoi (params[1]);
result = storeAsset(asset_name, value, ctx);
}
else if (function_name == "retrieveAsset")
{
result = retrieveAsset(asset_name, ctx);
}
else
{
// unknown function
LOG_DEBUG("HelloworldCC: RECEIVED UNKNOWN transaction '%s'", function_name);
return -1;
}
// check that result fits into response
int neededSize = result.size();
if (max_response_len < neededSize)
{
// error: buffer too small for the response to be sent
LOG_DEBUG("HelloworldCC: Response buffer too small");
*actual_response_len = 0;
return -1;
}
// copy result to response
memcpy(response, result.c_str(), neededSize);
*actual_response_len = neededSize;
LOG_DEBUG("HelloworldCC: Response: %s", result.c_str());
LOG_DEBUG("HelloworldCC: +++ Executing done +++");
return 0;
}
Here is the complete file, helloworld_cc.cpp
:
#include "shim.h"
#include "logging.h"
#include <string>
#define OK "OK"
#define NOT_FOUND "Asset not found"
#define MAX_VALUE_SIZE 1024
// Add asset_name, value to ledger
std::string storeAsset(std::string asset_name, int value, shim_ctx_ptr_t ctx)
{
LOG_DEBUG("HelloworldCC: +++ storeAsset +++");
put_state(asset_name.c_str(), (uint8_t*)&value, sizeof(int), ctx);
return OK;
}
std::string retrieveAsset(std::string asset_name, shim_ctx_ptr_t ctx)
{
std::string result;
LOG_DEBUG("HelloworldCC: +++ retrieveAsset +++");
uint32_t asset_bytes_len = 0;
uint8_t asset_bytes[MAX_VALUE_SIZE];
get_state(asset_name.c_str(), asset_bytes, sizeof(asset_bytes), &asset_bytes_len, ctx);
// check if asset_name exists
if (asset_bytes_len > 0)
{
result = asset_name + ":" + std::to_string((int)(*asset_bytes));
} else {
// asset does not exist
result = NOT_FOUND;
}
return result;
}
// implements chaincode logic for invoke
int invoke(
uint8_t* response,
uint32_t max_response_len,
uint32_t* actual_response_len,
shim_ctx_ptr_t ctx)
{
LOG_DEBUG("HelloworldCC: +++ Executing helloworld chaincode invocation +++");
std::string function_name;
std::vector<std::string> params;
get_func_and_params(function_name, params, ctx);
std::string asset_name = params[0];
std::string result;
if (function_name == "storeAsset")
{
int value = std::stoi (params[1]);
result = storeAsset(asset_name, value, ctx);
}
else if (function_name == "retrieveAsset")
{
result = retrieveAsset(asset_name, ctx);
}
else
{
// unknown function
LOG_DEBUG("HelloworldCC: RECEIVED UNKNOWN transaction '%s'", function_name);
return -1;
}
// check that result fits into response
int neededSize = result.size();
if (max_response_len < neededSize)
{
// error: buffer too small for the response to be sent
LOG_DEBUG("HelloworldCC: Response buffer too small");
*actual_response_len = 0;
return -1;
}
// copy result to response
memcpy(response, result.c_str(), neededSize);
*actual_response_len = neededSize;
LOG_DEBUG("HelloworldCC: Response: %s", result.c_str());
LOG_DEBUG("HelloworldCC: +++ Executing done +++");
return 0;
}
Make sure you have the environment variables set. In addition, set SGX_BUILD=DEBUG
to enable log messages.
To build the helloworld chaincode, we are using CMake. This simplifies the build process and compiles our chaincode using the SGX SDK. Create CMakeLists.txt
with the following content.
File: CMakeLists.txt
cmake_minimum_required(VERSION 3.5.1)
set(SOURCE_FILES
helloworld_cc.cpp
)
include(../../ecc_enclave/enclave/CMakeLists-common-app-enclave.txt)
Create Makefile
with the following content. For your convenience, you can copy the Makefile
from FPC-INSTALL-DIR/examples/auction
folder.
File: examples/helloworld/Makefile
TOP = ../..
include $(TOP)/build.mk
BUILD_DIR := _build
$(BUILD_DIR):
@if [ ! -d $(BUILD_DIR) ]; then \
mkdir -p $(BUILD_DIR) && \
cd $(BUILD_DIR) && \
cmake ./..; \
fi
build: $(BUILD_DIR)
$(MAKE) --directory=$<
clean:
rm -rf $(BUILD_DIR)
In FPC-INSTALL-DIR/examples/helloworld
folder, to build the chaincode, execute:
make
Following is a part of expected output. Please note [100%] Built target enclave
message in the output. This suggests that build was successful.
Output:
make[3]: Leaving directory '/home/bcuser/work/src/github.com/hyperledger-labs/fabric-private-chaincode/examples/helloworld/_build'
[100%] Built target enclave
make[2]: Leaving directory '/home/bcuser/work/src/github.com/hyperledger-labs/fabric-private-chaincode/examples/helloworld/_build'
/usr/bin/cmake -E cmake_progress_start /home/bcuser/work/src/github.com/hyperledger-labs/fabric-private-chaincode/examples/helloworld/_build/CMakeFiles 0
make[1]: Leaving directory '/home/bcuser/work/src/github.com/hyperledger-labs/fabric-private-chaincode/examples/helloworld/_build'
Next step is to test the chaincode by invoking the transactions, for which you need a basic Fabric network with a channel. You will use the FPC test framework to bring up a Fabric network in which the helloworld code can be executed as a chaincode in an SGX enclave. The Fabric network used in this tutorial is defined and configured using integration/config/core.yaml
. Specifically, please note the additions to the standard Fabric configurations. These are marked as FPC Addition
; these enable the integration points with Fabric and have to be replicated if you want to use your own Fabric configuration.
Create a file test.sh
in examples/helloworld
folder as follows. Note that the initial lines in the script points to files and folders in FPC framework.
-FPC_PATH
points to FPC-INSTALL-DIR
-FABRIC_CFG_PATH
points to the FPC-INSTALL-DIR/integration/config, which contains yaml files that define the Fabric network
-FABRIC_SCRIPTDIR
points to scripts with custom FPC wrappers and utility scripts.
File: test.sh
SCRIPTDIR="$(dirname $(readlink --canonicalize ${BASH_SOURCE}))"
FPC_PATH="${SCRIPTDIR}/../.."
FABRIC_CFG_PATH="${SCRIPTDIR}/../../integration/config"
FABRIC_SCRIPTDIR="${FPC_PATH}/fabric/bin/"
. ${FABRIC_SCRIPTDIR}/lib/common_utils.sh
. ${FABRIC_SCRIPTDIR}/lib/common_ledger.sh
#this is the path that will be used for the docker build of the chaincode enclave
CC_PATH=${FPC_PATH}/examples/helloworld/_build/lib/
CC_ID=helloworld_test
CC_VER="$(cat ${CC_PATH}/mrenclave)"
CC_EP="OR('SampleOrg.member')"
CC_SEQ="1"
Now that the environment is set, add commands to clear docker containers previously created, if any. Add calls to ledger_init
(which sets up a test network), run helloworld test and ledger_shutdown
(which cleans up the test network).
# 1. prepare
para
say "Preparing Helloworld Test ..."
# - clean up relevant docker images
docker_clean ${ERCC_ID}
docker_clean ${CC_ID}
# 2. run
say "- setup ledger"
ledger_init
say "- helloworld test"
helloworld_test # yet to be created
say "- shutdown ledger"
ledger_shutdown
Like in the case of Fabric, you install the chaincode using the peer lifecycle
commands and then invoke transactions.
To install the FPC chaincode, you need to use FPC-INSTALL-DIR/fabric/bin/peer.sh
. This is a custom FPC wrapper to be used instead of the peer
cli command from Fabric. ${PEER_CMD}
is set in FPC-INSTALL-DIR/fabric/bin/lib/common_ledger.sh
and conveniently points to the required script file.
With the variables set and common_ledger.sh
executed, usage of peer.sh
is as follows:
try ${PEER_CMD} lifecycle chaincode package --lang fpc-c --label ${CC_ID} --path ${CC_PATH} ${PKG}
try ${PEER_CMD} lifecycle chaincode install ${PKG}
In the next step, the FPC chaincode must be approved by the organizations on the channel by agreeing on the chaincode definition.
try ${PEER_CMD} lifecycle chaincode approveformyorg -o ${ORDERER_ADDR} -C ${CHAN_ID} --package-id ${PKG_ID} --name ${CC_ID} --version ${CC_VER} --sequence ${CC_SEQ} --signature-policy ${CC_EP}
try ${PEER_CMD} lifecycle chaincode checkcommitreadiness -C ${CHAN_ID} --name ${CC_ID} --version ${CC_VER} --sequence ${CC_SEQ} --signature-policy ${CC_EP}
try ${PEER_CMD} lifecycle chaincode commit -o ${ORDERER_ADDR} -C ${CHAN_ID} --name ${CC_ID} --version ${CC_VER} --sequence ${CC_SEQ} --signature-policy ${CC_EP}
To complete the installation, we need to create an enclave that runs the FPC Chaincode.
# create an FPC Chaincode enclave
try ${PEER_CMD} lifecycle chaincode initEnclave -o ${ORDERER_ADDR} --peerAddresses "localhost:7051" --name ${CC_ID}
Add the following content to the function, helloworld_test()
in test.sh. Please note the inline comments for each of the commands.
helloworld_test() {
say "- do hello world"
# install helloworld chaincode
# input: CC_ID:chaincode name; CC_VER:chaincode version;
# CC_PATH:path to build artifacts
say "- install helloworld chaincode"
PKG=/tmp/${CC_ID}.tar.gz
try ${PEER_CMD} lifecycle chaincode package --lang fpc-c --label ${CC_ID} --path ${CC_PATH} ${PKG}
try ${PEER_CMD} lifecycle chaincode install ${PKG}
PKG_ID=$(${PEER_CMD} lifecycle chaincode queryinstalled | awk "/Package ID: ${CC_ID}/{print}" | sed -n 's/^Package ID: //; s/, Label:.*$//;p')
try ${PEER_CMD} lifecycle chaincode approveformyorg -o ${ORDERER_ADDR} -C ${CHAN_ID} --package-id ${PKG_ID} --name ${CC_ID} --version ${CC_VER} --sequence ${CC_SEQ} --signature-policy ${CC_EP}
try ${PEER_CMD} lifecycle chaincode checkcommitreadiness -C ${CHAN_ID} --name ${CC_ID} --version ${CC_VER} --sequence ${CC_SEQ} --signature-policy ${CC_EP}
try ${PEER_CMD} lifecycle chaincode commit -o ${ORDERER_ADDR} -C ${CHAN_ID} --name ${CC_ID} --version ${CC_VER} --sequence ${CC_SEQ} --signature-policy ${CC_EP}
# create an FPC Chaincode enclave
try ${PEER_CMD} lifecycle chaincode initEnclave -o ${ORDERER_ADDR} --peerAddresses "localhost:7051" --name ${CC_ID}
# store the value of 100 in asset1
say "- invoke storeAsset transaction to store value 100 in asset1"
try ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Args":["storeAsset","asset1","100"]}' --waitForEvent
# retrieve current value for "asset1"; should be 100;
say "- invoke retrieveAsset transaction to retrieve current value of asset1"
try ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Args":["retrieveAsset","asset1"]}' --waitForEvent
say "- invoke query with retrieveAsset transaction to retrieve current value of asset1"
try ${PEER_CMD} chaincode query -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Args":["retrieveAsset","asset1"]}'
}
Putting all these code snippets together, here is the complete test.sh
file.
#!/bin/bash
SCRIPTDIR="$(dirname $(readlink --canonicalize ${BASH_SOURCE}))"
FPC_PATH="${SCRIPTDIR}/../.."
FABRIC_CFG_PATH="${SCRIPTDIR}/../../integration/config"
FABRIC_SCRIPTDIR="${FPC_PATH}/fabric/bin/"
. ${FABRIC_SCRIPTDIR}/lib/common_utils.sh
. ${FABRIC_SCRIPTDIR}/lib/common_ledger.sh
#this is the path that will be used for the docker build of the chaincode enclave
CC_PATH=${FPC_PATH}/examples/helloworld/_build/lib/
CC_ID=helloworld_test
CC_VER="$(cat ${CC_PATH}/mrenclave)"
CC_EP="OR('SampleOrg.member')"
CC_SEQ="1"
helloworld_test() {
say "- do hello world"
# install helloworld chaincode
# input: CC_ID:chaincode name; CC_VER:chaincode version;
# CC_PATH:path to build artifacts
say "- install helloworld chaincode"
PKG=/tmp/${CC_ID}.tar.gz
try ${PEER_CMD} lifecycle chaincode package --lang fpc-c --label ${CC_ID} --path ${CC_PATH} ${PKG}
try ${PEER_CMD} lifecycle chaincode install ${PKG}
PKG_ID=$(${PEER_CMD} lifecycle chaincode queryinstalled | awk "/Package ID: ${CC_ID}/{print}" | sed -n 's/^Package ID: //; s/, Label:.*$//;p')
try ${PEER_CMD} lifecycle chaincode approveformyorg -o ${ORDERER_ADDR} -C ${CHAN_ID} --package-id ${PKG_ID} --name ${CC_ID} --version ${CC_VER} --sequence ${CC_SEQ} --signature-policy ${CC_EP}
try ${PEER_CMD} lifecycle chaincode checkcommitreadiness -C ${CHAN_ID} --name ${CC_ID} --version ${CC_VER} --sequence ${CC_SEQ} --signature-policy ${CC_EP}
try ${PEER_CMD} lifecycle chaincode commit -o ${ORDERER_ADDR} -C ${CHAN_ID} --name ${CC_ID} --version ${CC_VER} --sequence ${CC_SEQ} --signature-policy ${CC_EP}
# create an FPC Chaincode enclave
try ${PEER_CMD} lifecycle chaincode initEnclave -o ${ORDERER_ADDR} --peerAddresses "localhost:7051" --name ${CC_ID}
# store the value of 100 in asset1
say "- invoke storeAsset transaction to store value 100 in asset1"
try ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Args":["storeAsset","asset1","100"]}' --waitForEvent
# retrieve current value for "asset1"; should be 100;
say "- invoke retrieveAsset transaction to retrieve current value of asset1"
try ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Args":["retrieveAsset","asset1"]}' --waitForEvent
say "- invoke query with retrieveAsset transaction to retrieve current value of asset1"
try ${PEER_CMD} chaincode query -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Args":["retrieveAsset","asset1"]}'
}
# 1. prepare
para
say "Preparing Helloworld Test ..."
# - clean up relevant docker images
docker_clean ${ERCC_ID}
docker_clean ${CC_ID}
trap ledger_shutdown EXIT
para
say "Run helloworld test"
say "- setup ledger"
ledger_init
say "- helloworld test"
helloworld_test
say "- shutdown ledger"
ledger_shutdown
para
yell "Helloworld test PASSED"
exit 0
Assuming we are still in $GOPATH/src/github.com/hyperledger-labs/fabric-private-chaincode/examples
, execute the test script:
cd $GOPATH/src/github.com/hyperledger-labs/fabric-private-chaincode/examples/helloworld
bash ./test.sh
If you see the message, test.sh: Helloworld test PASSED
, then the transactions have been successfully executed. Now, let us look the responses from the individual trasactions.
Output from test.sh for storeAsset
transaction invocation will look like:
test.sh: - invoke storeAsset transaction to store value 100 in asset1
peer.sh: /project/src/github.com/hyperledger/fabric/build/bin/peer chaincode query -o localhost:7050 -C mychannel -n ercc -c {"Args":["QueryChaincodeEncryptionKey", "helloworld_test"]}
peer.sh: /project/src/github.com/hyperledger/fabric/build/bin/peer chaincode query -o localhost:7050 -C mychannel -n ercc -c {"Args":["QueryChaincodeEndPoints", "helloworld_test"]}
peer.sh: /project/src/github.com/hyperledger/fabric/build/bin/peer chaincode query --peerAddresses localhost:7051 -o localhost:7050 -C mychannel -n helloworld_test -c {"Args":["__invoke", "CoACqxRdrnlNKNVavdNKiMD5jJn+p4goTugbfVyQMPZ6qQxaBFoqiXpSW6ubzj1d5yutxdyP9oDdbrRdEcMyuOcTVzx94mPpSLHEF+V6Hm+6VMbXE2M3JBgzP9U0kIrrgrekwOYVfXDlnWex9oSWSdLL2rGZJhBsODvZ/1K99Ey7X+8cHe9wTEnLT/RpcVjWrnxpXiJ/ZmfascANV5MRkRsyT1LAEZUDK8u6vhwdLLGbbXy5UmnLCqEMdrPEWHJNrULiKfRTUdUx9NrqwRyZgS0ZDL95MrRhEtgtE3gkP5olslFn5qm2TRSXh02CdTh5w94vOC55dK+BtZW2l3ckPS1UMQ=="]}
peer.sh: /project/src/github.com/hyperledger/fabric/build/bin/peer chaincode invoke -o localhost:7050 --waitForEvent -C mychannel -n helloworld_test -c {"Args":["__endorse", "CvoMCiAAGpKnvy4pnwKl/9aWElKT10xcM+5+O02AANy6OItihRI6CjgaNgoGYXNzZXQxGix3UVowT3prS0JmeHBDWWw2aXdGNlNEV2FKMWd2Z0drVVczLzA5a0duYm13PRq1CwrpCgrcBwpyCAMaDAjf+PaABhDI1OKsASIJbXljaGFubmVsKkBlOWY5Yjc0MWQzYjgxYTNhNzA2ZTVjNDQ1MDcyNzdiZjU1MzRlNDYxNWRjMmIwMjMzMmU4MWZlMTZjMTYwY2EwOhMSERIPaGVsbG93b3JsZF90ZXN0EuUGCsgGCglTYW1wbGVPcmcSugYtLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS0KTUlJQ05qQ0NBZDJnQXdJQkFnSVJBTW5mOS9kbVY5UnZDQ1Z3OXBaUVVmVXdDZ1lJS29aSXpqMEVBd0l3Z1lFeApDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saE1SWXdGQVlEVlFRSEV3MVRZVzRnClJuSmhibU5wYzJOdk1Sa3dGd1lEVlFRS0V4QnZjbWN4TG1WNFlXMXdiR1V1WTI5dE1Rd3dDZ1lEVlFRTEV3TkQKVDFBeEhEQWFCZ05WQkFNVEUyTmhMbTl5WnpFdVpYaGhiWEJzWlM1amIyMHdIaGNOTVRjeE1URXlNVE0wTVRFeApXaGNOTWpjeE1URXdNVE0wTVRFeFdqQnBNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0JNS1EyRnNhV1p2CmNtNXBZVEVXTUJRR0ExVUVCeE1OVTJGdUlFWnlZVzVqYVhOamJ6RU1NQW9HQTFVRUN4TURRMDlRTVI4d0hRWUQKVlFRREV4WndaV1Z5TUM1dmNtY3hMbVY0WVcxd2JHVXVZMjl0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowRApBUWNEUWdBRVo4UzRWNzFPQkpweU1JVlpkd1lkRlhBY2tJdHJwdlNyQ2YwSFFnNDBXVzlYU29PT083NkkrVW1mCkVrbVRsSUpYUDcvQXlSUlNSVTM4b0k4SXZ0dTRNNk5OTUVzd0RnWURWUjBQQVFIL0JBUURBZ2VBTUF3R0ExVWQKRXdFQi93UUNNQUF3S3dZRFZSMGpCQ1F3SW9BZ2luT1JJaG5QRUZaVWhYbTZlV0JrbTdLN1pjOFI0L3o3TFc0SApvc3NEbENzd0NnWUlLb1pJemowRUF3SURSd0F3UkFJZ1Zpa0lVWnpnZnVGc0dMUUhXSlVWSkNVN3BEYUVUa2F6ClB6RmdzQ2lMeFVBQ0lDZ3pKWWxXN252WnhQN2I2dGJldTN0OG1yaE1YUXM5NTZtRDQrQm9LdU5JCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KEhgBT3hTOjs8cnlaAk1LwePi40qsObkDW04ShwMKhAMKgQMIARIREg9oZWxsb3dvcmxkX3Rlc3Qa6QIKCF9faW52b2tlCtwCQ29BQ3F4UmRybmxOS05WYXZkTktpTUQ1akpuK3A0Z29UdWdiZlZ5UU1QWjZxUXhhQkZvcWlYcFNXNnViemoxZDV5dXR4ZHlQOW9EZGJyUmRFY015dU9jVFZ6eDk0bVBwU0xIRUYrVjZIbSs2Vk1iWEUyTTNKQmd6UDlVMGtJcnJncmVrd09ZVmZYRGxuV2V4OW9TV1NkTEwyckdaSmhCc09EdlovMUs5OUV5N1grOGNIZTl3VEVuTFQvUnBjVmpXcm54cFhpSi9abWZhc2NBTlY1TVJrUnN5VDFMQUVaVURLOHU2dmh3ZExMR2JiWHk1VW1uTENxRU1kclBFV0hKTnJVTGlLZlJUVWRVeDlOcnF3UnlaZ1MwWkRMOTVNclJoRXRndEUzZ2tQNW9sc2xGbjVxbTJUUlNYaDAyQ2RUaDV3OTR2T0M1NWRLK0J0WlcybDNja1BTMVVNUT09EkcwRQIhANz3BNlcghQhaGzN+Z7rem/JhcDXL8h2y3i4F7vFF7MBAiATGtz3GxXtCQZqJ2cjtOjhLChhkcqyIGclIQgC4W7JpSIgUCM/JIUzwZ7qRQXb1i/NmHUhccYDMQEqYBwa7KTfttcqQEVCNjA5M0ZCRTE4MTk0MTRGRUU3MzUxODAyRjYyNkE5NjY3OTFBNkQ4OTQzMkIwRTJCNURGNDBFOTNEMUI0Q0YSRjBEAiAtX4zseT2meebl5vLdCnlt5hLnH+QA3U4yAa9x7YuhEgIgY0LkRSBJkA3SW7Rpj27dXLvSmVd8kyK8c2Srqz/ZhIQ="]}
Response from the transaction is:
OK
Let us look at the output of retrieveAsset
transaction.
test.sh: - invoke retrieveAsset transaction to retrieve current value of asset1
peer.sh: /project/src/github.com/hyperledger/fabric/build/bin/peer chaincode query -o localhost:7050 -C mychannel -n ercc -c {"Args":["QueryChaincodeEncryptionKey", "helloworld_test"]}
peer.sh: /project/src/github.com/hyperledger/fabric/build/bin/peer chaincode query -o localhost:7050 -C mychannel -n ercc -c {"Args":["QueryChaincodeEndPoints", "helloworld_test"]}
peer.sh: /project/src/github.com/hyperledger/fabric/build/bin/peer chaincode query --peerAddresses localhost:7051 -o localhost:7050 -C mychannel -n helloworld_test -c {"Args":["__invoke", "CoACGbE4bFpQaubzAw/XQPRJnuD2bC3ZcCV4b9TC0gmrPIe+YBYMHYb5WDhNAPrgJvSCLiJbXb15qzp1C0I0lhSVadVLH465CL5huLbDwtmLswpGDVlswHPHmik+ce7Xx04hEqNupu3VUxar6dUdcUh8wyyW2fgj/q1f96ZAm4SH/DAjdu7y3qSqIlPp1LMpz+7SpM6/AVAP3aSuDetooyTitDjYMHU7OUxkj2pH41MSqA0g65/bszPQXKbcc49FivwnKEKyIV8KyXVrPB1s8JkpP89HfCQxmkRU3pmukMi4jkLxYT4tXz4MCwbJJp4K6aleCaz1sjdHONcIBveFb/nekQ=="]}
peer.sh: /project/src/github.com/hyperledger/fabric/build/bin/peer chaincode invoke -o localhost:7050 --waitForEvent -C mychannel -n helloworld_test -c {"Args":["__endorse", "CvkMCiyEzRpNIXSgFVu14waJqpDwF6JIKQy0sLD2hoPjt3/Qhayc6Im3VCF1FCboqhIuCgoKCAoGYXNzZXQxEiBu8sguYb+l8QfD8ToPBdPqoJbiZhYt/6AaZvCDqo47ARq0CwrpCgrcBwpyCAMaDAji+PaABhC4hKWDAyIJbXljaGFubmVsKkBkNTVkYmQwNmNmZWUxMmJhYzczNjc1MzlkMTFmNGUxY2QyOGZjNzNlNTJmMjczYjMwMDkzYWMxNTBhOWFhNDVlOhMSERIPaGVsbG93b3JsZF90ZXN0EuUGCsgGCglTYW1wbGVPcmcSugYtLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS0KTUlJQ05qQ0NBZDJnQXdJQkFnSVJBTW5mOS9kbVY5UnZDQ1Z3OXBaUVVmVXdDZ1lJS29aSXpqMEVBd0l3Z1lFeApDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saE1SWXdGQVlEVlFRSEV3MVRZVzRnClJuSmhibU5wYzJOdk1Sa3dGd1lEVlFRS0V4QnZjbWN4TG1WNFlXMXdiR1V1WTI5dE1Rd3dDZ1lEVlFRTEV3TkQKVDFBeEhEQWFCZ05WQkFNVEUyTmhMbTl5WnpFdVpYaGhiWEJzWlM1amIyMHdIaGNOTVRjeE1URXlNVE0wTVRFeApXaGNOTWpjeE1URXdNVE0wTVRFeFdqQnBNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0JNS1EyRnNhV1p2CmNtNXBZVEVXTUJRR0ExVUVCeE1OVTJGdUlFWnlZVzVqYVhOamJ6RU1NQW9HQTFVRUN4TURRMDlRTVI4d0hRWUQKVlFRREV4WndaV1Z5TUM1dmNtY3hMbVY0WVcxd2JHVXVZMjl0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowRApBUWNEUWdBRVo4UzRWNzFPQkpweU1JVlpkd1lkRlhBY2tJdHJwdlNyQ2YwSFFnNDBXVzlYU29PT083NkkrVW1mCkVrbVRsSUpYUDcvQXlSUlNSVTM4b0k4SXZ0dTRNNk5OTUVzd0RnWURWUjBQQVFIL0JBUURBZ2VBTUF3R0ExVWQKRXdFQi93UUNNQUF3S3dZRFZSMGpCQ1F3SW9BZ2luT1JJaG5QRUZaVWhYbTZlV0JrbTdLN1pjOFI0L3o3TFc0SApvc3NEbENzd0NnWUlLb1pJemowRUF3SURSd0F3UkFJZ1Zpa0lVWnpnZnVGc0dMUUhXSlVWSkNVN3BEYUVUa2F6ClB6RmdzQ2lMeFVBQ0lDZ3pKWWxXN252WnhQN2I2dGJldTN0OG1yaE1YUXM5NTZtRDQrQm9LdU5JCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KEhg6Rz4AFR7yI+3X1bdhVk7rPS+piryTqagShwMKhAMKgQMIARIREg9oZWxsb3dvcmxkX3Rlc3Qa6QIKCF9faW52b2tlCtwCQ29BQ0diRTRiRnBRYXViekF3L1hRUFJKbnVEMmJDM1pjQ1Y0YjlUQzBnbXJQSWUrWUJZTUhZYjVXRGhOQVByZ0p2U0NMaUpiWGIxNXF6cDFDMEkwbGhTVmFkVkxINDY1Q0w1aHVMYkR3dG1Mc3dwR0RWbHN3SFBIbWlrK2NlN1h4MDRoRXFOdXB1M1ZVeGFyNmRVZGNVaDh3eXlXMmZnai9xMWY5NlpBbTRTSC9EQWpkdTd5M3FTcUlsUHAxTE1weis3U3BNNi9BVkFQM2FTdURldG9veVRpdERqWU1IVTdPVXhrajJwSDQxTVNxQTBnNjUvYnN6UFFYS2JjYzQ5Rml2d25LRUt5SVY4S3lYVnJQQjFzOEprcFA4OUhmQ1F4bWtSVTNwbXVrTWk0amtMeFlUNHRYejRNQ3diSkpwNEs2YWxlQ2F6MXNqZEhPTmNJQnZlRmIvbmVrUT09EkYwRAIgUfrI86tO/OpDNEny4GHP4N3p8HWza0rEpZwPZfB9hEYCIH8P7UCwBQE9gQ8Q6k6PfKMC8L2r6ayNIT61KyZK4JRyIiAMstDJrHzmQk2UsMjsABWaqNt8Sbiaau1Cnhs+j2pMjipARUI2MDkzRkJFMTgxOTQxNEZFRTczNTE4MDJGNjI2QTk2Njc5MUE2RDg5NDMyQjBFMkI1REY0MEU5M0QxQjRDRhJHMEUCIQDuN5LKLukJ2mCUA8yWBqVO18g2+uDAMniVIrcgOskb8AIgGST4CSedNDJNZXYKkdPrgPb+H8hV+RD7pykLpUBnCSU="]}
Response from the transaction is:
asset1:100
Yay ! You did it !
If you want to interactively test FPC, you can use the commands in ../fabric/bin/
. It contains the standard cli commands you expect from fabric -- note: it is though important that you use these scripts rather than use the fabric commands directly. The provide the same interface as the fabric commands but do some additional magic under the cover. Additionally, there are also two convenience functions for quickly setting up and shutting down a ledger: ledger_init.sh
and ledger_shutdown.sh
.