如何解析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里面淘菩,這樣當(dāng)chaincode死掉再重新instantiate的時候综芥,會驗證chaincode的package是否已經(jīng)發(fā)生過變化硬爆,對照從chaincode package中讀取的hash值是否和state db里面已經(jīng)存儲的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的內(nèi)容把其中的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文件的內(nèi)容,注意chaincode.tar.gz的文件格式八毯,是一個tar文件搓侄,也就是說包含原始代碼文件的meta信息,比如讀寫權(quán)限话速,owner/group讶踪,時間戳等等等信息(好在fabric在打包的時候重寫了其中的很多項目,例如把時間戳全部清零了泊交,把owner/group也清零了乳讥,etc)柱查,只要有任何一個域值不一致,整個tar.gz文件就不一致云石,進(jìn)而生成的hash值就不一致唉工。
詳細(xì)tar文件格式請參與:
https://www.gnu.org/software/tar/manual/html_node/Standard.html
另外注意不同的fabric版本,不同的go語言版本(tar庫版本)汹忠,甚至不同的安裝方式(SDK酵紫,peer命令行安裝)都可能導(dǎo)致產(chǎn)生的chaincode package不一樣。