Flexible input (variadic function) in Fabric Chaincode possible? - hyperledger-fabric

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 }

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)

Related

Can different hyperledger fabric chaincodes view all key/value pairs in world state?

I got two Smart contract definitions currently commited on the same channel. Both are written in Go and are somewhat basic, only doing basic CRUD operations. However, I've noticed that key/value pairs written with one chaincode, are unavailable to the other.
So, with go-audit I created the following record:
But then, I tried to perform a get operation on key ping with chaincode go-asset, is not found with the following error (returned by the chaincode)
bad request: failed to invoke go-asset: Error: No valid responses from any peers. Errors: **someurl***, status=500, message=the asset ping does not exist.
This is the transaction that reads:
func (c *GoAssetContract) ReadGoAsset(ctx contractapi.TransactionContextInterface, goAssetID string) (*GoAsset, error) {
exists, err := c.GoAssetExists(ctx, hlpAssetID)
if err != nil {
return nil, fmt.Errorf("could not read from world state. %s", err)
} else if !exists {
return nil, fmt.Errorf("the asset %s does not exist", goAssetID)
}
bytes, _ := ctx.GetStub().GetState(goAssetID)
goAsset := new(GoAsset)
err = json.Unmarshal(bytes, goAsset)
if err != nil {
return nil, fmt.Errorf("could not unmarshal world state data to type GoAsset")
}
return GoAsset, nil
}
and the GoAsset struct
// GoAsset stores a value
type GoAsset struct {
Value string `json:"value"`
}
shouldn't world state be available to all chaincodes approved/committed on a channel?
chaincodes deployed to the same channel are namespaced, so that their keys remain specific to the chaincode that is using them. So what you see with 2 chaincodes deployed to the same channel is working as designed, they cannot see each others keys.
However a chaincode can consist of multiple distinct contracts and in that case the contracts have access to each others keys because they are still in the same chaincode deployment.
You can have one chaincode invoke/query another chaincode on the same channel using the InvokeChaincode() API. The called chaincode can return keys/values to the caller chaincode. With this approach it is possible to embed all access control logic in the called chaincode.

Hyperledger Fabric- Contracts are required to have at least 1 (non-ignored) public method

I'm using fabric tools provided for composer to deploy fabric network as it deploys 1 peer, 1 orderer, 1 couchdb, & 1 fabric-ca. I am able to install chain code on peer but instantiation fails with following error. I am using command on fabric-peer.
error in simulation: failed to execute transaction
2037ca1d4ec2682ad17499156de49aeb28053ad5b6943f1fe3520c407bac570e:
could not launch chaincode
product_1.1.1:e2901eb986174a4ac9bb963b06db851ea347ed6b48930de813c3dbc38df94a82:
chaincode registration failed: container exited with 2
when i checked the logs of docker container it is returning me this error
2021/07/29 08:41:29 Error create network chaincode chaincode: Contracts are required to have at least 1 (non-ignored) public method.
Contract PRODUCTChainCode has none. Method names that have been
ignored: GetAfterTransaction, GetBeforeTransaction, GetInfo, GetName,
GetTransactionContextHandler, GetUnknownTransaction,
GetIgnoredFunctions and GetEvaluateTransactions panic: Error create
network chaincode chaincode: Contracts are required to have at least
1 (non-ignored) public method. Contract PRODUCTChainCode has none.
Method names that have been ignored: GetAfterTransaction,
GetBeforeTransaction, GetInfo, GetName, GetTransactionContextHandler,
GetUnknownTransaction, GetIgnoredFunctions and GetEvaluateTransactions
goroutine 1 [running]: log.Panicf(0xa40a03, 0x2e, 0xc00059ff68, 0x1,
0x1) /usr/local/go/src/log/log.go:358 +0xc5 main.main()
/chaincode/input/src/main.go:18 +0x1b0
here is my main.go file
package main
import (
"log"
"product-chaincode/core/messages"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
// PRODUCTChainCode implementation
type PRODUCTChainCode struct {
contractapi.Contract
}
func main() {
PRODUCTChainCode, err := contractapi.NewChaincode(&PRODUCTChainCode{})
if err != nil {
log.Panicf(messages.ChaincodeCreateError, err.Error())
}
if err := PRODUCTChainCode.Start(); err != nil {
log.Panicf(messages.ChaincodeStartError, err.Error())
}
}
The following error message Contracts are required to have at last 1 (non-ignored) public method." tells us that the chain code you wrote does not have a public method.
In the Go language, functions (and variables) whose names begin with uppercase letters are treated as public and functions (and variables) whose names begin with lowercase letters are private.
Currently, there is only one main function in the chain code you created, so it seems that it cannot be executed because there is no public function.
Try adding one public function whose function name begins with uppercase letters.

Problems parsing POST request with GO webserver

I am very new to GO and I am trying to set up a very basic GO webserver and so far I have had no issues handling GET requests without any parameters, however now I am trying a POST request with some parameters and I am having two different issues depending on how I send the request.
Here's the code that handles the route
func SubmitData(w http.ResponseWriter, r *http.Request) {
reqBody, _ := ioutil.ReadAll(r.Body)
var userInfo SubmittedUserInfo
fmt.Println(string(reqBody))
unmarshalErr := json.Unmarshal(reqBody, &userInfo)
if unmarshalErr != nil {
fmt.Println("Error Unmarshalling JSON:", unmarshalErr)
}
_ = json.NewEncoder(w).Encode(userInfo)
if unmarshalErr != nil {
log.Printf("error decoding response: %v", unmarshalErr)
if e, ok := unmarshalErr.(*json.SyntaxError); ok {
log.Printf("syntax error at byte offset %d", e.Offset)
}
log.Printf("request body: %q", reqBody)
}
}
Here's the SubmittedUserInfo structure that I am trying to create with the JSON data.
type SubmittedUserInfo struct {
Number int16 `json:"Number"` // is always a number between 0 and 10
Age int16 `json:"Age"`
Nationality string `json:"Nationality"`
Gender string `json:"Gender"`
DominantHand string `json:"DominantHand"`
}
Here is the function that handles the requests
func handleRequests(port string) {
muxRouter := mux.NewRouter().StrictSlash(true)
muxRouter.HandleFunc("/", Home)
muxRouter.HandleFunc("/health", HealthCheck)
muxRouter.HandleFunc("/submit_data", SubmitData).Methods("POST")
log.Fatal(http.ListenAndServe(port, muxRouter))
}
Here are the actual errors I am getting. If I send the following request with Python 3.8 I get the corresponding error
import requests
url = "http://localhost:5050/submit_data"
args = {
"number": 0,
"age": 25,
"nationality": "russian",
"gender": "male",
"dominantHand": "right"
}
response = requests.post(url, args)
Error:
Error Unmarshalling JSON: invalid character 'N' looking for beginning of value
2021/03/28 11:23:27 error decoding response: invalid character 'N' looking for beginning of value
2021/03/28 11:23:27 syntax error at byte offset 1
2021/03/28 11:23:27 request body: "Number=0&Age=25&Nationality=russian&Gender=male&DominantHand=right"
Now if I send a request with Postman specifying the exact same parameters I get the error
Error Unmarshalling JSON: unexpected end of JSON input
2021/03/28 11:24:38 error decoding response: unexpected end of JSON input
2021/03/28 11:24:38 syntax error at byte offset 0
2021/03/28 11:24:38 request body: ""
What am I doing wrong? I am very used to doing this in Python and I won't be surprised if there's some additional work I need to do in GO to ensure everything is formatted properly, but I have no idea what that might be, so any help or advice would be greatly appreciated!
Your python code is sending the data in application/x-www-form-urlencoded format, you can see that by looking at the output of the log.Printf("request body: %q", reqBody) statement, which is:
2021/03/28 11:23:27 request body: "Number=0&Age=25&Nationality=russian&Gender=male&DominantHand=right"
and that is not JSON.
To send JSON with your python script you can do
requests.post(url, json=args)

Hyperledger Fabric: ENDORSEMENT_MISMATCH on asset query

It seems like I misunderstand how Hyperledger Fabric processes a query. I'm currently using the fabric-go-sdk to query an asset from the ledger like
asset, err := client.Query(channel.Request{ChaincodeID: someCCname, Fcn: "query", Args: [][]byte{[]byte(someID)}})
When my system is under load (many new transactions that are unrelated to the query) I sometimes get the following error message:
endorsement validation failed: Endorser Client Status Code: (3)
ENDORSEMENT_MISMATCH. Description: ProposalResponsePayloads do not
match.
Why is an endorsement involved if data is only queried? To me the error message seems to indicate that multiple peers answered differently to the query. Does that mean that some peers have the asset already committed into the ledger while others do not? It is noteworthy that the query is ran very shortly after the asset is created and does not happen consistently.
The query chaincode is very straight-forward and minimal:
func (c *TestChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
data, err := stub.GetState(args[0])
if err != nil {
return shim.Error(err)
}
if data== nil {
return shim.Error(err)
}
return shim.Success(data)
}

Bleno throws an index-out-of-range error on binary data

My code, that provides a BLE service, is running under nodejs and is using bleno package.
My service characteristic supports read and notify.
Here is my simplified implementation of onSubscribe function:
function(maxValueSize, updateValueCallback) {
var a = buildPacket();
var buf = Buffer.from(a);
console.log('Buffer: ', buf);
updateValueCallback(this.RESULT_SUCCESS, buf);
}
Function buildPacket returns a Uint8Array of 20 bytes. The console statement shows that the buffer is of 20 bytes and has the expected values.
However, call to updateValueCallback throws an error:
RangeError: Index out of range
at checkInt (buffer.js:1187:11)
at Buffer.writeUInt8 (buffer.js:1233:5)
at Gatt.<anonymous> (/home/pi/Dev/MyTest/node_modules/bleno/lib/hci socket/gatt.js:881:33)
Here is the relevant code from gatt.js:
878 if (useNotify) {
879 var notifyMessage = new Buffer(3 + dataLength);
880
881 notifyMessage.writeUInt8(ATT_OP_HANDLE_NOTIFY, 0);
882 notifyMessage.writeUInt16LE(valueHandle, 1);
Is there any step I am missing?
Most examples on bleno I read seem to send string data. However, I need to send binary data. Is there anything special required for sending binary data?
Turns out updateValueCallback takes only one parameter. I should have looked at bleno examples a bit more carefully:-(.
Just passing buf as the sole parameter fixed the problem.

Resources