Fabric 1.0原始碼分析(4)Chaincode(鏈碼)#platforms(鏈碼語言平臺)
# 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介面定義
```go
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相關工具函式
```go
//按鏈碼型別構造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相關工具函式
```go
//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程式碼如下:
```go
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結構體定義及方法。
```go
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)
```go
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)
```go
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
```go
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相關函式
```go
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相關函式
```go
//執行命令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型別及方法
```go
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
```
涉及方法如下:
```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
```
網址:http://www.qukuailianxueyuan.io/
欲領取造幣技術與全套虛擬機器資料
區塊鏈技術交流QQ群:756146052 備註:CSDN
尹成學院微信:備註:CSDN
網址:http://www.qukuailianxueyuan.io/
欲領取造幣技術與全套虛擬機器資料
區塊鏈技術交流QQ群:756146052 備註:CSDN
尹成學院微信:備註:CSDN
相關文章
- Fabric 1.0原始碼分析(3)Chaincode(鏈碼)原始碼AI
- Fabric 1.0原始碼分析(5)Chaincode(鏈碼)體系總結原始碼AI
- Fabric 1.0原始碼分析(42)scc(系統鏈碼)原始碼
- Fabric 1.0原始碼分析(29) Orderer #multichain(多鏈支援包)原始碼AI
- Fabric 1.0原始碼分析(42)scc(系統鏈碼) #cscc(通道相關)原始碼
- Fabric 1.0原始碼分析(34) Peer #peer chaincode命令及子命令實現原始碼AI
- Fabric 1.0原始碼分析(25) Orderer原始碼
- Fabric 1.0原始碼分析(31) Peer原始碼
- 區塊鏈教程Fabric1.0原始碼分析policy(背書策略)-兄弟連區塊鏈區塊鏈原始碼
- Fabric 1.0原始碼分析(1)BCCSP(區塊鏈加密服務提供者)原始碼區塊鏈加密
- Fabric 1.0原始碼分析(40) Proposal(提案)原始碼
- Fabric 1.0原始碼分析(47)Fabric 1.0.4 go程式碼量統計原始碼Go
- Fabric 1.0原始碼分析(18) Ledger(賬本)原始碼
- Fabric 1.0原始碼分析(43) Tx(Transaction 交易)原始碼
- Fabric基礎架構原理(4):鏈碼架構
- Fabric 1.0原始碼分析(13)events(事件服務)原始碼事件
- Fabric 1.0原始碼分析(26)Orderer #ledger(Orderer Ledger)原始碼
- Fabric 1.0原始碼分析(39) policy(背書策略)原始碼
- Fabric1.4原始碼解析:鏈碼例項化過程原始碼
- Fabric 1.0原始碼分析(15)gossip(流言演算法)原始碼Go演算法
- Fabric 1.0原始碼分析(23)LevelDB(KV資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(44)Tx #RWSet(讀寫集)原始碼
- 區塊鏈教程Fabric1.0原始碼分析流言演算法Gossip服務端一兄弟連區塊鏈教程區塊鏈原始碼演算法Go服務端
- 區塊鏈教程Fabric1.0原始碼分析流言演算法Gossip服務端二-兄弟連區塊鏈原始碼演算法Go服務端
- Fabric 1.0原始碼分析(14) flogging(Fabric日誌系統)原始碼
- Fabric 1.0原始碼分析(10)consenter(共識外掛)原始碼
- Fabric 1.0原始碼分析(35)Peer #EndorserServer(Endorser服務端)原始碼Server服務端
- Fabric 1.0原始碼分析(36) Peer #EndorserClient(Endorser客戶端)原始碼client客戶端
- Fabric 1.0原始碼分析(41)putils(protos/utils工具包)原始碼
- fabric 鏈碼安裝筆記筆記
- Hyperledger fabric 鏈碼篇GO(四)Go
- Linux原始碼分析工具鏈Linux原始碼
- Fabric 1.0原始碼分析(45)gRPC(Fabric中註冊的gRPC Service)原始碼RPC
- Fabric 1.0原始碼分析(2) blockfile(區塊檔案儲存)原始碼BloC
- Fabric 1.0原始碼分析(19) Ledger #statedb(狀態資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(21)Ledger #historydb(歷史資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(22)Ledger #blkstorage(block檔案儲存)原始碼BloC
- Fabric 1.0原始碼分析(32) Peer #peer node start命令實現原始碼