Blockchain tutorial Fabric1.0 source code analysis chain code language platform

Keywords: Go Docker Java github Programming

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

Posted by vaaaska on Thu, 12 Dec 2019 09:20:28 -0800