1. 程式人生 > >區塊鏈教程Fabric1.0源代碼分析鏈碼 鏈碼語言平臺

區塊鏈教程Fabric1.0源代碼分析鏈碼 鏈碼語言平臺

pro tor append config 長度 emp time delet let

Fabric 1.0源代碼筆記 之 Chaincode(鏈碼) #platforms(鏈碼語言平臺)

1、platforms概述

platforms代碼集中在core/chaincode/platforms目錄下。

  • core/chaincode/platforms目錄,鏈碼的編寫語言平臺實現,如golang或java。
    ???? platforms.go,Platform接口定義,及platforms相關工具函數。
    ????
    util目錄,Docker相關工具函數。
    ???? java目錄,java語言平臺實現。
    ????
    golang目錄,golang語言平臺實現。
    ????

    2、Platform接口定義

type Platform interface {
????//驗證ChaincodeSpec
????ValidateSpec(spec *pb.ChaincodeSpec) error
????//驗證ChaincodeDeploymentSpec
????ValidateDeploymentSpec(spec *pb.ChaincodeDeploymentSpec) error
????//獲取部署Payload
????GetDeploymentPayload(spec *pb.ChaincodeSpec) ([]byte, error)
????//生成Dockerfile
????GenerateDockerfile(spec *pb.ChaincodeDeploymentSpec) (string, error)
????//生成DockerBuild
????GenerateDockerBuild(spec *pb.ChaincodeDeploymentSpec, tw *tar.Writer) error
}
//代碼在core/chaincode/platforms/platforms.go

3、platforms相關工具函數

3.1、platforms相關工具函數

//按鏈碼類型構造Platform接口實例,如golang.Platform{}
func Find(chaincodeType pb.ChaincodeSpec_Type) (Platform, error)
//調取platform.GetDeploymentPayload(spec),獲取部署Payload
func GetDeploymentPayload(spec *pb.ChaincodeSpec) ([]byte, error)
//優先獲取tls根證書,如無則獲取tls證書
func getPeerTLSCert() ([]byte, error)
//調取platform.GenerateDockerfile(cds),創建Dockerfile
func generateDockerfile(platform Platform, cds *pb.ChaincodeDeploymentSpec, tls bool) ([]byte, error)
//調取platform.GenerateDockerBuild(cds, tw),創建DockerBuild
func generateDockerBuild(platform Platform, cds *pb.ChaincodeDeploymentSpec, inputFiles InputFiles, tw *tar.Writer) error
//調取generateDockerfile(platform, cds, cert != nil)
func GenerateDockerBuild(cds *pb.ChaincodeDeploymentSpec) (io.Reader, error)
//代碼在core/chaincode/platforms/platforms.go

3.2、Docker相關工具函數

//contents+hash合並後再哈希
func ComputeHash(contents []byte, hash []byte) []byte 
//哈希目錄下文件並打包
func HashFilesInDir(rootDir string, dir string, hash []byte, tw *tar.Writer) ([]byte, error) 
//目錄是否存在
func IsCodeExist(tmppath string) error 
//編譯鏈碼
func DockerBuild(opts DockerBuildOptions) error 
//代碼在core/chaincode/platforms/util/utils.go

func DockerBuild(opts DockerBuildOptions) error代碼如下:

type DockerBuildOptions struct {
????Image        string
????Env          []string
????Cmd          string
????InputStream  io.Reader
????OutputStream io.Writer
}

func DockerBuild(opts DockerBuildOptions) error {
????client, err := cutil.NewDockerClient()
????if err != nil {
????????return fmt.Errorf("Error creating docker client: %s", err)
????}
????if opts.Image == "" {
????????//通用的本地編譯環境
????????opts.Image = cutil.GetDockerfileFromConfig("chaincode.builder")
????}

????//確認鏡像是否存在或從遠程拉取
????_, err = client.InspectImage(opts.Image)
????if err != nil {
????????err = client.PullImage(docker.PullImageOptions{Repository: opts.Image}, docker.AuthConfiguration{})
????}

????//創建一個暫時的容器
????container, err := client.CreateContainer(docker.CreateContainerOptions{
????????Config: &docker.Config{
????????????Image:        opts.Image,
????????????Env:          opts.Env,
????????????Cmd:          []string{"/bin/sh", "-c", opts.Cmd},
????????????AttachStdout: true,
????????????AttachStderr: true,
????????},
????})
????//刪除容器
????defer client.RemoveContainer(docker.RemoveContainerOptions{ID: container.ID})

????//上傳輸入
????err = client.UploadToContainer(container.ID, docker.UploadToContainerOptions{
????????Path:        "/chaincode/input",
????????InputStream: opts.InputStream,
????})

????stdout := bytes.NewBuffer(nil)
????_, err = client.AttachToContainerNonBlocking(docker.AttachToContainerOptions{
????????Container:    container.ID,
????????OutputStream: stdout,
????????ErrorStream:  stdout,
????????Logs:         true,
????????Stdout:       true,
????????Stderr:       true,
????????Stream:       true,
????})

????//啟動容器
????err = client.StartContainer(container.ID, nil)
????//等待容器返回
????retval, err := client.WaitContainer(container.ID)
????//獲取容器輸出
????err = client.DownloadFromContainer(container.ID, docker.DownloadFromContainerOptions{
????????Path:         "/chaincode/output/.",
????????OutputStream: opts.OutputStream,
????})
????return nil
}
//代碼在core/chaincode/platforms/util/utils.go

4、golang語言平臺實現

4.1、golang.Platform結構體定義及方法

Platform接口golang語言平臺實現,即golang.Platform結構體定義及方法。

type Platform struct {
}

//驗證ChaincodeSpec,即檢查spec.ChaincodeId.Path是否存在
func (goPlatform *Platform) ValidateSpec(spec *pb.ChaincodeSpec) error
//驗證ChaincodeDeploymentSpec,即檢查cds.CodePackage(tar.gz文件)解壓後文件合法性
func (goPlatform *Platform) ValidateDeploymentSpec(cds *pb.ChaincodeDeploymentSpec) error
//獲取部署Payload,即將鏈碼目錄下文件及導入包所依賴的外部包目錄下文件達成tar.gz包
func (goPlatform *Platform) GetDeploymentPayload(spec *pb.ChaincodeSpec) ([]byte, error)
func (goPlatform *Platform) GenerateDockerfile(cds *pb.ChaincodeDeploymentSpec) (string, error)
func (goPlatform *Platform) GenerateDockerBuild(cds *pb.ChaincodeDeploymentSpec, tw *tar.Writer) error

func pathExists(path string) (bool, error) //路徑是否存在
func decodeUrl(spec *pb.ChaincodeSpec) (string, error) //spec.ChaincodeId.Path去掉http://或https://
func getGopath() (string, error) //獲取GOPATH
func filter(vs []string, f func(string) bool) []string //按func(string) bool過濾[]string
func vendorDependencies(pkg string, files Sources) //重新映射依賴關系
//代碼在core/chaincode/platforms/golang/platform.go

4.1.1 func (goPlatform Platform) GetDeploymentPayload(spec pb.ChaincodeSpec) ([]byte, error)

func (goPlatform *Platform) GetDeploymentPayload(spec *pb.ChaincodeSpec) ([]byte, error) {
????var err error
????code, err := getCode(spec) //獲取代碼,即構造CodeDescriptor,Gopath為代碼真實路徑,Pkg為代碼相對路徑
????env, err := getGoEnv()
????gopaths := splitEnvPaths(env["GOPATH"]) //GOPATH
????goroots := splitEnvPaths(env["GOROOT"]) //GOROOT,go安裝路徑
????gopaths[code.Gopath] = true //鏈碼真實路徑
????env["GOPATH"] = flattenEnvPaths(gopaths) //GOPATH、GOROOT、鏈碼真實路徑重新拼合為新GOPATH
????
????imports, err := listImports(env, code.Pkg) //獲取導入包列表
????var provided = map[string]bool{ //如下兩個包為ccenv已自帶,可刪除
????????"github.com/hyperledger/fabric/core/chaincode/shim": true,
????????"github.com/hyperledger/fabric/protos/peer":         true,
????}
????
????imports = filter(imports, func(pkg string) bool {
????????if _, ok := provided[pkg]; ok == true { //從導入包中刪除ccenv已自帶的包
????????????return false
????????}
????????for goroot := range goroots { //刪除goroot中自帶的包
????????????fqp := filepath.Join(goroot, "src", pkg)
????????????exists, err := pathExists(fqp)
????????????if err == nil && exists {
????????????????return false
????????????}
????????}???
????????return true
????})
????
????deps := make(map[string]bool)
????for _, pkg := range imports {
????????transitives, err := listDeps(env, pkg) //列出所有導入包的依賴包
????????deps[pkg] = true
????????for _, dep := range transitives {
????????????deps[dep] = true
????????}
????}
????delete(deps, "") //刪除空
????
????fileMap, err := findSource(code.Gopath, code.Pkg) //遍歷鏈碼路徑下文件
????for dep := range deps {
????????for gopath := range gopaths {
????????????fqp := filepath.Join(gopath, "src", dep)
????????????exists, err := pathExists(fqp)
????????????if err == nil && exists {
????????????????files, err := findSource(gopath, dep) //遍歷依賴包下文件
????????????????for _, file := range files {
????????????????????fileMap[file.Name] = file
????????????????}
????????????????
????????????}
????????}
????}
????
????files := make(Sources, 0) //數組
????for _, file := range fileMap {
????????files = append(files, file)
????}
????vendorDependencies(code.Pkg, files) //重新映射依賴關系
????sort.Sort(files)
????
????payload := bytes.NewBuffer(nil)
????gw := gzip.NewWriter(payload)
????tw := tar.NewWriter(gw)
????for _, file := range files {
????????err = cutil.WriteFileToPackage(file.Path, file.Name, tw) //將文件寫入壓縮包中
????}
????tw.Close()
????gw.Close()
????return payload.Bytes(), nil
}
//代碼在core/chaincode/platforms/golang/platform.go

4.1.2、func (goPlatform Platform) GenerateDockerfile(cds pb.ChaincodeDeploymentSpec) (string, error)

func (goPlatform *Platform) GenerateDockerfile(cds *pb.ChaincodeDeploymentSpec) (string, error) {
????var buf []string
????//go語言鏈碼部署依賴的基礎鏡像
????buf = append(buf, "FROM "+cutil.GetDockerfileFromConfig("chaincode.golang.runtime"))
????//binpackage.tar添加到/usr/local/bin目錄下
????buf = append(buf, "ADD binpackage.tar /usr/local/bin")
????dockerFileContents := strings.Join(buf, "\n")
????return dockerFileContents, nil
}
//代碼在core/chaincode/platforms/golang/platform.go

4.1.3、func (goPlatform Platform) GenerateDockerBuild(cds pb.ChaincodeDeploymentSpec, tw *tar.Writer) error

func (goPlatform *Platform) GenerateDockerBuild(cds *pb.ChaincodeDeploymentSpec, tw *tar.Writer) error {
????spec := cds.ChaincodeSpec
????pkgname, err := decodeUrl(spec)
????const ldflags = "-linkmode external -extldflags ‘-static‘"

????codepackage := bytes.NewReader(cds.CodePackage)
????binpackage := bytes.NewBuffer(nil)
????//編譯鏈碼
????err = util.DockerBuild(util.DockerBuildOptions{
????????Cmd:          fmt.Sprintf("GOPATH=/chaincode/input:$GOPATH go build -ldflags \"%s\" -o /chaincode/output/chaincode %s", ldflags, pkgname),
????????InputStream:  codepackage,
????????OutputStream: binpackage,
????})
????return cutil.WriteBytesToPackage("binpackage.tar", binpackage.Bytes(), tw)
}
//代碼在core/chaincode/platforms/golang/platform.go

4.2、env相關函數

type Env map[string]string
type Paths map[string]bool

func getEnv() Env //獲取環境變量,寫入map[string]string
func getGoEnv() (Env, error) //執行go env獲取go環境變量,寫入map[string]string
func flattenEnv(env Env) []string //拼合env,形式k=v,寫入[]string
func splitEnvPaths(value string) Paths //分割多個路徑字符串,linux下按:分割
func flattenEnvPaths(paths Paths) string //拼合多個路徑字符串,以:分隔
//代碼在core/chaincode/platforms/golang/env.go

4.3、list相關函數

//執行命令pgm,支持設置timeout,timeout後將kill進程
func runProgram(env Env, timeout time.Duration, pgm string, args ...string) ([]byte, error) 
//執行go list -f 規則 鏈碼路徑,獲取導入包列表或依賴包列表
func list(env Env, template, pkg string) ([]string, error) 
//執行go list -f "{{ join .Deps \"\\n\"}}" 鏈碼路徑,獲取依賴包列表
func listDeps(env Env, pkg string) ([]string, error) 
//執行go list -f "{{ join .Imports \"\\n\"}}" 鏈碼路徑,獲取導入包列表
func listImports(env Env, pkg string) ([]string, error) 
//代碼在core/chaincode/platforms/golang/list.go

4.4、Sources類型及方法

type Sources []SourceDescriptor
type SourceMap map[string]SourceDescriptor

type SourceDescriptor struct {
????Name, Path string
????Info       os.FileInfo
}

type CodeDescriptor struct {
????Gopath, Pkg string
????Cleanup     func()
}
//代碼在core/chaincode/platforms/golang/package.go

涉及方法如下:

//獲取代碼真實路徑
func getCodeFromFS(path string) (codegopath string, err error) 
//獲取代碼,即構造CodeDescriptor,Gopath為代碼真實路徑,Pkg為代碼相對路徑
func getCode(spec *pb.ChaincodeSpec) (*CodeDescriptor, error)
//數組長度 
func (s Sources) Len() int
//交換數組i,j內容
func (s Sources) Swap(i, j int) 
//比較i,j的名稱
func (s Sources) Less(i, j int) bool 
//遍歷目錄下文件,填充type SourceMap map[string]SourceDescriptor
func findSource(gopath, pkg string) (SourceMap, error) 
//代碼在core/chaincode/platforms/golang/package.go

區塊鏈教程Fabric1.0源代碼分析鏈碼 鏈碼語言平臺