Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Peer cli assist for client request/response processing #492

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions client_sdk/go/fpc/crypto/crypto.go
Original file line number Diff line number Diff line change
@@ -70,14 +70,31 @@ type EncryptionContextImpl struct {
chaincodeEncryptionKey []byte
}

func (e *EncryptionContextImpl) Reveal(responseBytes []byte) ([]byte, error) {
func (e *EncryptionContextImpl) Reveal(responseBytesB64 []byte) ([]byte, error) {
responseBytes, err := base64.StdEncoding.DecodeString(string(responseBytesB64))
if err != nil {
return nil, err
}

response := &protos.ChaincodeResponseMessage{}
err := proto.Unmarshal(responseBytes, response)
err = proto.Unmarshal(responseBytes, response)
if err != nil {
return nil, err
}

clearResponseB64, err := decrypt(response.EncryptedResponse, e.resultEncryptionKey)
if err != nil {
return nil, err
}
// TODO: above should eventually be a (protobuf but not base64 serialized) fabric response object,
// rather than just the (base64-serialized) response string.
// so we also get fpc chaincode return-code/error-message as in for normal fabric
clearResponse, err := base64.StdEncoding.DecodeString(string(clearResponseB64))
if err != nil {
return nil, err
}

return decrypt(response.EncryptedResponse, e.resultEncryptionKey)
return clearResponse, nil
}

func (e *EncryptionContextImpl) Conceal(function string, args []string) (string, error) {
24 changes: 20 additions & 4 deletions ecc/chaincode/ecc.go
Original file line number Diff line number Diff line change
@@ -102,24 +102,40 @@ func (t *EnclaveChaincode) initEnclave(stub shim.ChaincodeStubInterface) pb.Resp
func (t *EnclaveChaincode) invoke(stub shim.ChaincodeStubInterface) pb.Response {
// call enclave
var errMsg string
b64ChaincodeResponseMessage, errInvoke := t.enclave.ChaincodeInvoke(stub)

// prep chaincode request message as input
_, args := stub.GetFunctionAndParameters()
if len(args) != 1 {
return shim.Error("no chaincodeRequestMessage as argument found")
}
chaincodeRequestMessageB64 := args[0]
chaincodeRequestMessage, err := base64.StdEncoding.DecodeString(chaincodeRequestMessageB64)
if err != nil {
errMsg := fmt.Sprintf("cannot base64 decode ChaincodeRequestMessage ('%s'): %s", chaincodeRequestMessageB64, err.Error())
return shim.Error(errMsg)
}

chaincodeResponseMessage, errInvoke := t.enclave.ChaincodeInvoke(stub, chaincodeRequestMessage)
if errInvoke != nil {
errMsg = fmt.Sprintf("t.enclave.Invoke failed: %s", errInvoke)
logger.Errorf(errMsg)
// likely a chaincode error, so we still want response go back ...
}

chaincodeResponseMessageB64 := []byte(base64.StdEncoding.EncodeToString(chaincodeResponseMessage))
logger.Debugf("base64-encoded response message: '%s'", chaincodeResponseMessageB64)

var response pb.Response
if errInvoke == nil {
response = pb.Response{
Status: shim.OK,
Payload: b64ChaincodeResponseMessage,
Payload: chaincodeResponseMessageB64,
Message: errMsg,
}
} else {
response = pb.Response{
Status: shim.ERROR,
Payload: b64ChaincodeResponseMessage,
Payload: chaincodeResponseMessageB64,
Message: errMsg,
}
}
@@ -180,7 +196,7 @@ func (t *EnclaveChaincode) endorse(stub shim.ChaincodeStubInterface) pb.Response
return shim.Error(err.Error())
}

return shim.Success(nil)
return shim.Success([]byte("OK")) // make sure we have a non-empty return on success so we can distinguish success from failure in cli ...
}

func ccParamsMatch(expected, actual *protos.CCParameters) bool {
18 changes: 4 additions & 14 deletions ecc/chaincode/enclave/enclave.go
Original file line number Diff line number Diff line change
@@ -12,7 +12,6 @@ package enclave
import "C"
import (
"context"
"encoding/base64"
"fmt"
"unsafe"

@@ -89,15 +88,15 @@ func (e *EnclaveStub) Init(chaincodeParams, hostParams, attestationParams []byte
return C.GoBytes(credentialsBuffer, C.int(credentialsSize)), nil
}

func (e *EnclaveStub) GenerateCCKeys() (*protos.SignedCCKeyRegistrationMessage, error) {
func (e *EnclaveStub) GenerateCCKeys() ([]byte, error) {
panic("implement me")
}

func (e *EnclaveStub) ExportCCKeys(credentials *protos.Credentials) (*protos.SignedExportMessage, error) {
func (e *EnclaveStub) ExportCCKeys(credentials []byte) ([]byte, error) {
panic("implement me")
}

func (e *EnclaveStub) ImportCCKeys() (*protos.SignedCCKeyRegistrationMessage, error) {
func (e *EnclaveStub) ImportCCKeys() ([]byte, error) {
panic("implement me")
}

@@ -106,7 +105,7 @@ func (e *EnclaveStub) GetEnclaveId() (string, error) {
}

// ChaincodeInvoke calls the enclave for transaction processing
func (e *EnclaveStub) ChaincodeInvoke(stub shim.ChaincodeStubInterface) ([]byte, error) {
func (e *EnclaveStub) ChaincodeInvoke(stub shim.ChaincodeStubInterface, crmProtoBytes []byte) ([]byte, error) {
if !e.isInitialized {
return nil, fmt.Errorf("enclave not yet initialized")
}
@@ -133,15 +132,6 @@ func (e *EnclaveStub) ChaincodeInvoke(stub shim.ChaincodeStubInterface) ([]byte,
cresmProtoBytesPtr := C.malloc(maxResponseSize)
defer C.free(cresmProtoBytesPtr)

// prep chaincode request message as input
_, args := stub.GetFunctionAndParameters()
if len(args) != 1 {
return nil, fmt.Errorf("no chaincodeRequestMessage as argument found")
}
crmProtoBytes, err := base64.StdEncoding.DecodeString(args[0])
if err != nil {
return nil, fmt.Errorf("cannot decode ChaincodeRequestMessage: %s", err.Error())
}
crmProtoBytesPtr := C.CBytes(crmProtoBytes)
defer C.free(unsafe.Pointer(crmProtoBytesPtr))

29 changes: 19 additions & 10 deletions ecc/chaincode/enclave/interface.go
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@ SPDX-License-Identifier: Apache-2.0
package enclave

import (
"github.com/hyperledger-labs/fabric-private-chaincode/internal/protos"
"github.com/hyperledger/fabric-chaincode-go/shim"
"github.com/hyperledger/fabric/common/flogging"
)
@@ -17,19 +16,29 @@ var logger = flogging.MustGetLogger("enclave")

type StubInterface interface {

// Init initializes the chaincode enclave.
// The input and output parameters are serialized protobufs
// triggered by an admin
Init(chaincodeParams, hostParams, attestationParams []byte) ([]byte, error)
Init(chaincodeParams, hostParams, attestationParams []byte) (credentials []byte, err error)

// key generation
GenerateCCKeys() (*protos.SignedCCKeyRegistrationMessage, error)
// GetEnclaveId returns the EnclaveId hosted by the peer
GetEnclaveId() (string, error)

// key distribution (Post-MVP Feature)
ExportCCKeys(credentials *protos.Credentials) (*protos.SignedExportMessage, error)
ImportCCKeys() (*protos.SignedCCKeyRegistrationMessage, error)

// returns the EnclaveId hosted by the peer
GetEnclaveId() (string, error)
// GenerateCCKeys, key generation
// The output parameters is a serialized protobuf
GenerateCCKeys() (signedCCKeyRegistrationMessage []byte, err error)

// ExportCCKeys exports chaincode secrets to enclave with provided credentials
// The input and output parameters are serialized protobufs
ExportCCKeys(credentials []byte) (signedExportMessage []byte, err error)

// ImportCCKeys imports chaincode secrets
// The output parameters is a serialized protobuf
ImportCCKeys() (signedCCKeyRegistrationMessage []byte, err error)

// chaincode invoke
ChaincodeInvoke(stub shim.ChaincodeStubInterface) ([]byte, error)
// ChaincodeInvoke invokes fpc chaincode inside enclave
// chaincodeRequestMessage and chaincodeResponseMessage are serialized protobuf
ChaincodeInvoke(stub shim.ChaincodeStubInterface, chaincodeRequestMessage []byte) (chaincodeResponseMessage []byte, err error)
}
11 changes: 7 additions & 4 deletions ecc/chaincode/enclave/mock_enclave.go
Original file line number Diff line number Diff line change
@@ -83,16 +83,19 @@ func (m *MockEnclaveStub) Init(serializedChaincodeParams, serializedHostParamsBy
return proto.Marshal(credentials)
}

func (m MockEnclaveStub) GenerateCCKeys() (*protos.SignedCCKeyRegistrationMessage, error) {
func (m MockEnclaveStub) GenerateCCKeys() ([]byte, error) {
panic("implement me")
// -> *protos.SignedCCKeyRegistrationMessage
}

func (m MockEnclaveStub) ExportCCKeys(credentials *protos.Credentials) (*protos.SignedExportMessage, error) {
func (m MockEnclaveStub) ExportCCKeys(credentials []byte) ([]byte, error) {
panic("implement me")
// credentials *protos.Credentials -> *protos.SignedExportMessage,
}

func (m MockEnclaveStub) ImportCCKeys() (*protos.SignedCCKeyRegistrationMessage, error) {
func (m MockEnclaveStub) ImportCCKeys() ([]byte, error) {
panic("implement me")
// -> *protos.SignedCCKeyRegistrationMessage
}

func (m *MockEnclaveStub) GetEnclaveId() (string, error) {
@@ -104,7 +107,7 @@ func (m *MockEnclaveStub) GetEnclaveId() (string, error) {
return strings.ToUpper(hex.EncodeToString(hash[:])), nil
}

func (m *MockEnclaveStub) ChaincodeInvoke(stub shim.ChaincodeStubInterface) ([]byte, error) {
func (m *MockEnclaveStub) ChaincodeInvoke(stub shim.ChaincodeStubInterface, chaincodeRequestMessage []byte) ([]byte, error) {
logger.Debug("ChaincodeInvoke")

signedProposal, err := stub.GetSignedProposal()
37 changes: 20 additions & 17 deletions fabric/bin/lib/common_ledger.sh
Original file line number Diff line number Diff line change
@@ -182,28 +182,31 @@ ledger_shutdown() {

# Check the chaincode's response (ResponseData) of
# peer chaincode invoke/query
# (executed via 'try_r' macro) against expected result.
# In case of failures, tries to increment integer variable FAILURES
# (executed via 'try_r/try_out_r' macro) against expected result.
# In case of failures, tries to increment integer variable NUM_FAILURES. Increment in all cases NUM_TESTS
check_result() {
# Parse out the Response Data from the payload
CI_RESPONSE=${RESPONSE}
CI_RESPONSE=${CI_RESPONSE##*payload:\"}
CI_RESPONSE=${CI_RESPONSE%%\"*}
# Convert and de-encrypt it
CI_RESPONSE=$(echo ${CI_RESPONSE} | base64 -d)
say "b64 Decoded response: ${CI_RESPONSE}"
# Test response to expected result
if [ "${RESPONSE_TYPE}" == "out+err" ]; then
CI_RESPONSE=$(parse_invoke_result_from_log "${RESPONSE}")
CONTEXT=" context: '${RESPONSE}'"
else
CI_RESPONSE="${RESPONSE}"
CONTEXT=""
fi

if [[ ${CI_RESPONSE} == "$1" ]]; then
gecho "PASSED"
else
if [[ ${CI_RESPONSE} == $RESPONSE ]]; then
CONTEXT=""
else
CONTEXT=" context: '${RESPONSE}'"
fi
recho "FAILED (expected '${1}', got '${CI_RESPONSE}' ${CONTEXT})"
export FAILURES=$(($FAILURES+1))
recho "FAILED (expected '${1}', got '${CI_RESPONSE}'${CONTEXT})"
export NUM_FAILURES=$(($NUM_FAILURES+1))
fi
export NUM_TESTS=$(($NUM_TESTS+1))
}

parse_invoke_result_from_log() {
RESPONSE="$1"
RESPONSE=${RESPONSE##*payload:\"}
RESPONSE=${RESPONSE%%\"*}
echo "${RESPONSE}"
}


16 changes: 7 additions & 9 deletions fabric/bin/lib/common_utils.sh
Original file line number Diff line number Diff line change
@@ -43,9 +43,9 @@ function gecho () {

# Common reporting functions: say, yell & die
#-----------------------------------------
# say is stdout, yell is stderr
# they all write to stderr. if you want normal progres for stdout, just use echo
function say () {
echo "$(basename $0): $*"
echo "$(basename $0): $*" >&2;
}

function yell () {
@@ -72,16 +72,14 @@ try_fail() {
(! "$@") || die "rev-test failed: $*"
}

# Variant of try which stores commands stdout and stderr in variable RESPONSE
# Variant of try which stores commands stdout and stderr (or only stdout) in variable RESPONSE
try_r() {
echo "$@"
export RESPONSE=$("$@" 2>&1) || die "test failed: $*"
echo $RESPONSE
say "$@"
export RESPONSE=$("$@" 2>&1) RESPONSE_TYPE="out+err" || die "test failed: $*"
}

try_out_r() {
echo "$@"
export RESPONSE=$("$@") || die "test failed: $*"
echo $RESPONSE
say "$@"
export RESPONSE=$("$@") RESPONSE_TYPE="out" || die "test failed: $*"
}

296 changes: 238 additions & 58 deletions fabric/bin/peer.sh

Large diffs are not rendered by default.

53 changes: 27 additions & 26 deletions integration/auction_test.sh
Original file line number Diff line number Diff line change
@@ -22,7 +22,8 @@ CC_EP="OR('SampleOrg.member')" # note that we use .member as NodeOUs is disabled

num_rounds=3
num_clients=10
FAILURES=0
NUM_FAILURES=0
NUM_TESTS=0

auction_test() {
PKG=/tmp/${CC_ID}.tar.gz
@@ -46,78 +47,78 @@ auction_test() {

# Scenario 1
becho ">>>> Close and evaluate non existing auction. Response should be AUCTION_NOT_EXISTING"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"init", "Args": ["MyAuctionHouse"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"init", "Args": ["MyAuctionHouse"]}' --waitForEvent
check_result "OK"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"close", "Args": ["MyAuction"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"close", "Args": ["MyAuction"]}' --waitForEvent
check_result "AUCTION_NOT_EXISTING"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"eval", "Args": ["MyAuction0"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"eval", "Args": ["MyAuction0"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
check_result "AUCTION_NOT_EXISTING"

# Scenario 2
becho ">>>> Create an auction. Response should be OK"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"init", "Args": ["MyAuctionHouse"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"init", "Args": ["MyAuctionHouse"]}' --waitForEvent
check_result "OK"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"create", "Args": ["MyAuction1"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"create", "Args": ["MyAuction1"]}' --waitForEvent
check_result "OK"
becho ">>>> Create two equivalent bids. Response should be OK"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"submit", "Args": ["MyAuction1", "JohnnyCash0", "2"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"submit", "Args": ["MyAuction1", "JohnnyCash0", "2"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
check_result "OK"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"submit", "Args": ["MyAuction1", "JohnnyCash1", "2"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"submit", "Args": ["MyAuction1", "JohnnyCash1", "2"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
check_result "OK"
becho ">>>> Close auction. Response should be OK"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"close", "Args": ["MyAuction1"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"close", "Args": ["MyAuction1"]}' --waitForEvent
check_result "OK"
becho ">>>> Submit a bid on a closed auction. Response should be AUCTION_ALREADY_CLOSED"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"submit", "Args": ["MyAuction1", "JohnnyCash2", "2"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"submit", "Args": ["MyAuction1", "JohnnyCash2", "2"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
check_result "AUCTION_ALREADY_CLOSED";
becho ">>>> Evaluate auction. Response should be DRAW"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"eval", "Args": ["MyAuction1"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"eval", "Args": ["MyAuction1"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
check_result "DRAW"

# Scenario 3
becho ">>>> Create an auction. Response should be OK"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"init", "Args": ["MyAuctionHouse"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"init", "Args": ["MyAuctionHouse"]}' --waitForEvent
check_result "OK"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"create", "Args": ["MyAuction2"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"create", "Args": ["MyAuction2"]}' --waitForEvent
check_result "OK"
for (( i=0; i<=$num_rounds; i++ ))
do
becho ">>>> Submit unique bid. Response should be OK"
b="$(($i%$num_clients))"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"submit", "Args": ["MyAuction2", "JohnnyCash'$b'", "'$b'"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"submit", "Args": ["MyAuction2", "JohnnyCash'$b'", "'$b'"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
check_result "OK"
done
becho ">>>> Close auction. Response should be OK"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"close", "Args": ["MyAuction2"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"close", "Args": ["MyAuction2"]}' --waitForEvent
check_result "OK"
becho ">>>> Evaluate auction. Auction Result should be printed out"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"eval", "Args": ["MyAuction2"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"eval", "Args": ["MyAuction2"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
check_result '{"bidder":"JohnnyCash3","value":3}'

# Scenario 4
becho ">>>> Create a new auction. Response should be OK"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"init", "Args": ["MyAuctionHouse"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"init", "Args": ["MyAuctionHouse"]}' --waitForEvent
check_result "OK"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"create", "Args": ["MyAuction3"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"create", "Args": ["MyAuction3"]}' --waitForEvent
check_result "OK"
becho ">>>> Create a duplicate auction. Response should be AUCTION_ALREADY_EXISTING"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"create", "Args": ["MyAuction3"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"create", "Args": ["MyAuction3"]}' --waitForEvent
check_result "AUCTION_ALREADY_EXISTING"
becho ">>>> Close auction and evaluate. Response should be OK"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"close", "Args": ["MyAuction3"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"close", "Args": ["MyAuction3"]}' --waitForEvent
check_result "OK"
becho ">>>> Close an already closed auction. Response should be AUCTION_ALREADY_CLOSED"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"close", "Args": ["MyAuction3"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"close", "Args": ["MyAuction3"]}' --waitForEvent
check_result "AUCTION_ALREADY_CLOSED"
becho ">>>> Evaluate auction. Response should be NO_BIDS"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"eval", "Args": ["MyAuction3"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"eval", "Args": ["MyAuction3"]}' # Don't do --waitForEvent, so potentially there is some parallelism here ..
check_result "NO_BIDS"

# Code below is used to test bug in issue #42
becho ">>>> Create a new auction. Response should be OK"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"init", "Args": ["MyAuctionHouse"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"init", "Args": ["MyAuctionHouse"]}' --waitForEvent
check_result "OK"
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"create", "Args": ["MyAuction4"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Function":"create", "Args": ["MyAuction4"]}' --waitForEvent
check_result "OK"
}

@@ -142,10 +143,10 @@ say "- shutdown ledger"
ledger_shutdown

para
if [[ "$FAILURES" == 0 ]]; then
if [[ "$NUM_FAILURES" == 0 ]]; then
yell "Auction test PASSED"
else
yell "Auction test had ${FAILURES} failures"
yell "Auction test had ${NUM_FAILURES} failures out of ${NUM_TESTS} tests"
exit 1
fi
exit 0
20 changes: 11 additions & 9 deletions integration/deployment_test.sh
Original file line number Diff line number Diff line change
@@ -17,7 +17,8 @@ FABRIC_SCRIPTDIR="${FPC_PATH}/fabric/bin/"
. ${FABRIC_SCRIPTDIR}/lib/common_ledger.sh

CC_EP="OR('SampleOrg.member')" # note that we use .member as NodeOUs is disabled with the crypto material used in the integration tests.
FAILURES=0
NUM_FAILURES=0
NUM_TESTS=0

run_test() {

@@ -51,13 +52,13 @@ run_test() {
try ${PEER_CMD} lifecycle chaincode checkcommitreadiness -C ${CHAN_ID} --name marbles02 --version ${CC_VER} --sequence ${CC_SEQ}
try ${PEER_CMD} lifecycle chaincode commit -o ${ORDERER_ADDR} -C ${CHAN_ID} --name marbles02 --version ${CC_VER} --sequence ${CC_SEQ}

try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n auction_test -c '{"Args":["init", "MyAuctionHouse"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n auction_test -c '{"Args":["init", "MyAuctionHouse"]}' --waitForEvent
check_result "OK"

try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n auction_test -c '{"Args":["create", "MyAuction"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n auction_test -c '{"Args":["create", "MyAuction"]}' --waitForEvent
check_result "OK"

try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n marbles02 -c '{"Args":["initMarble","marble1","blue","35","tom"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n marbles02 -c '{"Args":["initMarble","marble1","blue","35","tom"]}' --waitForEvent

# install examples/echo
CC_PATH=${FPC_PATH}/examples/echo/_build/lib/
@@ -85,15 +86,15 @@ run_test() {
try_fail ${PEER_CMD} lifecycle chaincode initEnclave -o ${ORDERER_ADDR} --peerAddresses "localhost:7051" --name wrong-cc-id
try ${PEER_CMD} lifecycle chaincode initEnclave -o ${ORDERER_ADDR} --peerAddresses "localhost:7051" --name echo_test

try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n echo_test -c '{"Args": ["moin"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n echo_test -c '{"Args": ["moin"]}' --waitForEvent
check_result "moin"

try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n auction_test -c '{"Args":["submit", "MyAuction", "JohnnyCash0", "0"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n auction_test -c '{"Args":["submit", "MyAuction", "JohnnyCash0", "0"]}' --waitForEvent
check_result "OK"

try ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n marbles02 -c '{"Args":["readMarble","marble1"]}' --waitForEvent

try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n echo_test -c '{"Args": ["bonjour"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n echo_test -c '{"Args": ["bonjour"]}' --waitForEvent
check_result "bonjour"
}

@@ -127,11 +128,12 @@ say "- shutdown ledger"
ledger_shutdown

para
if [[ "$FAILURES" == 0 ]]; then
if [[ "$NUM_FAILURES" == 0 ]]; then
yell "Deployement test PASSED"
else
yell "Deployement test had ${FAILURES} failures"
yell "Deployement test had ${NUM_FAILURES} failures out of ${NUM_TESTS} tests"
exit 1
fi
exit 0


9 changes: 5 additions & 4 deletions integration/echo_test.sh
Original file line number Diff line number Diff line change
@@ -21,7 +21,8 @@ CC_SEQ="1"
CC_EP="OR('SampleOrg.member')" # note that we use .member as NodeOUs is disabled with the crypto material used in the integration tests.

num_rounds=10
FAILURES=0
NUM_FAILURES=0
NUM_TESTS=0

echo_test() {
PKG=/tmp/${CC_ID}.tar.gz
@@ -56,7 +57,7 @@ echo_test() {
for (( i=1; i<=$num_rounds; i++ ))
do
# echos
try_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Args": ["echo-'$i'"]}' --waitForEvent
try_out_r ${PEER_CMD} chaincode invoke -o ${ORDERER_ADDR} -C ${CHAN_ID} -n ${CC_ID} -c '{"Args": ["echo-'$i'"]}' --waitForEvent
check_result "echo-$i"
done
}
@@ -83,10 +84,10 @@ say "- shutdown ledger"
ledger_shutdown

para
if [[ "$FAILURES" == 0 ]]; then
if [[ "$NUM_FAILURES" == 0 ]]; then
yell "Echo test PASSED"
else
yell "Echo test had ${FAILURES} failures"
yell "Echo test had ${NUM_FAILURES} failures out of ${NUM_TESTS} tests"
exit 1
fi
exit 0
4 changes: 2 additions & 2 deletions integration/test-network/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ services:
ecc.peer0.org1.example.com:
container_name: ${CC_ID}.peer0.org1.example.com
hostname: ${CC_ID}.peer0.org1.example.com
image: fpc/fpc-echo${HW_EXTENSION:-}
image: fpc/fpc-${CC_ID}${HW_EXTENSION:-}
entrypoint: /usr/local/bin/chaincode
environment:
- CHAINCODE_SERVER_ADDRESS=${CC_ID}.peer0.org1.example.com:9999
@@ -41,7 +41,7 @@ services:
ecc.peer0.org2.example.com:
container_name: ${CC_ID}.peer0.org2.example.com
hostname: ${CC_ID}.peer0.org2.example.com
image: fpc/fpc-echo${HW_EXTENSION:-}
image: fpc/fpc-${CC_ID}${HW_EXTENSION:-}
entrypoint: /usr/local/bin/chaincode
environment:
- CHAINCODE_SERVER_ADDRESS=${CC_ID}.peer0.org2.example.com:9999
4 changes: 3 additions & 1 deletion utils/fabric/Makefile
Original file line number Diff line number Diff line change
@@ -10,9 +10,11 @@ GO_CMDS= get-fabric-container-name peer-cli-assist

build: $(GO_CMDS)

$(GO_CMDS):
$(GO_CMDS): FORCE
$(GO) build $(GOTAGS) ./$@.src/$@.go

FORCE:

test: build

clean:
129 changes: 119 additions & 10 deletions utils/fabric/peer-cli-assist.src/peer-cli-assist.go
Original file line number Diff line number Diff line change
@@ -7,21 +7,40 @@
package main

import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strings"

"github.com/hyperledger-labs/fabric-private-chaincode/client_sdk/go/fpc"
"github.com/hyperledger-labs/fabric-private-chaincode/client_sdk/go/fpc/crypto"
"github.com/hyperledger/fabric/common/flogging"
)

var logger = flogging.MustGetLogger("peer-cli-assist")

func printHelp() {
fmt.Printf(
`Usage: %s [attestation2Evidence | createEncryptRequest | processEncryptedResponse]
`Usage: %s [attestation2Evidence | handleRequestAndResponse <cid> <pipe>]
- attestation2Evidence: convert attestation to evidence in (base64-encoded) Credentials protobuf
- createEncryptRequest: create a (base64-encoded) encrypted fpc chaincode request protobuf
- processEncryptedResponse: decrypt and validate an (base64-encoded) encrypted fpc chaincode response protobuf
Input and outpus are via stdin and stdout, respectively.`,
(Input and outpus are via stdin and stdout, respectively.)
- handleRequestAndResponse: handles the encryption of invocation requests as well as the decryption
of the corresponding responses.
Expects three parameters
- <c_ek> the chaincode encryption key, as returned from ercc.QueryChaincodeEncryptionKey
(i.e., a base64-encoded string)
- <pipe> a path to an (existing) fifo file through which the results are communicated back
As input, expects two lines
- a (single line!) json string in peer cli format '{"Function": "...", "Args": [...]}' with the invocation params,
after which it returns (as single line) the (base64-encoded) ChaincodeRequestMessage protobuf, and then
- a (base64-encoded) ChaincodeResponseMessage protobuf, after which it will decrypt it and
return a json-encoded fabric response protobuf object
`,
os.Args[0])
// TODO: above we have to fix the response payload format (json?)
}

func main() {
@@ -45,12 +64,13 @@ func main() {
os.Exit(1)
}
fmt.Printf("%s\n", credentialsStringOut)
case "createEncryptRequest":
fmt.Fprintf(os.Stderr, "FATAL: command %s not yet implemented\n", os.Args[1])
os.Exit(1)
case "processEncryptedResponse":
fmt.Fprintf(os.Stderr, "FATAL: command %s not yet implemented\n", os.Args[1])
os.Exit(1)
case "handleRequestAndResponse":
if len(os.Args) != 4 {
fmt.Fprintf(os.Stderr, "ERROR: command 'handleRequestAndResponse' needs exactly two arguments\n")
printHelp()
os.Exit(1)
}
handleEncryptedRequestAndResponse(os.Args[2], os.Args[3])
default:
fmt.Fprintf(os.Stderr, "ERROR: Illegal command '%s'\n", os.Args[1])
printHelp()
@@ -59,3 +79,92 @@ func main() {

os.Exit(0)
}

func handleEncryptedRequestAndResponse(chaincodeEncryptionKey string, resultPipeName string) {
reader := bufio.NewReader(os.Stdin)
resultPipeFile, err := os.OpenFile(resultPipeName, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: couldn't open pipe '%s': %v\n", resultPipeName, err)
os.Exit(1)
}

// read request
requestJSON, err := reader.ReadString('\n')
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: couldn't read json request: %v\n", err)
os.Exit(1)
}
requestJSON = strings.TrimSpace(requestJSON)
type Request struct {
Function *string `json:"function,omitempty"`
Args *[]string `json:"args,omitempty"`
}
clearRequest := &Request{}
dec := json.NewDecoder(bytes.NewReader([]byte(requestJSON)))
dec.DisallowUnknownFields()
if err := dec.Decode(&clearRequest); err != nil {
fmt.Fprintf(os.Stderr, "ERROR: unexpected json '%s': %v\n", requestJSON, err)
os.Exit(1)
}
// Note fabric has two invocation formats, i.e., missing Function means function is Args[0]
if clearRequest.Args == nil {
clearRequest.Args = &([]string{})
}
if clearRequest.Function == nil {
if len(*clearRequest.Args) > 0 {
clearRequest.Function = &((*clearRequest.Args)[0])
remainingArgs := (*clearRequest.Args)[1:]
clearRequest.Args = &remainingArgs
} else {
emptyString := ""
clearRequest.Function = &emptyString
}
}
logger.Debugf("Normalized json args '%s' to function='%s'/args='%v'", requestJSON, *clearRequest.Function, *clearRequest.Args)

// setup crypto context
ep := &crypto.EncryptionProviderImpl{
GetCcEncryptionKey: func() ([]byte, error) {
// TODO: might have to do some re-formatting, e.g., de-hex, here?
return []byte(chaincodeEncryptionKey), nil
}}

ctx, err := ep.NewEncryptionContext()
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: could not setup crypto context: %v\n", err)
os.Exit(1)
}
logger.Debugf("Setup crypto context based on CC-ek '%v'", chaincodeEncryptionKey)

// encrypt request ...
encryptedRequest, err := ctx.Conceal(*clearRequest.Function, *clearRequest.Args)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: could not encrypt request: %v\n", err)
os.Exit(1)
}
// ... and return it
logger.Debugf("Transformed request '%v' to '%v' and write to pipe '%s'", clearRequest, encryptedRequest, resultPipeName)
resultPipeFile.WriteString(fmt.Sprintf("%s\n", encryptedRequest))

// read encrypted response ...
encryptedResponse, err := reader.ReadString('\n')
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: couldn't read encrypted response: %v\n", err)
os.Exit(1)
}
encryptedResponse = strings.TrimSuffix(encryptedResponse, "\n")

// .. decrypt it ..
// TODO: requires fix in Conceal & ecc/mock
// - should be base64 encoded
// - encrypted response should be a proper (serialized) response object, not only a string, and hence conceal should
// return the deserialized response, not a byte array ..
clearResponse, err := ctx.Reveal([]byte(encryptedResponse))
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: could not decrypt response: %v\n", err)
os.Exit(1)
}
// TODO: create a (single-line) json encoding once we get above a proper response object ...
logger.Debugf("Transformed response '%s' to '%s' and write to pipe '%s'", encryptedResponse, string(clearResponse), resultPipeName)
resultPipeFile.WriteString(fmt.Sprintf("%s\n", clearResponse))
}
29 changes: 29 additions & 0 deletions utils/fabric/peer-cli-assist.src/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash
# Copyright 2020 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0

#set -ex

result_pipe=$(mktemp -u -t testpipeXXXX)
mkfifo $result_pipe

cc_ek="a-key"
exec 3> >( ../peer-cli-assist handleRequestAndResponse "${cc_ek}" "${result_pipe}")
assist_pid=$!

exec 4<${result_pipe}

echo >&3 '{"Function":"init", "Args": ["MyAuctionHouse"]}'

read <&4 encrypted_request
echo "encrypted_request='$encrypted_request'"

echo >&3 "I'm supposed to be an base64 encoded serialized ChaincodeResponseMessage"
read <&4 decrypted_response
echo "decrypted_response='$decrypted_response'"

wait ${assist_pid}
echo "child exit code=$?"

rm $result_pipe