Go語言打造以太坊智能合約測試框架(level2)


傳送門: 柏鏈項目學院git



第二課 智能合約自動化編譯

前期內容回顧

以前咱們的介紹的是如何經過solc編譯智能合約,而且調用智能合約,本節咱們繼續實踐,將智能合約的代碼自動化編譯以及abi文件生成搞定。github

咱們須要掌握什麼技能呢?shell

  • go語言調用命令行
  • toml配置文件處理
  • awk工具的使用

go調用命令行

go調用命令行,咱們使用exec包bash

  • LookPath 能夠判斷一個可執行程序是否存在
  • Command 建立一個命令行
  • cmd.Run() 運行命令行,也可使用Start()模式,能夠去接收管道信息來獲得程序返回結果
  • 若是是一個shell腳本,那麼能夠用/bin/bash來啓動

toml配置文件處理

TOML的全稱是Tom's Obvious, Minimal Language,由於它的做者是GitHub聯合創始人Tom Preston-Werner。TOML 的目標是成爲一個極簡的配置文件格式。TOML 被設計成能夠無歧義地被映射爲哈希表,從而被多種語言解析。ide

toml學習教程
在使用的時候,記得要安裝toml第三方包。函數

go get -u github.com/BurntSushi/toml

以後能夠根據咱們的須要,來編寫配置文件,配置文件的目的仍然是爲了讓程序運行更靈活,而不該該成爲咱們的負擔!工具

awk工具使用

awk實際上是一個語言,unix平臺上處理文本的一種語言,其名稱得自於它的創始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首個字母。該語言的能力十分強大,能夠支持字符串處理,打印等操做,工程中對於文本處理要求比較高的環節多會使用awk進行操做。學習

awk功能舉例:ui

  1. factory.txt 是一個工廠內目前產品庫存狀況,若是數量低於75,須要從新下訂單,如何處理?
yekaideMacBook-Pro:awk yk$ cat factory.txt 
ProdA 70
ProdB 85
ProdC 74

示例以下:命令行

awk '{if ($2 < 75) printf("%s reorder\n",$0);if ($2 >= 75) print $0}' factory.txt
  1. 查看本系統中shell是bash的用戶名,並打印
cat /etc/passwd |grep bash|awk -F ":" '{print $1}'
  1. awk處理合約的go文件,將abi信息截取處理存儲到文本當中
awk '/const.+ABI = .+/{print substr($4,2,length($4)-2) }' pdbank.go > pdbank.abi

編寫自動編譯功能

main.go

package main

import (
    "fmt"
    "os"
)

func Usage() {
    fmt.Printf("%s 1  -- compiler code\n", os.Args[0])
    fmt.Printf("%s 2  -- build test code\n", os.Args[0])
}

func main() {
    if len(os.Args) < 2 {
        Usage()
        os.Exit(0)
    }
    if os.Args[1] == "1" {
        CompilerRun()
    } else if os.Args[1] == "1" {
        //build test code
    } else {
        Usage()
        os.Exit(0)
    }

}

接下來開始作填空題,也就是如何編譯,咱們先來實現。先編寫掃描目錄的代碼,獲取指定目錄的sol文件,而後自動化的造成編譯命令,送到命令行執行。

掃描指定目錄的sol文件

func CompilerRun() error {
    infos, err := ioutil.ReadDir("sol")
    if err != nil {
        fmt.Println("failed to readdir ", err)
        return err
    }
    for _, v := range infos {

        //後4位位.sol
        strNameRune := []rune(v.Name())
        strfix := string(strNameRune[len(strNameRune)-4:])
        if strfix == ".sol" && !v.IsDir() {
            fmt.Println(v.IsDir(), v.Name(), v.Size(), "ok")
            err = CompilerOnece("sol", v.Name(), "contracts")
            if err != nil {
                fmt.Println("call ompilerOnece err", err)
                break
            }
        }
    }
    return err
}

編譯函數

//編譯一個智能合約
func CompilerOnece(solPath, solName, targetPath string) error {

    //xxx.sol - > xxx.go
    goName := strings.Replace(solName, ".sol", ".go", -1)

    cmd := exec.Command("abigen", "-sol", solPath+"/"+solName, "-pkg", targetPath, "-out", targetPath+"/"+goName)
    return cmd.Run()
}

構建abi函數,咱們須要先用awk實現一個shell腳本,用來處理go文件的abi信息。

func BuildAbi(goCodeName string) error {
    abiName := strings.Replace(goCodeName, ".go", ".abi", -1)
    cmd := exec.Command("/bin/bash", "abi.sh", goCodeName, abiName)
    err := cmd.Run()
    fmt.Println("run BuildAbi ok!!", err)
    return nil
}

abi.sh

filename=$1
targetfile=$2
awk '/const.+ABI = .+/{print substr($4,2,length($4)-2) }' $filename > $targetfile

統一調用處理

func ParseRun() {
    solfiles, err := ParseDir("sol")
    fmt.Println(solfiles, err)
    for _, solfile := range solfiles {
        fmt.Println(solfile)
        codeName, err := Compiler(solfile, "sol", "contracts")
        if err != nil {
            fmt.Println("failed to complie code", err)
            return
        }
        err = BuildAbi(codeName)
        if err != nil {
            fmt.Println("failed to build abi", err)
            return
        }
    }
}

這樣咱們的基礎工做完成了,可是代碼不夠完美,咱們須要將部分寫死的變量用配置文件來設置,因此再加入toml處理配置文件的部分。

添加config.tomls

[version]
auth = "yekai"
company = "pdj"
buildday = "2019-01-01"
ver = "1.0.0" # 版本

[common]
solidityPath = "sol"
goPath = "contracts"
abiSH = "./abi.sh"

添加config.go

package main

import (
    "log"

    "github.com/BurntSushi/toml"
)

type ServerConfig struct {
    Version VersionInfo
    Common  CommonInfo
}

type VersionInfo struct {
    Auth     string
    Company  string
    BuildDay string
    Ver      string
}

type CommonInfo struct {
    SolidityPath string //智能合約原路徑
    GoPath       string //輸出go代碼路徑
    AbiSH        string //處理abi的shell腳本路徑
}

var ServConf ServerConfig

func init() {
    getConfig()
}

func getConfig() {
    var servConf ServerConfig
    _, err := toml.DecodeFile("config.toml", &servConf)
    if err != nil {
        log.Panic("faild to decodefile ", err)
    }
    ServConf = servConf
    //fmt.Println(servConf)
}

接下來替換原來的代碼部分

package main

import (
    "fmt"
    "io/ioutil"
    "os/exec"
    "strings"
)

//編譯一個智能合約
func CompilerOnece(solPath, solName, targetPath string) error {

    //xxx.sol - > xxx.go
    goName := strings.Replace(solName, ".sol", ".go", -1)

    cmd := exec.Command("abigen", "-sol", solPath+"/"+solName, "-pkg", targetPath, "-out", targetPath+"/"+goName)
    return cmd.Run()
}

//構造abi
func BuildAbi(codePath string) error {
    //"contracts/pdbank.go"
    //xxx.sol - > xxx.abi
    abiName := strings.Replace(codePath, ".sol", ".abi", -1)
    goName := strings.Replace(codePath, ".sol", ".go", -1)

    cmd := exec.Command(ServConf.Common.AbiSH, goName, abiName)
    return cmd.Run()
}

//掃描目錄,得到所有的文件
func CompilerRun() error {
    infos, err := ioutil.ReadDir(ServConf.Common.SolidityPath)
    if err != nil {
        fmt.Println("failed to readdir ", err)
        return err
    }
    for _, v := range infos {

        //後4位位.sol
        strNameRune := []rune(v.Name())
        strfix := string(strNameRune[len(strNameRune)-4:])
        if strfix == ".sol" && !v.IsDir() {
            fmt.Println(v.IsDir(), v.Name(), v.Size(), "ok")
            err = CompilerOnece(ServConf.Common.SolidityPath, v.Name(), ServConf.Common.GoPath)
            if err != nil {
                fmt.Println("call ompilerOnece err", err)
                break
            }
            //建立abi
            err = BuildAbi(ServConf.Common.GoPath + "/" + v.Name())
            if err != nil {
                fmt.Println("call BuildAbi err", err)
                break
            }
        }
    }
    return err
}



相關文章
相關標籤/搜索