Search code examples
hyperledger-fabrichyperledger-chaincode

Flexible input (variadic function) in Fabric Chaincode possible?


I'm trying to create a Hyperledger Fabric chaincode function/method which accepts an indeterminate number of inputs (i.e. variadic function).

I'm working in Go.

Here is what I have:

 38 type Prod struct {
 39   ProdID string `json:"prodID"`
 40   Attributes []string `json:"attributes"`
 41 }

420 func (s *SmartContract) Ver2(ctx contractapi.TransactionContextInterface, prodID string, input...string) error {
421   //create Prod struct
422   var p Prod
423   //set values based on input:
424   p.ProdID = prodID
425   p.Attributes = make([]string,len(input))
426   for i:=0; i<len(input); i++ {
427     p.Attributes[i]=input[i]
428   }
429   //convert Go struct into JSON format
430   assetJSON, err := json.Marshal(p)
431   if err != nil {
432     return fmt.Errorf("Problem encountered during marshaling: %v\n", err)
433   } 
434   //write data to ledger
435   return ctx.GetStub().PutState(prodID, assetJSON)
436 }

When I try to call this chaincode with the following code

peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /home/hans/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n scm -c '{"Args":["Ver2","BB","ok","maybe","test"]}'

I get this error:

Error: endorsement failure during invoke. response: status:500 message:"Error managing parameter param1. Conversion error. Value ok was not passed in expected format []string"

Is there a problem with my code or is this a limitation of the Fabric api that I am using?
I've tried searching online, but haven't stumbled on any relevant discussion or documentation around variadic functions in chaincode.

I got a similar (albeit non-chaincode) working version here: https://play.golang.org/p/-7NX8-Uf88p

For what it's worth, I succeeded in making a variadic helper function which I can call from another chaincode function, but the variadic input cannot be modified without updating the chaincode. (So not very useful to me.)

For reference, those two functions are listed below:

394 func (s *Prod) VeriTest(ctx contractapi.TransactionContextInterface, prodID string, attr ...string) { 
395   s.ProdID = prodID
396   s.Attributes = make([]string, len(attr))
397   for i:=0; i<len(attr); i++ { 
398     s.Attributes[i]=attr[i]
399   } 
400 }
401 
402 //Ver: Proof of Concept - linked to VeriTest above
403 func (s *SmartContract) Ver(ctx contractapi.TransactionContextInterface) error { 
404   //create Prod struct
405   var p Prod
406   //call VeriTest on Prod
407   p.VeriTest(ctx,"AA","test","string","five")
408   //convert Go struct into JSON format
409   assetJSON, err := json.Marshal(p)
410   if err != nil { 
411     return err
412   } 
413   //write data to ledger
414   return ctx.GetStub().PutState("AA", assetJSON)
415 }

Solution

  • The issue you are having is due to the fact that you are not properly encoding the []string parameter when calling peer chaincode invoke.

    The way you are calling it is sending an array of string parameters, but what you are trying to do is send one string parameter and then a second parameter which is an array of strings.

    In order to do this, you will need to encode the second parameter as a JSON array:

    peer chaincode invoke ... -n scm -c '{"Args":["Ver2","BB","[\"ok\",\"maybe\",\"test\"]"}'

    • The first Arg is of course the function name.
    • The second Arg will map to the prodID param.
    • The third Arg is a string-encoded JSON array which will map to your attr param.

    The last thing for a working solution is to change the type of the attr parameter from a variadic ...string to an array []string.

    (I omitted the other invoke params with the ... for clarity)