From fd498d2fa7fdc048b4150db1a15eef572f536a87 Mon Sep 17 00:00:00 2001 From: Gabor Hosszu Date: Wed, 10 Aug 2016 13:22:42 +0200 Subject: [PATCH] Use repeated bytes instead of repeated string for chaincode call args This allows applications to easily pass arbitrary blobs without having to serialize them to strings. At the same time, we also consolidate the function argument to be part of the repeated bytes args. For convenience and to simplify porting of existing chaincode to the new argument format, we introduce helper functions in the shim which cast between ([][]byte) and (string, []string). Change-Id: I67562523a208727157c4767e86e1ef437e997f13 Signed-off-by: Gabor Hosszu --- bddtests/peer_basic.feature | 7 +- bddtests/steps/peer_basic_impl.py | 27 ++-- bddtests/syschaincode/noop/chaincode.go | 11 +- bddtests/syschaincode/noop/chaincode_test.go | 3 +- core/chaincode/chaincode_support.go | 12 +- core/chaincode/exectransaction_test.go | 103 ++++++------- core/chaincode/handler.go | 34 ++--- core/chaincode/platforms/car/hash.go | 10 +- core/chaincode/platforms/car/test/car_test.go | 3 +- core/chaincode/platforms/golang/hash.go | 10 +- core/chaincode/platforms/java/hash.go | 10 +- .../platforms/java/test/java_test.go | 3 +- core/chaincode/shim/chaincode.go | 40 +++++- core/chaincode/shim/handler.go | 28 +++- .../src/main/java/example/LinkExample.java | 136 ++++++++++-------- .../hyperledger/java/shim/ChaincodeStub.java | 12 +- .../org/hyperledger/java/shim/Handler.java | 35 +++-- .../shim/java/src/main/proto/chaincode.proto | 5 +- core/container/vm_test.go | 5 +- core/devops.go | 12 +- core/ledger/blockchain_test.go | 3 +- core/rest/rest_api.go | 10 +- core/rest/rest_api.json | 4 - core/rest/rest_api_test.go | 41 ++++-- core/system_chaincode/api/sysccapi.go | 2 +- core/system_chaincode/importsysccs.go | 2 +- core/system_chaincode/systemchaincode_test.go | 11 +- core/util/utils.go | 30 +--- core/util/utils_test.go | 8 +- .../chaincode_example04.go | 7 +- .../chaincode_example05.go | 16 +-- examples/chaincode/go/passthru/passthru.go | 11 +- protos/chaincode.pb.go | 3 +- protos/chaincode.proto | 5 +- 34 files changed, 361 insertions(+), 298 deletions(-) diff --git a/bddtests/peer_basic.feature b/bddtests/peer_basic.feature index 0317a6fcc5f..35bf92bf4a2 100644 --- a/bddtests/peer_basic.feature +++ b/bddtests/peer_basic.feature @@ -349,8 +349,7 @@ Feature: Network of Peers # @doNotDecompose # @wip -# Arg[0] = a, base64 = 'YQ==' -# sha256 = 'acfb280369a87a57b1954210081d78943f1a0adb5368184984e8852a42c14df8' +# sha256 = '53a54f606b9cc14ae2825cc50736b183cf8d6fd0131b9d3176997efcf77d775f' # calculated using all the args Scenario: chaincode map single peer content generated ID Given we compose "docker-compose-1.yml" @@ -364,10 +363,10 @@ Feature: Network of Peers When I invoke chaincode "map" function name "put" on "vp0" with "sha256" | arg1 |arg2| - | YQ== | 10 | + | a | 10 | Then I should have received a transactionID Then I wait up to "25" seconds for transaction to be committed to all peers - Then I check the transaction ID if it is "acfb280369a87a57b1954210081d78943f1a0adb5368184984e8852a42c14df8" + Then I check the transaction ID if it is "cbe63379a50f0c9d798951e5ccdf70a5c341acda508d7fa1345841d977825f71" Scenario: chaincode example 01 single peer rejection message Given we compose "docker-compose-1-exp.yml" diff --git a/bddtests/steps/peer_basic_impl.py b/bddtests/steps/peer_basic_impl.py index 1aa8951d14d..d0ca7e85ca3 100644 --- a/bddtests/steps/peer_basic_impl.py +++ b/bddtests/steps/peer_basic_impl.py @@ -20,6 +20,7 @@ import time import copy from datetime import datetime, timedelta +import base64 import sys, requests, json @@ -194,7 +195,6 @@ def getArgsFromContext(context): if 'table' in context: # There is ctor arguments args = context.table[0].cells - return args @when(u'I deploy chaincode "{chaincodePath}" with ctor "{ctor}" to "{containerName}"') @@ -239,7 +239,8 @@ def deployChainCodeToContainer(context, chaincode, containerName): def createChaincodeSpec(context, chaincode): chaincode = validateChaincodeDictionary(chaincode) - + args = to_bytes(prepend(chaincode["constructor"], chaincode["args"])) + # Create a ChaincodeSpec structure chaincodeSpec = { "type": getChaincodeTypeValue(chaincode["language"]), "chaincodeID": { @@ -247,8 +248,7 @@ def createChaincodeSpec(context, chaincode): "name" : chaincode["name"] }, "ctorMsg": { - "function" : chaincode["constructor"], - "args" : chaincode["args"] + "args" : args }, } @@ -365,14 +365,12 @@ def invokeChaincode(context, devopsFunc, functionName, containerName, idGenAlg=N if 'table' in context: # There is ctor arguments args = context.table[0].cells - + args = to_bytes(prepend(functionName, args)) for idx, attr in enumerate(attributes): attributes[idx] = attr.strip() - context.chaincodeSpec['ctorMsg']['function'] = functionName context.chaincodeSpec['ctorMsg']['args'] = args context.chaincodeSpec['attributes'] = attributes - #If idGenAlg is passed then, we still using the deprecated devops API because this parameter can't be passed in the new API. if idGenAlg != None: invokeUsingDevopsService(context, devopsFunc, functionName, containerName, idGenAlg) @@ -424,6 +422,7 @@ def invokeMasterChaincode(context, devopsFunc, chaincodeName, functionName, cont args = [] if 'table' in context: args = context.table[0].cells + args = to_bytes(prepend(functionName, args)) typeGolang = 1 chaincodeSpec = { "type": typeGolang, @@ -431,7 +430,6 @@ def invokeMasterChaincode(context, devopsFunc, chaincodeName, functionName, cont "name" : chaincodeName }, "ctorMsg": { - "function" : functionName, "args" : args } } @@ -646,7 +644,7 @@ def step_impl(context, chaincodeName, functionName): if 'table' in context: # There is ctor arguments args = context.table[0].cells - context.chaincodeSpec['ctorMsg']['function'] = functionName + args = to_bytes(prepend(functionName, args)) context.chaincodeSpec['ctorMsg']['args'] = args #context.table[0].cells if ('table' in context) else [] # Invoke the POST chaincodeOpPayload = createChaincodeOpPayload("query", context.chaincodeSpec) @@ -678,8 +676,7 @@ def query_common(context, chaincodeName, functionName, value, failOnError): containerDataList = bdd_test_util.getContainerDataValuesFromContext(context, aliases, lambda containerData: containerData) # Update the chaincodeSpec ctorMsg for invoke - context.chaincodeSpec['ctorMsg']['function'] = functionName - context.chaincodeSpec['ctorMsg']['args'] = [value] + context.chaincodeSpec['ctorMsg']['args'] = to_bytes([functionName, value]) # Invoke the POST # Make deep copy of chaincodeSpec as we will be changing the SecurityContext per call. chaincodeOpPayload = createChaincodeOpPayload("query", copy.deepcopy(context.chaincodeSpec)) @@ -819,3 +816,11 @@ def compose_op(context, op): else: parseComposeOutput(context) print("After {0}ing, the container service list is = {1}".format(op, [containerData.composeService for containerData in context.compose_containers])) + +def to_bytes(strlist): + return [base64.standard_b64encode(s.encode('ascii')) for s in strlist] + +def prepend(elem, l): + if l is None or l == "": + return [elem] + return [elem] + l diff --git a/bddtests/syschaincode/noop/chaincode.go b/bddtests/syschaincode/noop/chaincode.go index a4923615871..b1709d7552c 100644 --- a/bddtests/syschaincode/noop/chaincode.go +++ b/bddtests/syschaincode/noop/chaincode.go @@ -17,9 +17,8 @@ package noop import ( - "encoding/base64" + "bytes" "errors" - "fmt" "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/core/chaincode/shim" @@ -95,12 +94,8 @@ func (t *SystemChaincode) Query(stub *shim.ChaincodeStub, function string, args if nil != merr { return nil, merr } - var data = newCCIS.ChaincodeSpec.CtorMsg.Args[0] - var dataInByteForm, b64err = base64.StdEncoding.DecodeString(data) - if b64err != nil { - return nil, fmt.Errorf("Error in decoding from Base64: %s", b64err) - } - return dataInByteForm, nil + var dataInByteForm = newCCIS.ChaincodeSpec.CtorMsg.Args + return bytes.Join(dataInByteForm, []byte{}), nil default: return nil, errors.New("Unsupported operation") } diff --git a/bddtests/syschaincode/noop/chaincode_test.go b/bddtests/syschaincode/noop/chaincode_test.go index b9e51efdb00..7664329a6ac 100644 --- a/bddtests/syschaincode/noop/chaincode_test.go +++ b/bddtests/syschaincode/noop/chaincode_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/golang/protobuf/proto" + "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/protos" ) @@ -115,7 +116,7 @@ func (ml mockLedger) GetTransactionByID(txID string) (*protos.Transaction, error if txID == "noSuchTX" { return nil, fmt.Errorf("Some error") } - newCCIS := &protos.ChaincodeInvocationSpec{ChaincodeSpec: &protos.ChaincodeSpec{CtorMsg: &protos.ChaincodeInput{Function: "execute", Args: []string{something}}}} + newCCIS := &protos.ChaincodeInvocationSpec{ChaincodeSpec: &protos.ChaincodeSpec{CtorMsg: &protos.ChaincodeInput{Args: shim.ToChaincodeArgs("execute", something)}}} pl, _ := proto.Marshal(newCCIS) return &protos.Transaction{Payload: pl}, nil } diff --git a/core/chaincode/chaincode_support.go b/core/chaincode/chaincode_support.go index 8c999def66f..0a081d71384 100644 --- a/core/chaincode/chaincode_support.go +++ b/core/chaincode/chaincode_support.go @@ -243,7 +243,7 @@ func (chaincodeSupport *ChaincodeSupport) deregisterHandler(chaincodehandler *Ha } // Based on state of chaincode send either init or ready to move to ready state -func (chaincodeSupport *ChaincodeSupport) sendInitOrReady(context context.Context, txid string, chaincode string, f *string, initArgs []string, timeout time.Duration, tx *pb.Transaction, depTx *pb.Transaction) error { +func (chaincodeSupport *ChaincodeSupport) sendInitOrReady(context context.Context, txid string, chaincode string, initArgs [][]byte, timeout time.Duration, tx *pb.Transaction, depTx *pb.Transaction) error { chaincodeSupport.runningChaincodes.Lock() //if its in the map, there must be a connected stream...nothing to do var chrte *chaincodeRTEnv @@ -257,7 +257,7 @@ func (chaincodeSupport *ChaincodeSupport) sendInitOrReady(context context.Contex var notfy chan *pb.ChaincodeMessage var err error - if notfy, err = chrte.handler.initOrReady(txid, f, initArgs, tx, depTx); err != nil { + if notfy, err = chrte.handler.initOrReady(txid, initArgs, tx, depTx); err != nil { return fmt.Errorf("Error sending %s: %s", pb.ChaincodeMessage_INIT, err) } if notfy != nil { @@ -411,9 +411,8 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, t *pb. //build the chaincode var cID *pb.ChaincodeID var cMsg *pb.ChaincodeInput - var f *string var cLang pb.ChaincodeSpec_Type - var initargs []string + var initargs [][]byte cds := &pb.ChaincodeDeploymentSpec{} if t.Type == pb.Transaction_CHAINCODE_DEPLOY { @@ -424,7 +423,6 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, t *pb. cID = cds.ChaincodeSpec.ChaincodeID cMsg = cds.ChaincodeSpec.CtorMsg cLang = cds.ChaincodeSpec.Type - f = &cMsg.Function initargs = cMsg.Args } else if t.Type == pb.Transaction_CHAINCODE_INVOKE || t.Type == pb.Transaction_CHAINCODE_QUERY { ci := &pb.ChaincodeInvocationSpec{} @@ -522,8 +520,8 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, t *pb. } if err == nil { - //send init (if (f,args)) and wait for ready state - err = chaincodeSupport.sendInitOrReady(context, t.Txid, chaincode, f, initargs, chaincodeSupport.ccStartupTimeout, t, depTx) + //send init (if (args)) and wait for ready state + err = chaincodeSupport.sendInitOrReady(context, t.Txid, chaincode, initargs, chaincodeSupport.ccStartupTimeout, t, depTx) if err != nil { chaincodeLogger.Errorf("sending init failed(%s)", err) err = fmt.Errorf("Failed to init chaincode(%s)", err) diff --git a/core/chaincode/exectransaction_test.go b/core/chaincode/exectransaction_test.go index 9b5c312cc7f..bfd8705dbbc 100644 --- a/core/chaincode/exectransaction_test.go +++ b/core/chaincode/exectransaction_test.go @@ -28,6 +28,7 @@ import ( "path/filepath" + "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/core/container" "github.com/hyperledger/fabric/core/container/ccintf" "github.com/hyperledger/fabric/core/crypto" @@ -338,8 +339,8 @@ func executeDeployTransaction(t *testing.T, url string) { var ctxt = context.Background() f := "init" - args := []string{"a", "100", "b", "200"} - spec := &pb.ChaincodeSpec{Type: 1, ChaincodeID: &pb.ChaincodeID{Path: url}, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + args := shim.ToChaincodeArgs(f, "a", "100", "b", "200") + spec := &pb.ChaincodeSpec{Type: 1, ChaincodeID: &pb.ChaincodeID{Path: url}, CtorMsg: &pb.ChaincodeInput{Args: args}} _, err = deploy(ctxt, spec) chaincodeID := spec.ChaincodeID.Name if err != nil { @@ -423,8 +424,8 @@ func checkFinalState(uuid string, chaincodeID string) error { func invokeExample02Transaction(ctxt context.Context, cID *pb.ChaincodeID, args []string, destroyImage bool) error { f := "init" - argsDeploy := []string{"a", "100", "b", "200"} - spec := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Function: f, Args: argsDeploy}} + argsDeploy := shim.ToChaincodeArgs(f, "a", "100", "b", "200") + spec := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Args: argsDeploy}} _, err := deploy(ctxt, spec) chaincodeID := spec.ChaincodeID.Name if err != nil { @@ -445,7 +446,8 @@ func invokeExample02Transaction(ctxt context.Context, cID *pb.ChaincodeID, args } f = "invoke" - spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + invokeArgs := append([]string{f}, args...) + spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Args: shim.ToChaincodeArgs(invokeArgs...)}} _, uuid, _, err := invoke(ctxt, spec, pb.Transaction_CHAINCODE_INVOKE) if err != nil { return fmt.Errorf("Error invoking <%s>: %s", chaincodeID, err) @@ -458,8 +460,8 @@ func invokeExample02Transaction(ctxt context.Context, cID *pb.ChaincodeID, args // Test for delete state f = "delete" - delArgs := []string{"a"} - spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Function: f, Args: delArgs}} + delArgs := shim.ToChaincodeArgs(f, "a") + spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Args: delArgs}} _, uuid, _, err = invoke(ctxt, spec, pb.Transaction_CHAINCODE_INVOKE) if err != nil { return fmt.Errorf("Error deleting state in <%s>: %s", chaincodeID, err) @@ -538,14 +540,14 @@ func exec(ctxt context.Context, chaincodeID string, numTrans int, numQueries int var spec *pb.ChaincodeSpec if typ == pb.Transaction_CHAINCODE_INVOKE { f := "invoke" - args := []string{"a", "b", "10"} + args := shim.ToChaincodeArgs(f, "a", "b", "10") - spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: &pb.ChaincodeID{Name: chaincodeID}, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: &pb.ChaincodeID{Name: chaincodeID}, CtorMsg: &pb.ChaincodeInput{Args: args}} } else { f := "query" - args := []string{"a"} + args := shim.ToChaincodeArgs(f, "a") - spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: &pb.ChaincodeID{Name: chaincodeID}, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: &pb.ChaincodeID{Name: chaincodeID}, CtorMsg: &pb.ChaincodeInput{Args: args}} } _, _, _, err := invoke(ctxt, spec, typ) @@ -613,9 +615,9 @@ func TestExecuteQuery(t *testing.T) { cID := &pb.ChaincodeID{Path: url} f := "init" - args := []string{"a", "100", "b", "200"} + args := shim.ToChaincodeArgs(f, "a", "100", "b", "200") - spec := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Args: args}} _, err = deploy(ctxt, spec) chaincodeID := spec.ChaincodeID.Name @@ -759,9 +761,9 @@ func TestExecuteInvalidQuery(t *testing.T) { cID := &pb.ChaincodeID{Path: url} f := "init" - args := []string{"a", "100"} + args := shim.ToChaincodeArgs(f, "a", "100") - spec := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Args: args}} _, err = deploy(ctxt, spec) chaincodeID := spec.ChaincodeID.Name @@ -776,9 +778,9 @@ func TestExecuteInvalidQuery(t *testing.T) { time.Sleep(time.Second) f = "query" - args = []string{"b", "200"} + args = shim.ToChaincodeArgs(f, "b", "200") - spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Args: args}} // This query should fail as it attempts to put state _, _, _, err = invoke(ctxt, spec, pb.Transaction_CHAINCODE_QUERY) @@ -832,9 +834,9 @@ func TestChaincodeInvokeChaincode(t *testing.T) { cID1 := &pb.ChaincodeID{Path: url1} f := "init" - args := []string{"a", "100", "b", "200"} + args := shim.ToChaincodeArgs(f, "a", "100", "b", "200") - spec1 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID1, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec1 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID1, CtorMsg: &pb.ChaincodeInput{Args: args}} _, err = deploy(ctxt, spec1) chaincodeID1 := spec1.ChaincodeID.Name @@ -853,9 +855,9 @@ func TestChaincodeInvokeChaincode(t *testing.T) { cID2 := &pb.ChaincodeID{Path: url2} f = "init" - args = []string{"e", "0"} + args = shim.ToChaincodeArgs(f, "e", "0") - spec2 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec2 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}} _, err = deploy(ctxt, spec2) chaincodeID2 := spec2.ChaincodeID.Name @@ -872,9 +874,9 @@ func TestChaincodeInvokeChaincode(t *testing.T) { // Invoke second chaincode, which will inturn invoke the first chaincode f = "invoke" - args = []string{"e", "1"} + args = shim.ToChaincodeArgs(f, "e", "1") - spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}} // Invoke chaincode var uuid string _, uuid, _, err = invoke(ctxt, spec2, pb.Transaction_CHAINCODE_INVOKE) @@ -946,9 +948,9 @@ func TestChaincodeInvokeChaincodeErrorCase(t *testing.T) { cID1 := &pb.ChaincodeID{Path: url1} f := "init" - args := []string{"a", "100", "b", "200"} + args := shim.ToChaincodeArgs(f, "a", "100", "b", "200") - spec1 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID1, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec1 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID1, CtorMsg: &pb.ChaincodeInput{Args: args}} _, err = deploy(ctxt, spec1) chaincodeID1 := spec1.ChaincodeID.Name @@ -967,9 +969,9 @@ func TestChaincodeInvokeChaincodeErrorCase(t *testing.T) { cID2 := &pb.ChaincodeID{Path: url2} f = "init" - args = []string{""} + args = shim.ToChaincodeArgs(f) - spec2 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec2 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}} _, err = deploy(ctxt, spec2) chaincodeID2 := spec2.ChaincodeID.Name @@ -986,9 +988,9 @@ func TestChaincodeInvokeChaincodeErrorCase(t *testing.T) { // Invoke second chaincode, which will inturn invoke the first chaincode but pass bad params f = chaincodeID1 - args = []string{"invoke", "a"} //expect {"invoke", "a","b","10"} + args = shim.ToChaincodeArgs(f, "invoke", "a") - spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}} // Invoke chaincode _, _, _, err = invoke(ctxt, spec2, pb.Transaction_CHAINCODE_INVOKE) @@ -1023,9 +1025,9 @@ func chaincodeQueryChaincode(user string) error { cID1 := &pb.ChaincodeID{Path: url1} f := "init" - args := []string{"a", "100", "b", "200"} + args := shim.ToChaincodeArgs(f, "a", "100", "b", "200") - spec1 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID1, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}, SecureContext: user} + spec1 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID1, CtorMsg: &pb.ChaincodeInput{Args: args}, SecureContext: user} _, err := deploy(ctxt, spec1) chaincodeID1 := spec1.ChaincodeID.Name @@ -1041,9 +1043,9 @@ func chaincodeQueryChaincode(user string) error { cID2 := &pb.ChaincodeID{Path: url2} f = "init" - args = []string{"sum", "0"} + args = shim.ToChaincodeArgs(f, "sum", "0") - spec2 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}, SecureContext: user} + spec2 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}, SecureContext: user} _, err = deploy(ctxt, spec2) chaincodeID2 := spec2.ChaincodeID.Name @@ -1057,9 +1059,9 @@ func chaincodeQueryChaincode(user string) error { // Invoke second chaincode, which will inturn query the first chaincode f = "invoke" - args = []string{chaincodeID1, "sum"} + args = shim.ToChaincodeArgs(f, chaincodeID1, "sum") - spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}, SecureContext: user} + spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}, SecureContext: user} // Invoke chaincode var retVal []byte _, _, retVal, err = invoke(ctxt, spec2, pb.Transaction_CHAINCODE_INVOKE) @@ -1080,9 +1082,9 @@ func chaincodeQueryChaincode(user string) error { // Query second chaincode, which will inturn query the first chaincode f = "query" - args = []string{chaincodeID1, "sum"} + args = shim.ToChaincodeArgs(f, chaincodeID1, "sum") - spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}, SecureContext: user} + spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}, SecureContext: user} // Invoke chaincode _, _, retVal, err = invoke(ctxt, spec2, pb.Transaction_CHAINCODE_QUERY) @@ -1169,9 +1171,9 @@ func TestChaincodeQueryChaincodeErrorCase(t *testing.T) { cID1 := &pb.ChaincodeID{Path: url1} f := "init" - args := []string{"a", "100", "b", "200"} + args := shim.ToChaincodeArgs(f, "a", "100", "b", "200") - spec1 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID1, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec1 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID1, CtorMsg: &pb.ChaincodeInput{Args: args}} _, err = deploy(ctxt, spec1) chaincodeID1 := spec1.ChaincodeID.Name @@ -1190,9 +1192,9 @@ func TestChaincodeQueryChaincodeErrorCase(t *testing.T) { cID2 := &pb.ChaincodeID{Path: url2} f = "init" - args = []string{""} + args = shim.ToChaincodeArgs(f) - spec2 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec2 := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}} _, err = deploy(ctxt, spec2) chaincodeID2 := spec2.ChaincodeID.Name @@ -1209,9 +1211,9 @@ func TestChaincodeQueryChaincodeErrorCase(t *testing.T) { // Invoke second chaincode, which will inturn invoke the first chaincode but pass bad params f = chaincodeID1 - args = []string{"query", "c"} //expect {"query", "a"} + args = shim.ToChaincodeArgs(f, "query", "c") - spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec2 = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID2, CtorMsg: &pb.ChaincodeInput{Args: args}} // Invoke chaincode _, _, _, err = invoke(ctxt, spec2, pb.Transaction_CHAINCODE_QUERY) @@ -1332,10 +1334,10 @@ func TestRangeQuery(t *testing.T) { url := "github.com/hyperledger/fabric/examples/chaincode/go/map" cID := &pb.ChaincodeID{Path: url} - args := []string{} f := "init" + args := shim.ToChaincodeArgs(f) - spec := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Args: args}} _, err = deploy(ctxt, spec) chaincodeID := spec.ChaincodeID.Name @@ -1349,9 +1351,9 @@ func TestRangeQuery(t *testing.T) { // Invoke second chaincode, which will inturn invoke the first chaincode f = "keys" - args = []string{} + args = shim.ToChaincodeArgs(f) - spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Args: args}} _, _, _, err = invoke(ctxt, spec, pb.Transaction_CHAINCODE_QUERY) if err != nil { @@ -1403,7 +1405,8 @@ func TestGetEvent(t *testing.T) { url := "github.com/hyperledger/fabric/examples/chaincode/go/eventsender" cID := &pb.ChaincodeID{Path: url} - spec := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Function: "init", Args: []string{}}} + f := "init" + spec := &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Args: shim.ToChaincodeArgs(f)}} _, err = deploy(ctxt, spec) chaincodeID := spec.ChaincodeID.Name @@ -1417,9 +1420,9 @@ func TestGetEvent(t *testing.T) { time.Sleep(time.Second) - args := []string{"i", "am", "satoshi"} + args := shim.ToChaincodeArgs("", "i", "am", "satoshi") - spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Function: "", Args: args}} + spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: cID, CtorMsg: &pb.ChaincodeInput{Args: args}} var ccevt *pb.ChaincodeEvent ccevt, _, _, err = invoke(ctxt, spec, pb.Transaction_CHAINCODE_INVOKE) diff --git a/core/chaincode/handler.go b/core/chaincode/handler.go index a967f6f5695..52c66deeef1 100644 --- a/core/chaincode/handler.go +++ b/core/chaincode/handler.go @@ -1236,21 +1236,19 @@ func (handler *Handler) setChaincodeSecurityContext(tx *pb.Transaction, msg *pb. msg.SecurityContext.Binding = binding msg.SecurityContext.Metadata = tx.Metadata - if tx.Type == pb.Transaction_CHAINCODE_INVOKE || tx.Type == pb.Transaction_CHAINCODE_QUERY { - cis := &pb.ChaincodeInvocationSpec{} - if err := proto.Unmarshal(tx.Payload, cis); err != nil { - chaincodeLogger.Errorf("Failed getting payload [%s]", err) - return err - } - - ctorMsgRaw, err := proto.Marshal(cis.ChaincodeSpec.GetCtorMsg()) - if err != nil { - chaincodeLogger.Errorf("Failed getting ctorMsgRaw [%s]", err) - return err - } + cis := &pb.ChaincodeInvocationSpec{} + if err := proto.Unmarshal(tx.Payload, cis); err != nil { + chaincodeLogger.Errorf("Failed getting payload [%s]", err) + return err + } - msg.SecurityContext.Payload = ctorMsgRaw + ctorMsgRaw, err := proto.Marshal(cis.ChaincodeSpec.GetCtorMsg()) + if err != nil { + chaincodeLogger.Errorf("Failed getting ctorMsgRaw [%s]", err) + return err } + + msg.SecurityContext.Payload = ctorMsgRaw msg.SecurityContext.TxTimestamp = tx.Timestamp } return nil @@ -1258,7 +1256,7 @@ func (handler *Handler) setChaincodeSecurityContext(tx *pb.Transaction, msg *pb. //if initArgs is set (should be for "deploy" only) move to Init //else move to ready -func (handler *Handler) initOrReady(txid string, f *string, initArgs []string, tx *pb.Transaction, depTx *pb.Transaction) (chan *pb.ChaincodeMessage, error) { +func (handler *Handler) initOrReady(txid string, initArgs [][]byte, tx *pb.Transaction, depTx *pb.Transaction) (chan *pb.ChaincodeMessage, error) { var ccMsg *pb.ChaincodeMessage var send bool @@ -1269,13 +1267,9 @@ func (handler *Handler) initOrReady(txid string, f *string, initArgs []string, t notfy := txctx.responseNotifier - if f != nil || initArgs != nil { + if initArgs != nil { chaincodeLogger.Debug("sending INIT") - var f2 string - if f != nil { - f2 = *f - } - funcArgsMsg := &pb.ChaincodeInput{Function: f2, Args: initArgs} + funcArgsMsg := &pb.ChaincodeInput{Args: initArgs} var payload []byte if payload, funcErr = proto.Marshal(funcArgsMsg); funcErr != nil { handler.deleteTxContext(txid) diff --git a/core/chaincode/platforms/car/hash.go b/core/chaincode/platforms/car/hash.go index 2d3ff0d74d5..870d572320d 100644 --- a/core/chaincode/platforms/car/hash.go +++ b/core/chaincode/platforms/car/hash.go @@ -21,6 +21,7 @@ import ( "fmt" "io/ioutil" + "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/core/util" pb "github.com/hyperledger/fabric/protos" ) @@ -34,11 +35,14 @@ import ( func generateHashcode(spec *pb.ChaincodeSpec, path string) (string, error) { ctor := spec.CtorMsg - if ctor == nil || ctor.Function == "" { + if ctor == nil || len(ctor.Args) == 0 { return "", fmt.Errorf("Cannot generate hashcode from empty ctor") } - - hash := util.GenerateHashFromSignature(spec.ChaincodeID.Path, ctor.Function, ctor.Args) + ctorbytes, err := proto.Marshal(ctor) + if err != nil { + return "", fmt.Errorf("Error marshalling constructor: %s", err) + } + hash := util.GenerateHashFromSignature(spec.ChaincodeID.Path, ctorbytes) buf, err := ioutil.ReadFile(path) if err != nil { diff --git a/core/chaincode/platforms/car/test/car_test.go b/core/chaincode/platforms/car/test/car_test.go index 18cf00ef938..e39b199a520 100644 --- a/core/chaincode/platforms/car/test/car_test.go +++ b/core/chaincode/platforms/car/test/car_test.go @@ -20,6 +20,7 @@ import ( "os" "testing" + "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/core/config" "github.com/hyperledger/fabric/core/container" pb "github.com/hyperledger/fabric/protos" @@ -46,7 +47,7 @@ func TestCar_BuildImage(t *testing.T) { } chaincodePath := cwd + "/org.hyperledger.chaincode.example02-0.1-SNAPSHOT.car" - spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_CAR, ChaincodeID: &pb.ChaincodeID{Path: chaincodePath}, CtorMsg: &pb.ChaincodeInput{Function: "f"}} + spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_CAR, ChaincodeID: &pb.ChaincodeID{Path: chaincodePath}, CtorMsg: &pb.ChaincodeInput{Args: shim.ToChaincodeArgs("f")}} if _, err := vm.BuildChaincodeContainer(spec); err != nil { t.Fail() t.Log(err) diff --git a/core/chaincode/platforms/golang/hash.go b/core/chaincode/platforms/golang/hash.go index d5fa1b4aec4..33eb8cc8847 100755 --- a/core/chaincode/platforms/golang/hash.go +++ b/core/chaincode/platforms/golang/hash.go @@ -29,6 +29,7 @@ import ( "strings" "time" + "github.com/golang/protobuf/proto" "github.com/op/go-logging" "github.com/spf13/viper" @@ -226,7 +227,7 @@ func generateHashcode(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, error) { } ctor := spec.CtorMsg - if ctor == nil || ctor.Function == "" { + if ctor == nil || len(ctor.Args) == 0 { return "", fmt.Errorf("Cannot generate hashcode from empty ctor") } @@ -267,8 +268,11 @@ func generateHashcode(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, error) { if err = isCodeExist(tmppath); err != nil { return "", fmt.Errorf("code does not exist %s", err) } - - hash := util.GenerateHashFromSignature(actualcodepath, ctor.Function, ctor.Args) + ctorbytes, err := proto.Marshal(ctor) + if err != nil { + return "", fmt.Errorf("Error marshalling constructor: %s", err) + } + hash := util.GenerateHashFromSignature(actualcodepath, ctorbytes) hash, err = hashFilesInDir(filepath.Join(codegopath, "src"), actualcodepath, hash, tw) if err != nil { diff --git a/core/chaincode/platforms/java/hash.go b/core/chaincode/platforms/java/hash.go index 6697598c04e..14abec13486 100644 --- a/core/chaincode/platforms/java/hash.go +++ b/core/chaincode/platforms/java/hash.go @@ -9,6 +9,7 @@ import ( "os" "strings" + "github.com/golang/protobuf/proto" cutil "github.com/hyperledger/fabric/core/container/util" "github.com/hyperledger/fabric/core/util" pb "github.com/hyperledger/fabric/protos" @@ -91,7 +92,7 @@ func generateHashcode(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, error) { } ctor := spec.CtorMsg - if ctor == nil || ctor.Function == "" { + if ctor == nil || len(ctor.Args) == 0 { return "", fmt.Errorf("Cannot generate hashcode from empty ctor") } @@ -132,8 +133,11 @@ func generateHashcode(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, error) { root = root[:len(root)-1] } root = root[:strings.LastIndex(root, "/")+1] - - hash := util.GenerateHashFromSignature(codepath, ctor.Function, ctor.Args) + ctorbytes, err := proto.Marshal(ctor) + if err != nil { + return "", fmt.Errorf("Error marshalling constructor: %s", err) + } + hash := util.GenerateHashFromSignature(codepath, ctorbytes) hash, err = hashFilesInDir(root, codepath, hash, tw) if err != nil { diff --git a/core/chaincode/platforms/java/test/java_test.go b/core/chaincode/platforms/java/test/java_test.go index 789542173fd..be3cdfc5734 100644 --- a/core/chaincode/platforms/java/test/java_test.go +++ b/core/chaincode/platforms/java/test/java_test.go @@ -20,6 +20,7 @@ import ( "os" "testing" + "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/core/config" "github.com/hyperledger/fabric/core/container" pb "github.com/hyperledger/fabric/protos" @@ -41,7 +42,7 @@ func TestJava_BuildImage(t *testing.T) { chaincodePath := "../../../shim/java" //TODO find a better way to launch example java chaincode - spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_JAVA, ChaincodeID: &pb.ChaincodeID{Path: chaincodePath}, CtorMsg: &pb.ChaincodeInput{Function: "f"}} + spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_JAVA, ChaincodeID: &pb.ChaincodeID{Path: chaincodePath}, CtorMsg: &pb.ChaincodeInput{Args: shim.ToChaincodeArgs("f")}} if _, err := vm.BuildChaincodeContainer(spec); err != nil { t.Fail() t.Log(err) diff --git a/core/chaincode/shim/chaincode.go b/core/chaincode/shim/chaincode.go index 4753c0995c1..b4dc8eab670 100644 --- a/core/chaincode/shim/chaincode.go +++ b/core/chaincode/shim/chaincode.go @@ -69,6 +69,7 @@ type ChaincodeStub struct { UUID string securityContext *pb.ChaincodeSecurityContext chaincodeEvent *pb.ChaincodeEvent + args [][]byte } // Peer address derived from command line or env var @@ -244,9 +245,19 @@ func chatWithPeer(chaincodename string, stream PeerChaincodeStream, cc Chaincode } // -- init stub --- +// ChaincodeInvocation functionality + func (stub *ChaincodeStub) init(uuid string, secContext *pb.ChaincodeSecurityContext) { stub.UUID = uuid stub.securityContext = secContext + stub.args = [][]byte{} + newCI := pb.ChaincodeInput{} + err := proto.Unmarshal(secContext.Payload, &newCI) + if err == nil { + stub.args = newCI.Args + } else { + panic("Arguments cannot be unmarshalled.") + } } // --------- Security functions ---------- @@ -257,15 +268,15 @@ func (stub *ChaincodeStub) init(uuid string, secContext *pb.ChaincodeSecurityCon // InvokeChaincode locally calls the specified chaincode `Invoke` using the // same transaction context; that is, chaincode calling chaincode doesn't // create a new transaction message. -func (stub *ChaincodeStub) InvokeChaincode(chaincodeName string, function string, args []string) ([]byte, error) { - return handler.handleInvokeChaincode(chaincodeName, function, args, stub.UUID) +func (stub *ChaincodeStub) InvokeChaincode(chaincodeName string, args [][]byte) ([]byte, error) { + return handler.handleInvokeChaincode(chaincodeName, args, stub.UUID) } // QueryChaincode locally calls the specified chaincode `Query` using the // same transaction context; that is, chaincode calling chaincode doesn't // create a new transaction message. -func (stub *ChaincodeStub) QueryChaincode(chaincodeName string, function string, args []string) ([]byte, error) { - return handler.handleQueryChaincode(chaincodeName, function, args, stub.UUID) +func (stub *ChaincodeStub) QueryChaincode(chaincodeName string, args [][]byte) ([]byte, error) { + return handler.handleQueryChaincode(chaincodeName, args, stub.UUID) } // --------- State functions ---------- @@ -380,6 +391,19 @@ func (iter *StateRangeQueryIterator) Close() error { return err } +func (stub *ChaincodeStub) GetArgs() [][]byte { + return stub.args +} + +func (stub *ChaincodeStub) GetStringArgs() []string { + args := stub.GetArgs() + strargs := make([]string, 0, len(args)) + for _, barg := range args { + strargs = append(strargs, string(barg)) + } + return strargs +} + // TABLE FUNCTIONALITY // TODO More comments here with documentation @@ -1009,3 +1033,11 @@ func (c *ChaincodeLogger) Errorf(format string, args ...interface{}) { func (c *ChaincodeLogger) Criticalf(format string, args ...interface{}) { c.logger.Criticalf(format, args...) } + +func ToChaincodeArgs(args ...string) [][]byte { + bargs := make([][]byte, len(args)) + for i, arg := range args { + bargs[i] = []byte(arg) + } + return bargs +} diff --git a/core/chaincode/shim/handler.go b/core/chaincode/shim/handler.go index 05a3ce0f96f..4c9ef1b8ef9 100644 --- a/core/chaincode/shim/handler.go +++ b/core/chaincode/shim/handler.go @@ -223,7 +223,8 @@ func (handler *Handler) handleInit(msg *pb.ChaincodeMessage) { // Create the ChaincodeStub which the chaincode can use to callback stub := new(ChaincodeStub) stub.init(msg.Txid, msg.SecurityContext) - res, err := handler.cc.Init(stub, input.Function, input.Args) + function, params := getFunctionAndParams(stub) + res, err := handler.cc.Init(stub, function, params) // delete isTransaction entry handler.deleteIsTransaction(msg.Txid) @@ -290,7 +291,8 @@ func (handler *Handler) handleTransaction(msg *pb.ChaincodeMessage) { // Create the ChaincodeStub which the chaincode can use to callback stub := new(ChaincodeStub) stub.init(msg.Txid, msg.SecurityContext) - res, err := handler.cc.Invoke(stub, input.Function, input.Args) + function, params := getFunctionAndParams(stub) + res, err := handler.cc.Invoke(stub, function, params) // delete isTransaction entry handler.deleteIsTransaction(msg.Txid) @@ -337,7 +339,8 @@ func (handler *Handler) handleQuery(msg *pb.ChaincodeMessage) { // Create the ChaincodeStub which the chaincode can use to callback stub := new(ChaincodeStub) stub.init(msg.Txid, msg.SecurityContext) - res, err := handler.cc.Query(stub, input.Function, input.Args) + function, params := getFunctionAndParams(stub) + res, err := handler.cc.Query(stub, function, params) // delete isTransaction entry handler.deleteIsTransaction(msg.Txid) @@ -740,14 +743,14 @@ func (handler *Handler) handleRangeQueryStateClose(id, txid string) (*pb.RangeQu } // handleInvokeChaincode communicates with the validator to invoke another chaincode. -func (handler *Handler) handleInvokeChaincode(chaincodeName string, function string, args []string, txid string) ([]byte, error) { +func (handler *Handler) handleInvokeChaincode(chaincodeName string, args [][]byte, txid string) ([]byte, error) { // Check if this is a transaction if !handler.isTransaction[txid] { return nil, errors.New("Cannot invoke chaincode in query context") } chaincodeID := &pb.ChaincodeID{Name: chaincodeName} - input := &pb.ChaincodeInput{Function: function, Args: args} + input := &pb.ChaincodeInput{Args: args} payload := &pb.ChaincodeSpec{ChaincodeID: chaincodeID, CtorMsg: input} payloadBytes, err := proto.Marshal(payload) if err != nil { @@ -806,9 +809,9 @@ func (handler *Handler) handleInvokeChaincode(chaincodeName string, function str } // handleQueryChaincode communicates with the validator to query another chaincode. -func (handler *Handler) handleQueryChaincode(chaincodeName string, function string, args []string, txid string) ([]byte, error) { +func (handler *Handler) handleQueryChaincode(chaincodeName string, args [][]byte, txid string) ([]byte, error) { chaincodeID := &pb.ChaincodeID{Name: chaincodeName} - input := &pb.ChaincodeInput{Function: function, Args: args} + input := &pb.ChaincodeInput{Args: args} payload := &pb.ChaincodeSpec{ChaincodeID: chaincodeID, CtorMsg: input} payloadBytes, err := proto.Marshal(payload) if err != nil { @@ -904,3 +907,14 @@ func filterError(errFromFSMEvent error) error { } return nil } + +func getFunctionAndParams(stub *ChaincodeStub) (function string, params []string) { + allargs := stub.GetStringArgs() + function = "" + params = []string{} + if len(allargs) >= 1 { + function = allargs[0] + params = allargs[1:] + } + return +} diff --git a/core/chaincode/shim/java/src/main/java/example/LinkExample.java b/core/chaincode/shim/java/src/main/java/example/LinkExample.java index c9468b10987..9eec17898f8 100644 --- a/core/chaincode/shim/java/src/main/java/example/LinkExample.java +++ b/core/chaincode/shim/java/src/main/java/example/LinkExample.java @@ -1,62 +1,74 @@ -/* -Copyright DTCC 2016 All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package example; - -import org.hyperledger.java.shim.ChaincodeBase; -import org.hyperledger.java.shim.ChaincodeStub; - -public class LinkExample extends ChaincodeBase { - - //Default name for map chaincode in dev mode - //Can be set to a hash location via init or setMap - private String mapChaincode = "map"; - - @Override - public String run(ChaincodeStub stub, String function, String[] args) { - switch (function) { - case "init": - case "setMap": - mapChaincode = args[0]; - break; - case "put": - stub.invokeChaincode(mapChaincode, function, args); - default: - break; - } - return null; - } - - @Override - public String query(ChaincodeStub stub, String function, String[] args) { - String tmp = stub.queryChaincode("map", function, args); - if (tmp.isEmpty()) tmp = "NULL"; - else tmp = "\"" + tmp + "\""; - tmp += " (queried from map chaincode)"; - return tmp; - } - - public static void main(String[] args) throws Exception { - new LinkExample().start(args); - //new Example().start(); - } - - @Override - public String getChaincodeID() { - return "link"; - } - -} +/* +Copyright DTCC 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package example; + +import com.google.protobuf.ByteString; +import org.hyperledger.java.shim.ChaincodeBase; +import org.hyperledger.java.shim.ChaincodeStub; + +import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; + +public class LinkExample extends ChaincodeBase { + + //Default name for map chaincode in dev mode + //Can be set to a hash location via init or setMap + private String mapChaincode = "map"; + + @Override + public String run(ChaincodeStub stub, String function, String[] args) { + switch (function) { + case "init": + case "setMap": + mapChaincode = args[0]; + break; + case "put": + stub.invokeChaincode(mapChaincode, function, toByteStringList(args)); + default: + break; + } + return null; + } + + @Override + public String query(ChaincodeStub stub, String function, String[] args) { + String tmp = stub.queryChaincode("map", function, toByteStringList(args)); + if (tmp.isEmpty()) tmp = "NULL"; + else tmp = "\"" + tmp + "\""; + tmp += " (queried from map chaincode)"; + return tmp; + } + + public static void main(String[] args) throws Exception { + new LinkExample().start(args); + //new Example().start(); + } + + @Override + public String getChaincodeID() { + return "link"; + } + + private List toByteStringList(String[] args) { + LinkedList result = new LinkedList(); + for (int i=0; i args) { return handler.handleInvokeChaincode(chaincodeName, function, args, uuid).toStringUtf8(); } @@ -96,7 +98,7 @@ public String invokeChaincode(String chaincodeName, String function, String[] ar * @param args * @return */ - public String queryChaincode(String chaincodeName, String function, String[] args) { + public String queryChaincode(String chaincodeName, String function, List args) { return handler.handleQueryChaincode(chaincodeName, function, args, uuid).toStringUtf8(); } @@ -138,7 +140,7 @@ public void putRawState(String key, ByteString value) { * @param args * @return */ - public ByteString queryRawChaincode(String chaincodeName, String function, String[] args) { + public ByteString queryRawChaincode(String chaincodeName, String function, List args) { return handler.handleQueryChaincode(chaincodeName, function, args, uuid); } @@ -150,7 +152,7 @@ public ByteString queryRawChaincode(String chaincodeName, String function, Strin * @param args the arguments to be provided in the chaincode call * @return the value returned by the chaincode call */ - public ByteString invokeRawChaincode(String chaincodeName, String function, String[] args) { + public ByteString invokeRawChaincode(String chaincodeName, String function, List args) { return handler.handleInvokeChaincode(chaincodeName, function, args, uuid); } @@ -572,6 +574,4 @@ public void createTable(String name) { // } // // return true, nil - - } diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java index babbcce6676..95b1094314a 100644 --- a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java @@ -38,11 +38,10 @@ Licensed to the Apache Software Foundation (ASF) under one import static protos.Chaincode.ChaincodeMessage.Type.RESPONSE; import static protos.Chaincode.ChaincodeMessage.Type.TRANSACTION; -import java.util.Arrays; import java.util.HashMap; +import java.util.List; import com.google.protobuf.ByteString; -import com.google.protobuf.ProtocolStringList; import org.hyperledger.java.fsm.CBDesc; import org.hyperledger.java.fsm.Event; @@ -233,7 +232,7 @@ public void handleInit(ChaincodeMessage message) { // Call chaincode's Run ByteString result; try { - result = chaincode.runHelper(stub, input.getFunction(), arrayHelper(input.getArgsList())); + result = chaincode.runHelper(stub, getFunction(input.getArgsList()), getParameters(input.getArgsList())); } catch (Exception e) { // Send ERROR message to chaincode support and change state logger.debug(String.format("[%s]Init failed. Sending %s", shortID(message), ERROR)); @@ -270,11 +269,17 @@ public void handleInit(ChaincodeMessage message) { new Thread(task).start(); } + private String getFunction(List args) { + return (args.size() > 0) ? args.get(0).toStringUtf8() : ""; + } - private String[] arrayHelper(ProtocolStringList argsList) { - String[] array = new String[argsList.size()]; - argsList.toArray(array); - return array; + private String[] getParameters(List args) { + int size = (args.size() == 0) ? 0 : args.size() - 1; + String[] strArgs = new String[size]; + for(int i = 1; i < args.size(); ++i) { + strArgs[i-1] = args.get(i).toStringUtf8(); + } + return strArgs; } // enterInitState will initialize the chaincode if entering init from established. @@ -326,7 +331,7 @@ public void handleTransaction(ChaincodeMessage message) { // Call chaincode's Run ByteString response; try { - response = chaincode.runHelper(stub, input.getFunction(), arrayHelper(input.getArgsList())); + response = chaincode.runHelper(stub, getFunction(input.getArgsList()), getParameters(input.getArgsList())); } catch (Exception e) { e.printStackTrace(); System.err.flush(); @@ -392,7 +397,7 @@ public void handleQuery(ChaincodeMessage message) { ByteString response; try { - response = chaincode.queryHelper(stub, input.getFunction(), arrayHelper(input.getArgsList())); + response = chaincode.queryHelper(stub, getFunction(input.getArgsList()), getParameters(input.getArgsList())); } catch (Exception e) { // Send ERROR message to chaincode support and change state logger.debug(String.format("[%s]Query execution failed. Sending %s", @@ -762,7 +767,7 @@ public void handleDeleteState(String key, String uuid) { // } // } - public ByteString handleInvokeChaincode(String chaincodeName, String function, String[] args, String uuid) { + public ByteString handleInvokeChaincode(String chaincodeName, String function, List args, String uuid) { // Check if this is a transaction if (!isTransaction.containsKey(uuid)) { throw new RuntimeException("Cannot invoke chaincode in query context"); @@ -771,8 +776,8 @@ public ByteString handleInvokeChaincode(String chaincodeName, String function, S ChaincodeID id = ChaincodeID.newBuilder() .setName(chaincodeName).build(); ChaincodeInput input = ChaincodeInput.newBuilder() - .setFunction(function) - .addAllArgs(Arrays.asList(args)) + .addArgs(ByteString.copyFromUtf8(function)) + .addAllArgs(args) .build(); ChaincodeSpec payload = ChaincodeSpec.newBuilder() .setChaincodeID(id) @@ -837,11 +842,11 @@ public ByteString handleInvokeChaincode(String chaincodeName, String function, S } } - public ByteString handleQueryChaincode(String chaincodeName, String function, String[] args, String uuid) { + public ByteString handleQueryChaincode(String chaincodeName, String function, List args, String uuid) { ChaincodeID id = ChaincodeID.newBuilder().setName(chaincodeName).build(); ChaincodeInput input = ChaincodeInput.newBuilder() - .setFunction(function) - .addAllArgs(Arrays.asList(args)) + .addArgs(ByteString.copyFromUtf8(function)) + .addAllArgs(args) .build(); ChaincodeSpec payload = ChaincodeSpec.newBuilder() .setChaincodeID(id) diff --git a/core/chaincode/shim/java/src/main/proto/chaincode.proto b/core/chaincode/shim/java/src/main/proto/chaincode.proto index efb80a31c71..b66d89290c2 100644 --- a/core/chaincode/shim/java/src/main/proto/chaincode.proto +++ b/core/chaincode/shim/java/src/main/proto/chaincode.proto @@ -47,10 +47,7 @@ message ChaincodeID { // Carries the chaincode function and its arguments. message ChaincodeInput { - - string function = 1; - repeated string args = 2; - + repeated bytes args = 2; } // Carries the chaincode specification. This is the actual metadata required for diff --git a/core/container/vm_test.go b/core/container/vm_test.go index 7f75248d202..a01aaac1570 100644 --- a/core/container/vm_test.go +++ b/core/container/vm_test.go @@ -24,6 +24,7 @@ import ( "os" "testing" + "github.com/hyperledger/fabric/core/chaincode/shim" cutil "github.com/hyperledger/fabric/core/container/util" pb "github.com/hyperledger/fabric/protos" "golang.org/x/net/context" @@ -73,7 +74,7 @@ func TestVM_BuildImage_ChaincodeLocal(t *testing.T) { } // Build the spec chaincodePath := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example01" - spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeID: &pb.ChaincodeID{Path: chaincodePath}, CtorMsg: &pb.ChaincodeInput{Function: "f"}} + spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeID: &pb.ChaincodeID{Path: chaincodePath}, CtorMsg: &pb.ChaincodeInput{Args: shim.ToChaincodeArgs("f")}} if _, err := vm.BuildChaincodeContainer(spec); err != nil { t.Fail() t.Log(err) @@ -90,7 +91,7 @@ func TestVM_BuildImage_ChaincodeRemote(t *testing.T) { } // Build the spec chaincodePath := "https://github.com/prjayach/chaincode_examples/chaincode_example02" - spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeID: &pb.ChaincodeID{Path: chaincodePath}, CtorMsg: &pb.ChaincodeInput{Function: "f"}} + spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeID: &pb.ChaincodeID{Path: chaincodePath}, CtorMsg: &pb.ChaincodeInput{Args: shim.ToChaincodeArgs("f")}} if _, err := vm.BuildChaincodeContainer(spec); err != nil { t.Fail() t.Log(err) diff --git a/core/devops.go b/core/devops.go index 16660fc92aa..894e10828dd 100644 --- a/core/devops.go +++ b/core/devops.go @@ -213,7 +213,11 @@ func (d *Devops) invokeOrQuery(ctx context.Context, chaincodeInvocationSpec *pb. var customIDgenAlg = strings.ToLower(chaincodeInvocationSpec.IdGenerationAlg) var id string var generr error - id, generr = util.GenerateIDWithAlg(customIDgenAlg, chaincodeInvocationSpec.ChaincodeSpec.CtorMsg.Args) + ctorbytes, merr := proto.Marshal(chaincodeInvocationSpec.ChaincodeSpec.CtorMsg) + if merr != nil { + return nil, fmt.Errorf("Error marshalling constructor: %s", merr) + } + id, generr = util.GenerateIDWithAlg(customIDgenAlg, ctorbytes) if generr != nil { return nil, generr } @@ -444,7 +448,11 @@ func (d *Devops) EXP_ExecuteWithBinding(ctx context.Context, executeWithBinding return &pb.Response{Status: pb.Response_FAILURE, Msg: []byte(err.Error())}, nil } - tid, generr := util.GenerateIDWithAlg("", executeWithBinding.ChaincodeInvocationSpec.ChaincodeSpec.CtorMsg.Args) + ctorbytes, merr := proto.Marshal(executeWithBinding.ChaincodeInvocationSpec.ChaincodeSpec.CtorMsg) + if merr != nil { + return nil, fmt.Errorf("Error marshalling constructor: %s", err) + } + tid, generr := util.GenerateIDWithAlg("", ctorbytes) if generr != nil { return nil, fmt.Errorf("Error: cannot generate TX ID (executing with binding)") } diff --git a/core/ledger/blockchain_test.go b/core/ledger/blockchain_test.go index 5cd8623e1a5..8d114fabd85 100644 --- a/core/ledger/blockchain_test.go +++ b/core/ledger/blockchain_test.go @@ -20,6 +20,7 @@ import ( "testing" "time" + "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/core/ledger/testutil" "github.com/hyperledger/fabric/core/util" "github.com/hyperledger/fabric/protos" @@ -58,7 +59,7 @@ func TestBlockChain_SingleBlock(t *testing.T) { // Create the Chaincode specification chaincodeSpec := &protos.ChaincodeSpec{Type: protos.ChaincodeSpec_GOLANG, ChaincodeID: &protos.ChaincodeID{Path: "Contracts"}, - CtorMsg: &protos.ChaincodeInput{Function: "Initialize", Args: []string{"param1"}}} + CtorMsg: &protos.ChaincodeInput{Args: shim.ToChaincodeArgs("Initialize", "param1")}} chaincodeDeploymentSepc := &protos.ChaincodeDeploymentSpec{ChaincodeSpec: chaincodeSpec} uuid := testutil.GenerateID(t) newChaincodeTx, err := protos.NewChaincodeDeployTransaction(chaincodeDeploymentSepc, uuid) diff --git a/core/rest/rest_api.go b/core/rest/rest_api.go index 2390428c773..e6836764b6c 100644 --- a/core/rest/rest_api.go +++ b/core/rest/rest_api.go @@ -788,7 +788,7 @@ func (s *ServerOpenchainREST) Deploy(rw web.ResponseWriter, req *web.Request) { } // Check that the CtorMsg is not left blank. - if (spec.CtorMsg == nil) || (spec.CtorMsg.Function == "") { + if (spec.CtorMsg == nil) || (len(spec.CtorMsg.Args) == 0) { rw.WriteHeader(http.StatusBadRequest) fmt.Fprintf(rw, "{\"Error\": \"Payload must contain a CtorMsg with a Chaincode function name.\"}") restLogger.Error("{\"Error\": \"Payload must contain a CtorMsg with a Chaincode function name.\"}") @@ -929,7 +929,7 @@ func (s *ServerOpenchainREST) Invoke(rw web.ResponseWriter, req *web.Request) { } // Check that the CtorMsg is not left blank. - if (spec.ChaincodeSpec.CtorMsg == nil) || (spec.ChaincodeSpec.CtorMsg.Function == "") { + if (spec.ChaincodeSpec.CtorMsg == nil) || (len(spec.ChaincodeSpec.CtorMsg.Args) == 0) { rw.WriteHeader(http.StatusBadRequest) fmt.Fprintf(rw, "{\"Error\": \"Payload must contain a CtorMsg with a Chaincode function name.\"}") restLogger.Error("{\"Error\": \"Payload must contain a CtorMsg with a Chaincode function name.\"}") @@ -1071,7 +1071,7 @@ func (s *ServerOpenchainREST) Query(rw web.ResponseWriter, req *web.Request) { } // Check that the CtorMsg is not left blank. - if (spec.ChaincodeSpec.CtorMsg == nil) || (spec.ChaincodeSpec.CtorMsg.Function == "") { + if (spec.ChaincodeSpec.CtorMsg == nil) || (len(spec.ChaincodeSpec.CtorMsg.Args) == 0) { rw.WriteHeader(http.StatusBadRequest) fmt.Fprintf(rw, "{\"Error\": \"Payload must contain a CtorMsg with a Chaincode function name.\"}") restLogger.Error("{\"Error\": \"Payload must contain a CtorMsg with a Chaincode function name.\"}") @@ -1400,7 +1400,7 @@ func (s *ServerOpenchainREST) processChaincodeDeploy(spec *pb.ChaincodeSpec) rpc } // Check that the CtorMsg is not left blank. - if (spec.CtorMsg == nil) || (spec.CtorMsg.Function == "") { + if (spec.CtorMsg == nil) || (len(spec.CtorMsg.Args) == 0) { // Format the error appropriately for further processing error := formatRPCError(InvalidParams.Code, InvalidParams.Message, "Payload must contain a CtorMsg with a Chaincode function name.") restLogger.Error("Payload must contain a CtorMsg with a Chaincode function name.") @@ -1524,7 +1524,7 @@ func (s *ServerOpenchainREST) processChaincodeInvokeOrQuery(method string, spec } // Check that the CtorMsg is not left blank. - if (spec.ChaincodeSpec.CtorMsg == nil) || (spec.ChaincodeSpec.CtorMsg.Function == "") { + if (spec.ChaincodeSpec.CtorMsg == nil) || (len(spec.ChaincodeSpec.CtorMsg.Args) == 0) { // Format the error appropriately for further processing error := formatRPCError(InvalidParams.Code, InvalidParams.Message, "Payload must contain a CtorMsg with a Chaincode function name.") restLogger.Error("Payload must contain a CtorMsg with a Chaincode function name.") diff --git a/core/rest/rest_api.json b/core/rest/rest_api.json index e6ed9c5c53b..d82e763b1a6 100644 --- a/core/rest/rest_api.json +++ b/core/rest/rest_api.json @@ -630,10 +630,6 @@ "ChaincodeInput": { "type": "object", "properties": { - "function": { - "type": "string", - "description": "Function to execute within the Chaincode." - }, "args": { "type": "array", "items": { diff --git a/core/rest/rest_api_test.go b/core/rest/rest_api_test.go index 9f1af52a854..c6816f7b68b 100644 --- a/core/rest/rest_api_test.go +++ b/core/rest/rest_api_test.go @@ -18,6 +18,7 @@ package rest import ( "bytes" + "encoding/base64" "encoding/json" "fmt" "io/ioutil" @@ -33,6 +34,11 @@ import ( "github.com/hyperledger/fabric/protos" ) +var failb64 string = base64.StdEncoding.EncodeToString([]byte("fail")) +var initb64 string = base64.StdEncoding.EncodeToString([]byte("Init")) +var change_ownerb64 string = base64.StdEncoding.EncodeToString([]byte("change_owner")) +var get_ownerb64 string = base64.StdEncoding.EncodeToString([]byte("get_owner")) + func performHTTPGet(t *testing.T, url string) []byte { response, err := http.Get(url) if err != nil { @@ -117,7 +123,10 @@ func (d *mockDevops) Deploy(c context.Context, spec *protos.ChaincodeSpec) (*pro } func (d *mockDevops) Invoke(c context.Context, cis *protos.ChaincodeInvocationSpec) (*protos.Response, error) { - switch cis.ChaincodeSpec.CtorMsg.Function { + if len(cis.ChaincodeSpec.CtorMsg.Args) == 0 { + return nil, fmt.Errorf("No function invoked") + } + switch string(cis.ChaincodeSpec.CtorMsg.Args[0]) { case "fail": return nil, fmt.Errorf("Invoke failure") case "change_owner": @@ -127,7 +136,10 @@ func (d *mockDevops) Invoke(c context.Context, cis *protos.ChaincodeInvocationSp } func (d *mockDevops) Query(c context.Context, cis *protos.ChaincodeInvocationSpec) (*protos.Response, error) { - switch cis.ChaincodeSpec.CtorMsg.Function { + if len(cis.ChaincodeSpec.CtorMsg.Args) == 0 { + return nil, fmt.Errorf("No function invoked") + } + switch string(cis.ChaincodeSpec.CtorMsg.Args[0]) { case "fail": return nil, fmt.Errorf("Query failure with special-\" chars") case "get_owner": @@ -581,8 +593,9 @@ func TestServerOpenchainREST_API_Chaincode_Deploy(t *testing.T) { "path": "non-existing" }, "ctorMsg": { - "function": "Init", - "args": [] + "args": ["` + + initb64 + + `"] }, "secureContext": "myuser" } @@ -607,8 +620,9 @@ func TestServerOpenchainREST_API_Chaincode_Deploy(t *testing.T) { "path": "github.com/hyperledger/fabric/core/rest/test_chaincode" }, "ctorMsg": { - "function": "Init", - "args": [] + "args": ["` + + initb64 + + `"] } } }` @@ -629,8 +643,9 @@ func TestServerOpenchainREST_API_Chaincode_Deploy(t *testing.T) { "path": "github.com/hyperledger/fabric/core/rest/test_chaincode" }, "ctorMsg": { - "function": "Init", - "args": [] + "args": ["` + + initb64 + + `"] }, "secureContext": "myuser" } @@ -676,7 +691,7 @@ func TestServerOpenchainREST_API_Chaincode_Invoke(t *testing.T) { performHTTPPost(t, httpServer.URL+"/registrar", []byte(`{"enrollId":"myuser","enrollSecret":"password"}`)) // Test invoke with "fail" function - httpResponse, body = performHTTPPost(t, httpServer.URL+"/chaincode", []byte(`{"jsonrpc":"2.0","ID":123,"method":"invoke","params":{"type":1,"chaincodeID":{"name":"dummy"},"ctorMsg":{"function":"fail","args":[]},"secureContext":"myuser"}}`)) + httpResponse, body = performHTTPPost(t, httpServer.URL+"/chaincode", []byte(`{"jsonrpc":"2.0","ID":123,"method":"invoke","params":{"type":1,"chaincodeID":{"name":"dummy"},"ctorMsg":{"args":["`+failb64+`"]},"secureContext":"myuser"}}`)) if httpResponse.StatusCode != http.StatusOK { t.Errorf("Expected an HTTP status code %#v but got %#v", http.StatusOK, httpResponse.StatusCode) } @@ -686,7 +701,7 @@ func TestServerOpenchainREST_API_Chaincode_Invoke(t *testing.T) { } // Test invoke with "change_owner" function - httpResponse, body = performHTTPPost(t, httpServer.URL+"/chaincode", []byte(`{"jsonrpc":"2.0","ID":123,"method":"invoke","params":{"type":1,"chaincodeID":{"name":"dummy"},"ctorMsg":{"function":"change_owner","args":[]},"secureContext":"myuser"}}`)) + httpResponse, body = performHTTPPost(t, httpServer.URL+"/chaincode", []byte(`{"jsonrpc":"2.0","ID":123,"method":"invoke","params":{"type":1,"chaincodeID":{"name":"dummy"},"ctorMsg":{"args":["`+change_ownerb64+`"]},"secureContext":"myuser"}}`)) if httpResponse.StatusCode != http.StatusOK { t.Errorf("Expected an HTTP status code %#v but got %#v", http.StatusOK, httpResponse.StatusCode) } @@ -727,7 +742,7 @@ func TestServerOpenchainREST_API_Chaincode_Query(t *testing.T) { performHTTPPost(t, httpServer.URL+"/registrar", []byte(`{"enrollId":"myuser","enrollSecret":"password"}`)) // Test query with non-existing chaincode name - httpResponse, body = performHTTPPost(t, httpServer.URL+"/chaincode", []byte(`{"jsonrpc":"2.0","ID":123,"method":"query","params":{"type":1,"chaincodeID":{"name":"non-existing"},"ctorMsg":{"function":"Init","args":[]},"secureContext":"myuser"}}`)) + httpResponse, body = performHTTPPost(t, httpServer.URL+"/chaincode", []byte(`{"jsonrpc":"2.0","ID":123,"method":"query","params":{"type":1,"chaincodeID":{"name":"non-existing"},"ctorMsg":{"args":["`+initb64+`"]},"secureContext":"myuser"}}`)) if httpResponse.StatusCode != http.StatusOK { t.Errorf("Expected an HTTP status code %#v but got %#v", http.StatusOK, httpResponse.StatusCode) } @@ -737,7 +752,7 @@ func TestServerOpenchainREST_API_Chaincode_Query(t *testing.T) { } // Test query with fail function - httpResponse, body = performHTTPPost(t, httpServer.URL+"/chaincode", []byte(`{"jsonrpc":"2.0","ID":123,"method":"query","params":{"type":1,"chaincodeID":{"name":"dummy"},"ctorMsg":{"function":"fail","args":[]},"secureContext":"myuser"}}`)) + httpResponse, body = performHTTPPost(t, httpServer.URL+"/chaincode", []byte(`{"jsonrpc":"2.0","ID":123,"method":"query","params":{"type":1,"chaincodeID":{"name":"dummy"},"ctorMsg":{"args":["`+failb64+`"]},"secureContext":"myuser"}}`)) if httpResponse.StatusCode != http.StatusOK { t.Errorf("Expected an HTTP status code %#v but got %#v", http.StatusOK, httpResponse.StatusCode) } @@ -750,7 +765,7 @@ func TestServerOpenchainREST_API_Chaincode_Query(t *testing.T) { } // Test query with get_owner function - httpResponse, body = performHTTPPost(t, httpServer.URL+"/chaincode", []byte(`{"jsonrpc":"2.0","ID":123,"method":"query","params":{"type":1,"chaincodeID":{"name":"dummy"},"ctorMsg":{"function":"get_owner","args":[]},"secureContext":"myuser"}}`)) + httpResponse, body = performHTTPPost(t, httpServer.URL+"/chaincode", []byte(`{"jsonrpc":"2.0","ID":123,"method":"query","params":{"type":1,"chaincodeID":{"name":"dummy"},"ctorMsg":{"args":["`+get_ownerb64+`"]},"secureContext":"myuser"}}`)) if httpResponse.StatusCode != http.StatusOK { t.Errorf("Expected an HTTP status code %#v but got %#v", http.StatusOK, httpResponse.StatusCode) } diff --git a/core/system_chaincode/api/sysccapi.go b/core/system_chaincode/api/sysccapi.go index 6b98ec86ead..d8ebc553e95 100644 --- a/core/system_chaincode/api/sysccapi.go +++ b/core/system_chaincode/api/sysccapi.go @@ -47,7 +47,7 @@ type SystemChaincode struct { Path string //InitArgs initialization arguments to startup the system chaincode - InitArgs []string + InitArgs [][]byte // Chaincode is the actual chaincode object Chaincode shim.Chaincode diff --git a/core/system_chaincode/importsysccs.go b/core/system_chaincode/importsysccs.go index 06bca7e9a96..c99ed746250 100644 --- a/core/system_chaincode/importsysccs.go +++ b/core/system_chaincode/importsysccs.go @@ -28,7 +28,7 @@ var systemChaincodes = []*api.SystemChaincode{ Enabled: true, Name: "noop", Path: "github.com/hyperledger/fabric/bddtests/syschaincode/noop", - InitArgs: []string{}, + InitArgs: [][]byte{}, Chaincode: &noop.SystemChaincode{}, }} diff --git a/core/system_chaincode/systemchaincode_test.go b/core/system_chaincode/systemchaincode_test.go index 36b6f2c7695..b47d060c1be 100644 --- a/core/system_chaincode/systemchaincode_test.go +++ b/core/system_chaincode/systemchaincode_test.go @@ -24,6 +24,7 @@ import ( "time" "github.com/hyperledger/fabric/core/chaincode" + "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/core/db" "github.com/hyperledger/fabric/core/ledger" "github.com/hyperledger/fabric/core/system_chaincode/api" @@ -110,7 +111,7 @@ func TestExecuteDeploySysChaincode(t *testing.T) { Enabled: true, Name: "sample_syscc", Path: "github.com/hyperledger/fabric/core/system_chaincode/samplesyscc", - InitArgs: []string{}, + InitArgs: [][]byte{}, Chaincode: &samplesyscc.SampleSysCC{}, }, } @@ -121,9 +122,9 @@ func TestExecuteDeploySysChaincode(t *testing.T) { url := "github.com/hyperledger/fabric/core/system_chaincode/sample_syscc" f := "putval" - args := []string{"greeting", "hey there"} + args := shim.ToChaincodeArgs(f, "greeting", "hey there") - spec := &pb.ChaincodeSpec{Type: 1, ChaincodeID: &pb.ChaincodeID{Name: "sample_syscc", Path: url}, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + spec := &pb.ChaincodeSpec{Type: 1, ChaincodeID: &pb.ChaincodeID{Name: "sample_syscc", Path: url}, CtorMsg: &pb.ChaincodeInput{Args: args}} _, _, _, err = invoke(ctxt, spec, pb.Transaction_CHAINCODE_INVOKE) if err != nil { closeListenerAndSleep(lis) @@ -133,8 +134,8 @@ func TestExecuteDeploySysChaincode(t *testing.T) { } f = "getval" - args = []string{"greeting"} - spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: &pb.ChaincodeID{Name: "sample_syscc", Path: url}, CtorMsg: &pb.ChaincodeInput{Function: f, Args: args}} + args = shim.ToChaincodeArgs(f, "greeting") + spec = &pb.ChaincodeSpec{Type: 1, ChaincodeID: &pb.ChaincodeID{Name: "sample_syscc", Path: url}, CtorMsg: &pb.ChaincodeInput{Args: args}} _, _, _, err = invoke(ctxt, spec, pb.Transaction_CHAINCODE_QUERY) if err != nil { closeListenerAndSleep(lis) diff --git a/core/util/utils.go b/core/util/utils.go index 056a616e527..af4caad1026 100644 --- a/core/util/utils.go +++ b/core/util/utils.go @@ -31,7 +31,7 @@ import ( ) type alg struct { - hashFun func([]string) string + hashFun func([]byte) string } const defaultAlg = "sha256" @@ -86,39 +86,23 @@ func CreateUtcTimestamp() *gp.Timestamp { } //GenerateHashFromSignature returns a hash of the combined parameters -func GenerateHashFromSignature(path string, ctor string, args []string) []byte { - fargs := ctor - if args != nil { - for _, str := range args { - fargs = fargs + str - } - } - cbytes := []byte(path + fargs) - - b := make([]byte, len(cbytes)) - copy(b, cbytes) - hash := ComputeCryptoHash(b) - return hash +func GenerateHashFromSignature(path string, args []byte) []byte { + return ComputeCryptoHash(args) } // GenerateIDfromTxSHAHash generates SHA256 hash using Tx payload -func GenerateIDfromTxSHAHash(payload []string) string { - h := sha256.New() - for _, part := range payload { - utf8bytes := []byte(part) - h.Write(utf8bytes) - } - return fmt.Sprintf("%x", h.Sum([]byte{})) +func GenerateIDfromTxSHAHash(payload []byte) string { + return fmt.Sprintf("%x", sha256.Sum256(payload)) } // GenerateIDWithAlg generates an ID using a custom algorithm -func GenerateIDWithAlg(customIDgenAlg string, strPayload []string) (string, error) { +func GenerateIDWithAlg(customIDgenAlg string, payload []byte) (string, error) { if customIDgenAlg == "" { customIDgenAlg = defaultAlg } var alg = availableIDgenAlgs[customIDgenAlg] if alg.hashFun != nil { - return alg.hashFun(strPayload), nil + return alg.hashFun(payload), nil } return "", fmt.Errorf("Wrong ID generation algorithm was given: %s", customIDgenAlg) } diff --git a/core/util/utils_test.go b/core/util/utils_test.go index 97c0896124c..5be086f0e95 100644 --- a/core/util/utils_test.go +++ b/core/util/utils_test.go @@ -59,12 +59,12 @@ func TestTimestamp(t *testing.T) { } func TestGenerateHashFromSignature(t *testing.T) { - if bytes.Compare(GenerateHashFromSignature("aPath", "aCtor", []string{"1", "2"}), - GenerateHashFromSignature("aPath", "aCtor", []string{"1", "2"})) != 0 { + if bytes.Compare(GenerateHashFromSignature("aPath", []byte("aCtor12")), + GenerateHashFromSignature("aPath", []byte("aCtor12"))) != 0 { t.Fatalf("Expected hashes to match, but they did not match") } - if bytes.Compare(GenerateHashFromSignature("aPath", "aCtor", []string{"1", "2"}), - GenerateHashFromSignature("bPath", "bCtor", []string{"3", "4"})) == 0 { + if bytes.Compare(GenerateHashFromSignature("aPath", []byte("aCtor12")), + GenerateHashFromSignature("bPath", []byte("bCtor34"))) == 0 { t.Fatalf("Expected hashes to be different, but they match") } } diff --git a/examples/chaincode/go/chaincode_example04/chaincode_example04.go b/examples/chaincode/go/chaincode_example04/chaincode_example04.go index fc483ed8aaf..28a1b6451a5 100644 --- a/examples/chaincode/go/chaincode_example04/chaincode_example04.go +++ b/examples/chaincode/go/chaincode_example04/chaincode_example04.go @@ -33,8 +33,7 @@ type SimpleChaincode struct { func (t *SimpleChaincode) getChaincodeToCall(stub *shim.ChaincodeStub) (string, error) { //This is the hashcode for github.com/hyperledger/fabric/core/example/chaincode/chaincode_example02 //if the example is modifed this hashcode will change!! - chainCodeToCall := "a5389f7dfb9efae379900a41db1503fea2199fe400272b61ac5fe7bd0c6b97cf10ce3aa8dd00cd7626ce02f18accc7e5f2059dae6eb0786838042958352b89fb" //with SHA3 - + chainCodeToCall := "9578ffde22fede358d6390d6a3f81bee6e690e210ad7c544e93d567f55e9290369df30822cd771acc0ca233fb32c7948c257599e0412b9b4f2d1eae5721486ce" return chainCodeToCall, nil } @@ -92,8 +91,8 @@ func (t *SimpleChaincode) Invoke(stub *shim.ChaincodeStub, function string, args } f := "invoke" - invokeArgs := []string{"a", "b", "10"} - response, err := stub.InvokeChaincode(chainCodeToCall, f, invokeArgs) + invokeArgs := shim.ToChaincodeArgs(f, "a", "b", "10") + response, err := stub.InvokeChaincode(chainCodeToCall, invokeArgs) if err != nil { errStr := fmt.Sprintf("Failed to invoke chaincode. Got error: %s", err.Error()) fmt.Printf(errStr) diff --git a/examples/chaincode/go/chaincode_example05/chaincode_example05.go b/examples/chaincode/go/chaincode_example05/chaincode_example05.go index 4bc7801eafa..820f4e94dc7 100644 --- a/examples/chaincode/go/chaincode_example05/chaincode_example05.go +++ b/examples/chaincode/go/chaincode_example05/chaincode_example05.go @@ -73,8 +73,8 @@ func (t *SimpleChaincode) Invoke(stub *shim.ChaincodeStub, function string, args // Query chaincode_example02 f := "query" - queryArgs := []string{"a"} - response, err := stub.QueryChaincode(chaincodeURL, f, queryArgs) + queryArgs := shim.ToChaincodeArgs(f, "a") + response, err := stub.QueryChaincode(chaincodeURL, queryArgs) if err != nil { errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", err.Error()) fmt.Printf(errStr) @@ -87,8 +87,8 @@ func (t *SimpleChaincode) Invoke(stub *shim.ChaincodeStub, function string, args return nil, errors.New(errStr) } - queryArgs = []string{"b"} - response, err = stub.QueryChaincode(chaincodeURL, f, queryArgs) + queryArgs = shim.ToChaincodeArgs(f, "b") + response, err = stub.QueryChaincode(chaincodeURL, queryArgs) if err != nil { errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", err.Error()) fmt.Printf(errStr) @@ -133,8 +133,8 @@ func (t *SimpleChaincode) Query(stub *shim.ChaincodeStub, function string, args // Query chaincode_example02 f := "query" - queryArgs := []string{"a"} - response, err := stub.QueryChaincode(chaincodeURL, f, queryArgs) + queryArgs := shim.ToChaincodeArgs(f, "a") + response, err := stub.QueryChaincode(chaincodeURL, queryArgs) if err != nil { errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", err.Error()) fmt.Printf(errStr) @@ -147,8 +147,8 @@ func (t *SimpleChaincode) Query(stub *shim.ChaincodeStub, function string, args return nil, errors.New(errStr) } - queryArgs = []string{"b"} - response, err = stub.QueryChaincode(chaincodeURL, f, queryArgs) + queryArgs = shim.ToChaincodeArgs(f, "b") + response, err = stub.QueryChaincode(chaincodeURL, queryArgs) if err != nil { errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", err.Error()) fmt.Printf(errStr) diff --git a/examples/chaincode/go/passthru/passthru.go b/examples/chaincode/go/passthru/passthru.go index 5d481c9ff6c..27dbb15ba2c 100644 --- a/examples/chaincode/go/passthru/passthru.go +++ b/examples/chaincode/go/passthru/passthru.go @@ -47,17 +47,10 @@ func (p *PassthruChaincode) iq(invoke bool, stub *shim.ChaincodeStub, function s } chaincodeID := function - var f string - var cargs []string - if len(args) > 0 { - f = args[0] - cargs = args[1:] - } - if invoke { - return stub.InvokeChaincode(chaincodeID, f, cargs) + return stub.InvokeChaincode(chaincodeID, shim.ToChaincodeArgs(args...)) } - return stub.QueryChaincode(chaincodeID, f, cargs) + return stub.QueryChaincode(chaincodeID, shim.ToChaincodeArgs(args...)) } // Invoke passes through the invoke call diff --git a/protos/chaincode.pb.go b/protos/chaincode.pb.go index acbfe93a20a..771373199bd 100644 --- a/protos/chaincode.pb.go +++ b/protos/chaincode.pb.go @@ -187,8 +187,7 @@ func (*ChaincodeID) ProtoMessage() {} // Carries the chaincode function and its arguments. type ChaincodeInput struct { - Function string `protobuf:"bytes,1,opt,name=function" json:"function,omitempty"` - Args []string `protobuf:"bytes,2,rep,name=args" json:"args,omitempty"` + Args [][]byte `protobuf:"bytes,2,rep,name=args,proto3" json:"args,omitempty"` } func (m *ChaincodeInput) Reset() { *m = ChaincodeInput{} } diff --git a/protos/chaincode.proto b/protos/chaincode.proto index efb80a31c71..495817d56bd 100644 --- a/protos/chaincode.proto +++ b/protos/chaincode.proto @@ -47,10 +47,7 @@ message ChaincodeID { // Carries the chaincode function and its arguments. message ChaincodeInput { - - string function = 1; - repeated string args = 2; - + repeated bytes args = 1; } // Carries the chaincode specification. This is the actual metadata required for