Chain code and platform of Fabric 1.0 source code Notes
1. Overview of platforms
The platforms code is centralized in the core/chaincode/platforms directory.
- core/chaincode/platforms directory, the programming language platform implementation of chain code, such as golang or java.
Platform.go, platform interface definition, and platform related tool functions.
util directory, Docker related tool functions.
Java directory, implemented by java language platform.
* golang directory, implemented by the golang language platform.
2. Platform interface definition
type Platform interface { //Verify ChaincodeSpec ValidateSpec(spec *pb.ChaincodeSpec) error //Verify ChaincodeDeploymentSpec ValidateDeploymentSpec(spec *pb.ChaincodeDeploymentSpec) error //Get deployment Payload GetDeploymentPayload(spec *pb.ChaincodeSpec) ([]byte, error) //Generate Dockerfile GenerateDockerfile(spec *pb.ChaincodeDeploymentSpec) (string, error) //Build DockerBuild GenerateDockerBuild(spec *pb.ChaincodeDeploymentSpec, tw *tar.Writer) error } //The code is in core/chaincode/platforms/platforms.go
3. Platform related tool functions
3.1. Platform related tool functions
//Construct platform interface instance according to chain code type, such as golang.Platform {} func Find(chaincodeType pb.ChaincodeSpec_Type) (Platform, error) //Call platform.GetDeploymentPayload(spec) to get deployment Payload func GetDeploymentPayload(spec *pb.ChaincodeSpec) ([]byte, error) //Take tls root certificate first, or take tls certificate if not func getPeerTLSCert() ([]byte, error) //Call platform.GenerateDockerfile(cds) to create a Dockerfile func generateDockerfile(platform Platform, cds *pb.ChaincodeDeploymentSpec, tls bool) ([]byte, error) //Call platform.GenerateDockerBuild(cds, tw) to create DockerBuild func generateDockerBuild(platform Platform, cds *pb.ChaincodeDeploymentSpec, inputFiles InputFiles, tw *tar.Writer) error //Call generatedockerfile (platform, CDs, cert! = Nil) func GenerateDockerBuild(cds *pb.ChaincodeDeploymentSpec) (io.Reader, error) //The code is in core/chaincode/platforms/platforms.go
3.2. Docker related tool functions
//contents+hash merge and then hash func ComputeHash(contents []byte, hash []byte) []byte //File in hash directory and package func HashFilesInDir(rootDir string, dir string, hash []byte, tw *tar.Writer) ([]byte, error) //Directory exists func IsCodeExist(tmppath string) error //Compile chain code func DockerBuild(opts DockerBuildOptions) error //The code is in core/chaincode/platforms/util/utils.go
The error code of func dockerbuild (opts dockerbuildoptions) is as follows:
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 == "" { //General local compilation environment opts.Image = cutil.GetDockerfileFromConfig("chaincode.builder") } //Confirm whether the image exists or is pulled remotely _, err = client.InspectImage(opts.Image) if err != nil { err = client.PullImage(docker.PullImageOptions{Repository: opts.Image}, docker.AuthConfiguration{}) } //Create a temporary container 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, }, }) //Delete container defer client.RemoveContainer(docker.RemoveContainerOptions{ID: container.ID}) //Upload input 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, }) //Starting container err = client.StartContainer(container.ID, nil) //Wait for container to return retval, err := client.WaitContainer(container.ID) //Get container output err = client.DownloadFromContainer(container.ID, docker.DownloadFromContainerOptions{ Path: "/chaincode/output/.", OutputStream: opts.OutputStream, }) return nil } //The code is in core/chaincode/platforms/util/utils.go
4. Implementation of golang language platform
4.1 definition and method of golang.Platform structure
Platform interface: the implementation of golang language platform, that is, the definition and method of golang.Platform structure.
type Platform struct { } //Verify ChaincodeSpec, that is, check if spec.ChaincodeId.Path exists func (goPlatform *Platform) ValidateSpec(spec *pb.ChaincodeSpec) error //Verify the ChaincodeDeploymentSpec, that is, check the validity of cds.CodePackage (tar.gz file) after decompression func (goPlatform *Platform) ValidateDeploymentSpec(cds *pb.ChaincodeDeploymentSpec) error //Obtain the deployment Payload, that is, the files under the chain code directory and the external package directory on which the imported package depends are generated into the tar.gz package 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) //Does the path exist func decodeUrl(spec *pb.ChaincodeSpec) (string, error) //spec.ChaincodeId.Path remove http: / / or https:// func getGopath() (string, error) //Get GOPATH func filter(vs []string, f func(string) bool) []string //Press func(string) bool to filter [] string func vendorDependencies(pkg string, files Sources) //Remapping dependencies //The code is in 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) //Get the code, that is, construct CodeDescriptor, Gopath is the real path of the code, and Pkg is the relative path of the code env, err := getGoEnv() gopaths := splitEnvPaths(env["GOPATH"]) //GOPATH goroots := splitEnvPaths(env["GOROOT"]) //GOROOT, go installation path gopaths[code.Gopath] = true //Chain code real path env["GOPATH"] = flattenEnvPaths(gopaths) //GOPATH, GOROOT and chain code real path are reassembled into new GOPATH imports, err := listImports(env, code.Pkg) //Get the list of imported packages var provided = map[string]bool{ //The following two packages are included in ccenv and can be deleted "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 { //Remove the package that comes with ccenv from the import package return false } for goroot := range goroots { //Delete the package in 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) //List dependent packages for all imported packages deps[pkg] = true for _, dep := range transitives { deps[dep] = true } } delete(deps, "") //Delete space fileMap, err := findSource(code.Gopath, code.Pkg) //File under traversal chain code path 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) //Traverse files under dependent packages for _, file := range files { fileMap[file.Name] = file } } } } files := make(Sources, 0) //array for _, file := range fileMap { files = append(files, file) } vendorDependencies(code.Pkg, files) //Remapping dependencies 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) //Write files to a compressed package } tw.Close() gw.Close() return payload.Bytes(), nil } //The code is in 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 language chain code deployment depends on the basic image buf = append(buf, "FROM "+cutil.GetDockerfileFromConfig("chaincode.golang.runtime")) //Add binpackage.tar to / usr/local/bin directory buf = append(buf, "ADD binpackage.tar /usr/local/bin") dockerFileContents := strings.Join(buf, "\n") return dockerFileContents, nil } //The code is in 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) //Compile chain code 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) } //The code is in core/chaincode/platforms/golang/platform.go
4.2 env correlation function
type Env map[string]string type Paths map[string]bool func getEnv() Env //Get environment variable and write map[string]string func getGoEnv() (Env, error) //Execute go env to get go environment variable and write map[string]string func flattenEnv(env Env) []string //Splice env, form k=v, write [] string func splitEnvPaths(value string) Paths //Split multiple path strings, linux press: split func flattenEnvPaths(paths Paths) string //Concatenate multiple path strings to: separate //Code in core/chaincode/platforms/golang/env.go
4.3 list related functions
//Execute the command pgm, support setting timeout, and kill the process after timeout func runProgram(env Env, timeout time.Duration, pgm string, args ...string) ([]byte, error) //Execute go list -f rule chain code path to get the list of imported packages or dependent packages func list(env Env, template, pkg string) ([]string, error) //Execute the go list - F "{join. DEPs \" \ \ n \ "}}" chain code path to get the list of dependent packages func listDeps(env Env, pkg string) ([]string, error) //Execute go list - F "{join. Imports \" \ \ n \ "}}" chain code path to get the list of imported packages func listImports(env Env, pkg string) ([]string, error) //The code is in core/chaincode/platforms/golang/list.go
4.4 types and methods of 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() } //The code is in core/chaincode/platforms/golang/package.go
The methods involved are as follows:
//Get code real path func getCodeFromFS(path string) (codegopath string, err error) //Get the code, that is, construct CodeDescriptor, Gopath is the real path of the code, and Pkg is the relative path of the code func getCode(spec *pb.ChaincodeSpec) (*CodeDescriptor, error) //Array length func (s Sources) Len() int //Swap array i, j contents func (s Sources) Swap(i, j int) //Compare the names of i, j func (s Sources) Less(i, j int) bool //Traverse the files in the directory and fill in the type SourceMap map[string]SourceDescriptor func findSource(gopath, pkg string) (SourceMap, error) //The code is in core/chaincode/platforms/golang/package.go