如何解析chaincode package文件

如何解析chaincode package文件

背景
在做chaincode instanticate的时候碰到如下错误:
Error: Error endorsing query: rpc error: code = Unknown desc = error executing chaincode: could not get ChaincodeDeploymentSpec for {ccname}:{ccversion}: get ChaincodeDeploymentSpec for {ccname}/{channel} from LSCC error: chaincode fingerprint mismatch data mismatch - <nil>

原因是
首先peer在安装chaincode的时候会把chaincode安装到路径下面:/var/hyperledger/production/chaincodes/{ccname}.{ccversion},这是一个chaincode package格式文件,包含chaincode的打包源文件(tar.gz),和其他信息。

然后peer在第一次instanticate chaincode的时候会把chainocde的验证信息写到channel的state db里面,这样当chaincode死掉再重新instantiate的时候,会验证chaincode的package是否已经发生过变化,对照从chaincode package中读取的hash值是否和state db里面已经存储的hash值是否一致。

$ cat fabric/core/common/ccprovider/cdspackage.go
// ValidateCC returns error if the chaincode is not found or if its not a
// ChaincodeDeploymentSpec
func (ccpack *CDSPackage) ValidateCC(ccdata *ChaincodeData) error {
    if ccpack.depSpec == nil {
        return fmt.Errorf("uninitialized package")
    }

    if ccpack.data == nil {
        return fmt.Errorf("nil data")
    }

    if ccdata.Name != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name || ccdata.Version != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version {
        return fmt.Errorf("invalid chaincode data %v (%v)", ccdata, ccpack.depSpec.ChaincodeSpec.ChaincodeId)
    }

    otherdata := &CDSData{}
    err := proto.Unmarshal(ccdata.Data, otherdata)
    if err != nil {
        return err
    }

    // Here verify the hash value whether same ?
    if !ccpack.data.Equals(otherdata) {
        return fmt.Errorf("data mismatch")
    }

    return nil
}

如果不一致,chaincode instantiate就会失败,报上述错误。

下面程序就是读取chaincode package的内容把其中的chaincode代码包,和hash值取出来。

package main

import (
       "fmt"
       "io/ioutil"

    pb "github.com/hyperledger/fabric/protos/peer"
       "github.com/hyperledger/fabric/core/common/ccprovider"
       "github.com/golang/protobuf/proto"
)


/**
 * This tool will extrace chaincode tar.gz package from chaincode CDSPackage
 *  Chaincode package are named format as: ccname.ccversion, and
 *  placed under path set by SetChaincodesPath
 * Output:
 *  chaincode tar.gz file
 *  chaincode code and meta hash value
 */
func main() {

    ccprovider.SetChaincodesPath("/var/hyperledger/production/chaincodes")

    var ccpack  *ccprovider.CDSPackage = &ccprovider.CDSPackage{}
    var cdsdata *pb.ChaincodeDeploymentSpec
    _, cdsdata, err := ccpack.InitFromFS("{ccname}", "{ccversion}")
    if err != nil {
        fmt.Printf("ERROR: Cannot load chaincode package, error=[%v]\n", err)
        return
    }

    var ccdata *ccprovider.ChaincodeData = ccpack.GetChaincodeData()
    otherdata := &ccprovider.CDSData{}
    err = proto.Unmarshal(ccdata.Data, otherdata)
    if err != nil {
        fmt.Printf("ERROR: Cannot unmarshal chaincode data, error=[%v]\n", err)
        return
    }

    ioutil.WriteFile("chaincode.tar.gz",    cdsdata.CodePackage, 0644)
    ioutil.WriteFile("chaincode.code.hash", []byte(fmt.Sprintf("%v\n", otherdata.CodeHash)), 0644)
    ioutil.WriteFile("chaincode.meta.hash", []byte(fmt.Sprintf("%v\n", otherdata.MetaDataHash)), 0644)

    fmt.Printf("Done\n")
}

我们可以解开chaincode.tar.gz查看chaincode文件的内容,注意chaincode.tar.gz的文件格式,是一个tar文件,也就是说包含原始代码文件的meta信息,比如读写权限,owner/group,时间戳等等等信息(好在fabric在打包的时候重写了其中的很多项目,例如把时间戳全部清零了,把owner/group也清零了,etc),只要有任何一个域值不一致,整个tar.gz文件就不一致,进而生成的hash值就不一致。

详细tar文件格式请参与:
https://www.gnu.org/software/tar/manual/html_node/Standard.html

另外注意不同的fabric版本,不同的go语言版本(tar库版本),甚至不同的安装方式(SDK,peer命令行安装)都可能导致产生的chaincode package不一样。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容