You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
One thing to note is that GetBlockByNumber works fine.
This has been observed in my testing of v2.5.3 and v2.5.10.
Steps to reproduce
This has been reproducible locally with Fabric Samples' test-network, on Hyperledger Labs CC-Tools and in production environments. To reproduce it with the test-network, follow the steps.
Deploy the test network in Fabric Samples
cd test-network
./network.sh up createChannel
./network.sh deployCC
Assuming you are on Fabric Samples, replace asset-transfer-basic/application-gateway-go/assetTransfer.go with the following content:
/*Copyright 2021 IBM All Rights Reserved.SPDX-License-Identifier: Apache-2.0*/package main
import (
"crypto/x509""encoding/hex""fmt""os""path""time""github.com/hyperledger/fabric-gateway/pkg/client""github.com/hyperledger/fabric-gateway/pkg/identity"
protos "github.com/hyperledger/fabric-protos-go-apiv2/common""google.golang.org/grpc""google.golang.org/grpc/credentials""google.golang.org/protobuf/proto"
)
const (
mspID="Org1MSP"cryptoPath="../../test-network/organizations/peerOrganizations/org1.example.com"certPath=cryptoPath+"/users/User1@org1.example.com/msp/signcerts"keyPath=cryptoPath+"/users/User1@org1.example.com/msp/keystore"tlsCertPath=cryptoPath+"/peers/peer0.org1.example.com/tls/ca.crt"peerEndpoint="dns:///localhost:7051"gatewayPeer="peer0.org1.example.com"
)
funcmain() {
// The gRPC client connection should be shared by all Gateway connections to this endpointclientConnection:=newGrpcConnection()
deferclientConnection.Close()
id:=newIdentity()
sign:=newSign()
// Create a Gateway connection for a specific client identitygw, err:=client.Connect(
id,
client.WithSign(sign),
client.WithClientConnection(clientConnection),
// Default timeouts for different gRPC callsclient.WithEvaluateTimeout(5*time.Second),
client.WithEndorseTimeout(15*time.Second),
client.WithSubmitTimeout(5*time.Second),
client.WithCommitStatusTimeout(1*time.Minute),
)
iferr!=nil {
panic(err)
}
defergw.Close()
// Override default values for chaincode and channel name as they may differ in testing contexts.chaincodeName:="qscc"ifccname:=os.Getenv("CHAINCODE_NAME"); ccname!="" {
chaincodeName=ccname
}
channelName:="mychannel"ifcname:=os.Getenv("CHANNEL_NAME"); cname!="" {
channelName=cname
}
network:=gw.GetNetwork(channelName)
contract:=network.GetContract(chaincodeName)
block, _:=contract.EvaluateTransaction("GetBlockByNumber", "mychannel", "3")
blockMap, _:=decodeBlock(block)
blockHash:=blockMap["header"].(map[string]interface{})["data_hash"].(string)
fmt.Println("Block hash:", blockHash)
hashBytes, _:=hex.DecodeString(blockHash)
_, err=contract.EvaluateTransaction("GetBlockByHash", "mychannel", string(hashBytes))
iferr!=nil {
panic(fmt.Errorf("failed to evaluate transaction: %w", err))
}
}
funcdecodeBlock(b []byte) (map[string]interface{}, error) {
varblock protos.Blockerr:=proto.Unmarshal(b, &block)
iferr!=nil {
returnnil, err
}
blockMap:=map[string]interface{}{
"header": map[string]interface{}{
"number": block.Header.Number,
"previous_hash": hex.EncodeToString(block.Header.PreviousHash),
"data_hash": hex.EncodeToString(block.Header.DataHash),
},
"metadata": block.Metadata,
}
returnblockMap, nil
}
// newGrpcConnection creates a gRPC connection to the Gateway server.funcnewGrpcConnection() *grpc.ClientConn {
certificatePEM, err:=os.ReadFile(tlsCertPath)
iferr!=nil {
panic(fmt.Errorf("failed to read TLS certifcate file: %w", err))
}
certificate, err:=identity.CertificateFromPEM(certificatePEM)
iferr!=nil {
panic(err)
}
certPool:=x509.NewCertPool()
certPool.AddCert(certificate)
transportCredentials:=credentials.NewClientTLSFromCert(certPool, gatewayPeer)
connection, err:=grpc.NewClient(peerEndpoint, grpc.WithTransportCredentials(transportCredentials))
iferr!=nil {
panic(fmt.Errorf("failed to create gRPC connection: %w", err))
}
returnconnection
}
// newIdentity creates a client identity for this Gateway connection using an X.509 certificate.funcnewIdentity() *identity.X509Identity {
certificatePEM, err:=readFirstFile(certPath)
iferr!=nil {
panic(fmt.Errorf("failed to read certificate file: %w", err))
}
certificate, err:=identity.CertificateFromPEM(certificatePEM)
iferr!=nil {
panic(err)
}
id, err:=identity.NewX509Identity(mspID, certificate)
iferr!=nil {
panic(err)
}
returnid
}
// newSign creates a function that generates a digital signature from a message digest using a private key.funcnewSign() identity.Sign {
privateKeyPEM, err:=readFirstFile(keyPath)
iferr!=nil {
panic(fmt.Errorf("failed to read private key file: %w", err))
}
privateKey, err:=identity.PrivateKeyFromPEM(privateKeyPEM)
iferr!=nil {
panic(err)
}
sign, err:=identity.NewPrivateKeySign(privateKey)
iferr!=nil {
panic(err)
}
returnsign
}
funcreadFirstFile(dirPathstring) ([]byte, error) {
dir, err:=os.Open(dirPath)
iferr!=nil {
returnnil, err
}
fileNames, err:=dir.Readdirnames(1)
iferr!=nil {
returnnil, err
}
returnos.ReadFile(path.Join(dirPath, fileNames[0]))
}
Under asset-transfer-basic/application-gateway-go/ run
go mod tidy
go mod vendor
Run the application
go run .
The application will GetBlockByNumber with 3 as the number for the block, then it will decode it partially so as to get the hash of the block, and then attempt to fetch the block by hash. This should throw a panic.
panic: failed to evaluate transaction: rpc error: code = Aborted desc = failed to evaluate transaction, see attached details for more info
Check the peer logs
2024-10-02 22:25:40.176 UTC 0113 ERRO [endorser] simulateProposal -> failed to invoke chaincode qscc, error: transaction returned with failure: failed to marshal response: string field contains invalid UTF-8
github.com/hyperledger/fabric/core/chaincode.processChaincodeExecutionResult
/core/chaincode/chaincode_support.go:188
github.com/hyperledger/fabric/core/chaincode.(*ChaincodeSupport).Execute
/core/chaincode/chaincode_support.go:162
github.com/hyperledger/fabric/core/endorser.(*SupportImpl).Execute
/core/endorser/support.go:126
github.com/hyperledger/fabric/core/endorser.(*Endorser).callChaincode
/core/endorser/endorser.go:120
github.com/hyperledger/fabric/core/endorser.(*Endorser).simulateProposal
/core/endorser/endorser.go:187
github.com/hyperledger/fabric/core/endorser.(*Endorser).ProcessProposalSuccessfullyOrError
/core/endorser/endorser.go:409
github.com/hyperledger/fabric/core/endorser.(*Endorser).ProcessProposal
/core/endorser/endorser.go:350
github.com/hyperledger/fabric/core/handlers/auth/filter.(*expirationCheckFilter).ProcessProposal
/core/handlers/auth/filter/expiration.go:61
github.com/hyperledger/fabric/core/handlers/auth/filter.(*filter).ProcessProposal
/core/handlers/auth/filter/filter.go:32
github.com/hyperledger/fabric/internal/pkg/gateway.(*EndorserServerAdapter).ProcessProposal
/internal/pkg/gateway/gateway.go:43
github.com/hyperledger/fabric/internal/pkg/gateway.(*Server).Evaluate.func1
/internal/pkg/gateway/evaluate.go:64
runtime.goexit
/usr/local/go/src/runtime/asm_arm64.s:1223 channel=mychannel txID=54e6bb0b
The text was updated successfully, but these errors were encountered:
Upon further investigation @Tubar2 has discovered that the data_hash that we were using to try and get the block is not the hash needed for the function.
We got the function to work correctly by going inside the peer chains folder, opening up the LevelDB data that indexes each block with the hash. With that hash (which is not the data_hash), it works.
The question is: how do I get the correct hash since it doesn't seem to be in the block and taking the SHA256 of the block bytes does not return an existing hash. The answer is that the hash should be taken from the header of the block. Like this:
funcBlockHeaderHash(b*protos.BlockHeader) []byte {
sum:=sha256.Sum256(BlockHeaderBytes(b))
returnsum[:]
}
typeasn1Headerstruct {
Number*big.IntPreviousHash []byteDataHash []byte
}
funcBlockHeaderBytes(b*protos.BlockHeader) []byte {
asn1Header:=asn1Header{
PreviousHash: b.PreviousHash,
DataHash: b.DataHash,
Number: new(big.Int).SetUint64(b.Number),
}
result, err:=asn1.Marshal(asn1Header)
iferr!=nil {
// Errors should only arise for types which cannot be encoded, since the// BlockHeader type is known a-priori to contain only encodable types, an// error here is fatal and should not be propagatedpanic(err)
}
returnresult
}
Because of that, I'll close the issue. Hopefully, this serves for anyone that thinks that data_hash is the actual hash the block is indexed by.
Description
Using qscc to get block by hash using the GetBlockByHash transaction. I am getting the following error as logged by the peer.
I'm querying it using the Gateway SDK for Go like this (some syntax supressed for clarity):
Before the error the peer also logs:
One thing to note is that
GetBlockByNumber
works fine.This has been observed in my testing of v2.5.3 and v2.5.10.
Steps to reproduce
This has been reproducible locally with Fabric Samples'
test-network
, on Hyperledger Labs CC-Tools and in production environments. To reproduce it with thetest-network
, follow the steps.asset-transfer-basic/application-gateway-go/assetTransfer.go
with the following content:asset-transfer-basic/application-gateway-go/
runThe application will
GetBlockByNumber
with3
as the number for the block, then it will decode it partially so as to get the hash of the block, and then attempt to fetch the block by hash. This should throw a panic.The text was updated successfully, but these errors were encountered: