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


傳送門: 柏鏈項目學院html



第三課 智能合約自動化測試

以前課程回顧

咱們以前介紹了go語言調用exec處理命令行,介紹了toml配置文件的處理,以及awk處理文本文件得到ABI信息。咱們的代碼算是完成了從智能合約到go語言的自動編譯,同時也能夠自動提取到ABI信息。git

具體能夠參考:github

第一課 go語言與智能合約調用的前因後果編程

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

本節主要工做

  • go語言模版編程
  • 目標代碼生成
  • 目標代碼的調用代碼生成
  • 配置文件的自動化生成

原有代碼優化

想要自動化生成測試代碼,首先你要知道目標代碼長什麼樣兒,也就是以前咱們寫的部署合約,調用合約的代碼。仔細思考,其實會發現分了兩層內容,第一層內容就是針對每個合約函數都要有一個調用函數生成,假設合約函數爲X,咱們把調用該函數的函數叫CallX,咱們想要完成測試,又須要在main函數內能找到調用CallX的入口。先一步一步來,先完成一個小目標,那就是生成這些個CallX,
再來梳理一下咱們以前調用合約的代碼。app

const keydata = `{"address":"791443d21a76e16cc510b6b1684344d2a5ce751c","crypto":{"cipher":"aes-128-ctr","ciphertext":"bbccbf9deb8c907d9f245767fffb57880c4cfd265dde9372d7278a8e963043bd","cipherparams":{"iv":"95e8f925fe0f3460f0ca3ccebc481b14"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"2036300fae07d954a10e70a2f87876fa198b310dc16a72f3fab3265978e7d798"},"mac":"009ce0ecd11d5f563d2f7bd41eb957f1ca3b517f31653dd18fac20e77b7feb5c"},"id":"7f42641c-580a-4398-b00c-74ef2710bcae","version":3}`

func CallDeploy() error {
    //連接到以太坊,cli就是backend
    cli, err := ethclient.Dial("http://localhost:8545")
    if err != nil {
        fmt.Println("failed to dial ", err)
        return err
    }
    //建立身份,須要私鑰= pass+keystore文件
    auth, err := bind.NewTransactor(strings.NewReader(keydata), "123")
    if err != nil {
        fmt.Println("failed to NewTransactor auth", err)
        return err
    }
    auth.GasLimit = 400000
    addr, ts, pd, err := DeployPdbank(auth, cli, "yekai233")
    if err != nil {
        fmt.Println("failed to DeployPdbank", err)
        return err
    }
    bkname, _ := pd.BankName(nil)
    fmt.Println("addr=", addr.Hex(), "bkname=", bkname, "hash=", ts.Hash().Hex())
    return err
}

對於keydata來講,咱們不能寫死了,須要從keystore文件讀取這部分數據。http://localhost:8545這樣的鏈接地址最好經過配置文件來搞定,但思路要清楚的是,咱們這個代碼是未來要執行的,這個代碼它須要一個本身的配置文件,並不是是咱們當前工程的配置文件,直白一點的說就是咱們須要給將來的代碼寶寶們自動的生成一個配置文件。記得有這麼回事兒,咱們先無論這一點,先以能完成任務爲前提框架

咱們仍是來單獨造成一個函數,用於專門造成簽名,它須要讀取keystore文件,這個文件能夠根據對應的帳戶地址到以太坊keystore目錄獲得,這樣就容易多了!ide

//設置簽名
func MakeAuth(addr, pass string) (*bind.TransactOpts, error) {
    keystorePath  :=  "{{.Keydir}}"
    fileName, err := GetFileName(string([]rune(addr)[2:]), keystorePath)
    if err != nil {
        fmt.Println("failed to GetFileName", err)
        return nil, err
    }

    file, err := os.Open(keystorePath + "/" + fileName)
    if err != nil {
        fmt.Println("failed to open file ", err)
        return nil, err
    }
    auth, err := bind.NewTransactor(file, pass)
    if err != nil {
        fmt.Println("failed to NewTransactor  ", err)
        return nil, err
    }
    return auth, err
}
func GetFileName(address, dirname string) (string, error) {

    data, err := ioutil.ReadDir(dirname)
    if err != nil {
        fmt.Println("read dir err", err)
        return "", err
    }
    for _, v := range data {
        if strings.Index(v.Name(), address) > 0 {
            //表明找到文件
            return v.Name(), nil
        }
    }

    return "", nil
}

因而,咱們將原來寫的測試代碼進行完善一下!函數

此外,鏈接這裏也規範一下,使用init函數來搞定!測試

var testclient *ethclient.Client

func init() {
    cli, err := CreateCli("http://localhost:8545")
    if err != nil {
        log.Panic("failed to connect to eth", err)
    }
    testclient = cli
}
func CallBankName() (error) {
    instance, err := NewPdbank(common.HexToAddress("0xD55E88D9156355C584982Db2C96dD1C2c63788C2"), testclient)
    if err != nil {
        fmt.Println("failed to get contract instance", err)
        return err
    }
    data,err := instance.BankName(nil)
    if err != nil {
        fmt.Println("failed to get Balances", err)
        return err
    }
    fmt.Println(data,err)
    return nil
}


func CallWithdraw(addr, pass string) (*types.Transaction, error) {

    instance, err := NewPdbank(common.HexToAddress("0xD55E88D9156355C584982Db2C96dD1C2c63788C2"), testclient)
    if err != nil {
        fmt.Println("failed to get contract instance", err)
        return nil, err
    }
    auth, err := MakeAuth(addr, pass)
    if err != nil {
        fmt.Println("failed to makeAuth", err)
        return nil, err
    }
    auth.Value = big.NewInt(0)
    ts,err := instance.Withdraw(auth,big.NewInt(10000))
    if err != nil {
        fmt.Println("failed to call ", err)
        return nil, err
    }
    fmt.Println(ts.ChainId(), ts.Hash().Hex(), ts.Nonce())
    return ts , err
}

這樣代碼看上去舒服多了,接下來考慮目標代碼的生成辦法。

go語言與模版編程

go語言模版編程須要用到template包,go語言的模版呢實際上也提供了兩大類模版處理。一類是文本的,一類是html的。鑑於咱們的目標代碼是go語言,因此本次使用基於文本的模版處理。

下面是一個最簡單的模版處理的例子,{{.Count}}和{{.Material}}至關因而這個模版要填的兩個空。

type Inventory struct {
    Material string
    Count    uint
}
sweaters := Inventory{"wool", 17}
tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
if err != nil { panic(err) }
err = tmpl.Execute(os.Stdout, sweaters)
if err != nil { panic(err) }
  • New 方法建立一個模版
  • Parse 是指定模版的內容
  • Execute 是執行,兩個參數分別是寫入的方向以及模版實例化的內容

一個複雜一些的例子

// Define a template.
const letter = `
Dear {{.Name}},
{{if .Attended}}
It was a pleasure to see you at the wedding.{{else}}
It is a shame you couldn't make it to the wedding.{{end}}
{{with .Gift}}Thank you for the lovely {{.}}.
{{end}}
Best wishes,
Josie
`

// Prepare some data to insert into the template.
type Recipient struct {
    Name, Gift string
    Attended   bool
}
var recipients = []Recipient{
    {"Aunt Mildred", "bone china tea set", true},
    {"Uncle John", "moleskin pants", false},
    {"Cousin Rodney", "", false},
}

// Create a new template and parse the letter into it.
t := template.Must(template.New("letter").Parse(letter))

// Execute the template for each recipient.
for _, r := range recipients {
    err := t.Execute(os.Stdout, r)
    if err != nil {
        log.Println("executing template:", err)
    }
}

咱們能夠發現一些特色:

  • {{.Name}}這樣的pipeline(官方學名)必定能在結構體中找到這個名字
  • 可使用if和with作流程處理

因而把咱們的目標代碼,能夠製做成定製化的模版!

建立一個用於存放模版定義的文件,template_code.go

package templates

const Main_tmpl = `package main

import (
    "fmt"
    "log"
    "os"

    "gosol/contracts"
    "io/ioutil"
    "math/big"
    "strings"

    "github.com/ethereum/go-ethereum/accounts/abi/bind"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/ethclient"
)

var testclient *ethclient.Client

func init() {
    cli, err := CreateCli("{{.Connstr}}")
    if err != nil {
        log.Panic("failed to connect to eth", err)
    }
    testclient = cli
}

func GetFileName(address, dirname string) (string, error) {

    data, err := ioutil.ReadDir(dirname)
    if err != nil {
        fmt.Println("read dir err", err)
        return "", err
    }
    for _, v := range data {
        if strings.Index(v.Name(), address) > 0 {
            //表明找到文件
            return v.Name(), nil
        }
    }

    return "", nil
}

//建立連接
func CreateCli(connstr string) (*ethclient.Client, error) {
    cli, err := ethclient.Dial(connstr)
    if err != nil {
        fmt.Println("failed to dial provide", err)
        return nil, err
    }
    return cli, err
}

//設置簽名
func MakeAuth(addr, pass string) (*bind.TransactOpts, error) {
    keystorePath  :=  "{{.Keydir}}"
    fileName, err := GetFileName(string([]rune(addr)[2:]), keystorePath)
    if err != nil {
        fmt.Println("failed to GetFileName", err)
        return nil, err
    }

    file, err := os.Open(keystorePath + "/" + fileName)
    if err != nil {
        fmt.Println("failed to open file ", err)
        return nil, err
    }
    auth, err := bind.NewTransactor(file, pass)
    if err != nil {
        fmt.Println("failed to NewTransactor  ", err)
        return nil, err
    }
    return auth, err
}
`

const Deploy_sol_tmpl = `
func Deploy{{.ContractName}}() (common.Address, error) {
    auth, err := MakeAuth("{{.FromAddr}}", "{{.Pass}}")
    if err != nil {
        fmt.Println("failed to makeAuth", err)
        return common.HexToAddress(""), err
    }

    //common.Address, *types.Transaction, *Pdbank, error
    contractaddr, ts, _, err := contracts.{{.CallFunc}}
    if err != nil {
        fmt.Println("failed to deloy ",err)
        return common.HexToAddress(""), err
    }
    fmt.Println(ts.ChainId(), ts.Hash().Hex(), ts.Nonce())
    fmt.Println(contractaddr.Hex())
    return contractaddr, err
}

`

先搞定部署合約代碼自動生成部分!

//1. 寫到哪
    outfile, err := os.OpenFile("build/solcall.go", os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
    if err != nil {
        fmt.Println("failed to open file", err)
        return err
    }
    defer outfile.Close()
    //2. 寫什麼
    _, err = outfile.WriteString(test_main_temp)
    if err != nil {
        fmt.Println("failed to write ", err)
        return err
    }
    // 讀取abi文件信息
    abiInfos, err := readAbi("contracts/pdbank.abi")
    if err != nil {
        fmt.Println("failed to read abi", err)
        return err
    }
    //fmt.Println(infos)

    //3. 寫入部署合約代碼
    //定義部署模版
    deploy_temp, err := template.New("deploy").Parse(test_deploy_temp)
    if err != nil {
        fmt.Println("failed to template deploy", err)
        return err
    }
    var deploy_data DeployContractParams
    deploy_data.DeployName = "DeployPdbank"
    
    for _, v := range abiInfos {
        v.Name = strings.Title(v.Name) //標題優化,首字母大寫, hello world - > Hello World
        if v.Type == "constructor" {
            // 若是是構造函數-部署函數
            deploy_data.DeployParams = "(auth,testClient"
            for _, vv := range v.Inputs {
                //須要根據輸入數據類型來判斷如何處理:string,address,uint256
                if vv.Type == "address" {
                    deploy_data.DeployParams += ",common.HexToAddress(\"0xD55E88D9156355C584982Db2C96dD1C2c63788C2\")"
                } else if vv.Type == "uint256" {
                    deploy_data.DeployParams += ",big.NewInt(1000)"
                } else if vv.Type == "string" {
                    deploy_data.DeployParams += ",\"yekai\""
                }

            }
            deploy_data.DeployParams += ")"
            //模版的執行
            err = deploy_temp.Execute(outfile, &deploy_data)
            if err != nil {
                fmt.Println("failed to template Execute ", err)
                return err
            }
        }
    }

接下來的代碼仍是相同的套路。
完整代碼以下;

//temp_org.go
package templates

const test_main_temp = `
package main

import (
    "fmt"
    "gosolkit/contracts"
    "io/ioutil"
    "log"
    "math/big"
    "os"
    "strings"

    "github.com/ethereum/go-ethereum/accounts/abi/bind"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

var testClient *ethclient.Client

func init() {
    cli, err := Connect("http://localhost:8545")
    if err != nil {
        log.Fatalln("failed to connect to eth", err)
    }
    testClient = cli
}

func Connect(connstr string) (*ethclient.Client, error) {
    return ethclient.Dial(connstr)
}

//簽名函數
func MakeAuth(addr, pass string) (*bind.TransactOpts, error) {
    //1. 根據addr找到keystore目錄下的文件
    keyDir := "/Users/yk/ethdev/data/keystore"
    infos, err := ioutil.ReadDir(keyDir)
    if err != nil {
        fmt.Println("failed to readdir", err)
        return nil, err
    }
    //UTC--2019-03-16T13-00-48.032030904Z--791443d21a76e16cc510b6b1684344d2a5ce751c
    //0x791443d21a76e16cc510b6b1684344d2a5ce751c
    strAddr := ([]rune(addr))[2:]
    for _, v := range infos {
        strVname := []rune(v.Name())
        if len(strVname) > len(strAddr) {
            strVname2 := strVname[len(strVname)-len(strAddr):]
            if strings.EqualFold(string(strAddr), string(strVname2)) {
                //找到了匹配的文件
                //fmt.Println(addr, v.Name())
                //2. 作簽名
                reader, err := os.Open(keyDir + "/" + v.Name())
                if err != nil {
                    fmt.Println("failed to open file", err)
                    return nil, err
                }
                defer reader.Close()
                auth, err := bind.NewTransactor(reader, pass)
                if err != nil {
                    fmt.Println("failed to NewTransactor auth", err)
                    return nil, err
                }
                return auth, err
            }
        }
    }

    return nil, nil
}
`
const test_deploy_temp = `
func CallDeploy() error {
    //建立身份,須要私鑰= pass+keystore文件
    auth, err := MakeAuth("0x791443d21a76e16cc510b6b1684344d2a5ce751c", "123")
    if err != nil {
        fmt.Println("failed to MakeAuth auth", err)
        return err
    }
    addr, ts, _, err := contracts.{{.DeployName}}{{.DeployParams}}
    if err != nil {
        fmt.Println("failed to DeployPdbank", err)
        return err
    }

    fmt.Println("addr=", addr.Hex(), "hash=", ts.Hash().Hex())
    return err
}
`

const test_nogas_temp = `
func Call{{.FuncName}}() error {

    //使用以前部署獲得的合約地址
    instance, err := contracts.{{.NewContractName}}(common.HexToAddress("0xD55E88D9156355C584982Db2C96dD1C2c63788C2"), testClient)
    if err != nil {
        fmt.Println("failed to instance contract", err)
        return err
    }
    //調用合約函數
    {{.OutParams}} := instance.{{.FuncName}}{{.InputParams}}
    fmt.Println({{.OutParams}})

    return err
}
`

const test_gas_temp = `
func Call{{.FuncName}}() error {

    //2. 構造函數入口 - 合約對象
    instance, err := contracts.{{.NewContractName}}(common.HexToAddress("0xD55E88D9156355C584982Db2C96dD1C2c63788C2"), testClient)
    if err != nil {
        fmt.Println("failed to contract instance", err)
        return err
    }
    //3. 設置簽名
    auth, err := MakeAuth("0x791443d21a76e16cc510b6b1684344d2a5ce751c", "123")
    if err != nil {
        fmt.Println("failed to MakeAuth auth", err)
        return err
    }
    //4. 函數調用
    auth.Value = big.NewInt(0)
    ts, err := instance.{{.FuncName}}{{.InputParams}}
    if err != nil {
        fmt.Println("failed to Deposit ", err)
        return err
    }
    fmt.Println(ts.Hash().Hex())
    return err
}
`
//temp_main.go
package templates

const test_run_main_temp = `
package main
import (
    "fmt"
    "os"
)
var funcNames = []string{%s}
`

//1. 提供一個命令行幫助
const test_build_main_temp = `
func Usage() {
    fmt.Printf("%s 1 -- deploy\n", os.Args[0])
    num := 2
    for _, v := range funcNames {
        fmt.Printf("%s %d -- %s\n", os.Args[0], num, v)
        num++
    }
}
func main() {
    if len(os.Args) < 2 {
        Usage()
        os.Exit(0)
    }
    if os.Args[1] == "1" {
        CallDeploy()
    }{{range.}} else if os.Args[1] == "{{.Num}}" {
        Call{{.FuncName}}()
    } {{end}}
}
`
//temp_impl.go
package templates

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "os"
    "strings"
    "text/template"
)

type DeployContractParams struct {
    DeployName   string
    DeployParams string
}

//無gas函數調用
type FuncNoGasParams struct {
    FuncName        string
    NewContractName string
    OutParams       string
    InputParams     string
}

//有gas函數調用
type FuncGasParams struct {
    FuncName        string
    NewContractName string
    InputParams     string
}

type InputsOutPuts struct {
    Name string
    Type string
}

type FuncInfo struct {
    FuncName string
    Num      int
}

type AbiInfo struct {
    Constant        bool
    Inputs          []InputsOutPuts
    Name            string
    Outputs         []InputsOutPuts
    Payable         bool
    StateMutability string
    Type            string
}

func readAbi(abifile string) ([]AbiInfo, error) {
    file, err := os.Open(abifile)
    if err != nil {
        fmt.Println("failed to open file ", err)
        return nil, err
    }
    data, err := ioutil.ReadAll(file)
    if err != nil {
        fmt.Println("failed to read abi", err)
        return nil, err
    }
    var abiInfos []AbiInfo
    strdata := strings.Replace(string(data), "\\", "", -1)
    err = json.Unmarshal([]byte(strdata), &abiInfos)
    if err != nil {
        fmt.Println("failed to Unmarshal abi", err)
        return nil, err
    }
    return abiInfos, err
}

func Impl_run_code() error {
    //1. 寫到哪
    outfile, err := os.OpenFile("build/solcall.go", os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
    if err != nil {
        fmt.Println("failed to open file", err)
        return err
    }
    defer outfile.Close()
    //2. 寫什麼
    _, err = outfile.WriteString(test_main_temp)
    if err != nil {
        fmt.Println("failed to write ", err)
        return err
    }
    // 讀取abi文件信息
    abiInfos, err := readAbi("contracts/pdbank.abi")
    if err != nil {
        fmt.Println("failed to read abi", err)
        return err
    }
    //fmt.Println(infos)

    //3. 寫入部署合約代碼
    //定義部署模版
    deploy_temp, err := template.New("deploy").Parse(test_deploy_temp)
    if err != nil {
        fmt.Println("failed to template deploy", err)
        return err
    }
    var deploy_data DeployContractParams
    deploy_data.DeployName = "DeployPdbank"

    //定義nogas函數的模版
    nogas_temp, err := template.New("nogas").Parse(test_nogas_temp)
    if err != nil {
        fmt.Println("failed to template nogas_temp", err)
        return err
    }

    var func_nogas_data FuncNoGasParams
    func_nogas_data.NewContractName = "NewPdbank"

    //定義有gas模版
    hasgas_temp, err := template.New("hasgas").Parse(test_gas_temp)
    if err != nil {
        fmt.Println("failed to template hasgas_temp", err)
        return err
    }

    var func_gas_data FuncGasParams
    func_gas_data.NewContractName = "NewPdbank"

    //對abi進行遍歷處理
    for _, v := range abiInfos {
        v.Name = strings.Title(v.Name) //標題優化,首字母大寫, hello world - > Hello World
        if v.Type == "constructor" {
            // 若是是構造函數-部署函數
            deploy_data.DeployParams = "(auth,testClient"
            for _, vv := range v.Inputs {
                //須要根據輸入數據類型來判斷如何處理:string,address,uint256
                if vv.Type == "address" {
                    deploy_data.DeployParams += ",common.HexToAddress(\"0xD55E88D9156355C584982Db2C96dD1C2c63788C2\")"
                } else if vv.Type == "uint256" {
                    deploy_data.DeployParams += ",big.NewInt(1000)"
                } else if vv.Type == "string" {
                    deploy_data.DeployParams += ",\"yekai\""
                }

            }
            deploy_data.DeployParams += ")"
            //模版的執行
            err = deploy_temp.Execute(outfile, &deploy_data)
            if err != nil {
                fmt.Println("failed to template Execute ", err)
                return err
            }
        } else {
            //處理其餘函數
            if len(v.Outputs) > 0 {
                //不須要gas函數
                func_nogas_data.FuncName = v.Name

                func_nogas_data.InputParams = "(nil"
                for _, vv := range v.Inputs {
                    //須要根據輸入數據類型來判斷如何處理:string,address,uint256
                    if vv.Type == "address" {
                        func_nogas_data.InputParams += ",common.HexToAddress(\"0xD55E88D9156355C584982Db2C96dD1C2c63788C2\")"
                    } else if vv.Type == "uint256" {
                        func_nogas_data.InputParams += ",big.NewInt(1000)"
                    } else if vv.Type == "string" {
                        func_nogas_data.InputParams += ",\"yekai\""
                    }

                }
                func_nogas_data.InputParams += ")"
                //輸入參數
                num := 0
                strOutPuts := ""
                for _, _ = range v.Outputs {
                    strOutPuts = fmt.Sprintf("%sdata%d,", strOutPuts, num)
                    num++
                }
                strOutPuts += "err"
                func_nogas_data.OutParams = strOutPuts

                //模版的執行
                err = nogas_temp.Execute(outfile, &func_nogas_data)
                if err != nil {
                    fmt.Println("failed to template nogas Execute ", err)
                    return err
                }
            } else {
                //須要消耗gas
                func_gas_data.FuncName = v.Name
                func_gas_data.InputParams = "(auth"
                for _, vv := range v.Inputs {
                    //須要根據輸入數據類型來判斷如何處理:string,address,uint256
                    if vv.Type == "address" {
                        func_gas_data.InputParams += ",common.HexToAddress(\"0xD55E88D9156355C584982Db2C96dD1C2c63788C2\")"
                    } else if vv.Type == "uint256" {
                        func_gas_data.InputParams += ",big.NewInt(1000)"
                    } else if vv.Type == "string" {
                        func_gas_data.InputParams += ",\"yekai\""
                    }

                }
                func_gas_data.InputParams += ")"
                //模版的執行
                err = hasgas_temp.Execute(outfile, &func_gas_data)
                if err != nil {
                    fmt.Println("failed to template hasgas Execute ", err)
                    return err
                }
            }
        }
    }

    return nil
}

func Impl_main_code() error {
    //1. 寫到哪
    outfile, err := os.OpenFile("build/main.go", os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
    if err != nil {
        fmt.Println("failed to open file", err)
        return err
    }
    defer outfile.Close()
    // 讀取abi文件信息
    abiInfos, err := readAbi("contracts/pdbank.abi")
    if err != nil {
        fmt.Println("failed to read abi", err)
        return err
    }
    funcNames := ""
    //"abc","def","
    num := 0
    var funcInfos []FuncInfo
    var funcInfo FuncInfo
    // 2- 第一個函數
    for _, v := range abiInfos {
        if v.Type != "constructor" {
            if num == 0 {
                //第一個
                funcNames += fmt.Sprintf(`"%s"`, v.Name)
            } else {
                funcNames += fmt.Sprintf(`,"%s"`, v.Name)
            }
            num++
            funcInfo.FuncName = strings.Title(v.Name)
            funcInfo.Num = num + 1
            funcInfos = append(funcInfos, funcInfo)
        }
    }
    main_str1 := fmt.Sprintf(test_run_main_temp, funcNames)
    _, err = outfile.WriteString(main_str1)
    if err != nil {
        fmt.Println("failed to write to main.go", err)
        return err
    }

    //創建一個模版,輸出內容
    main_temp, err := template.New("main").Parse(test_build_main_temp)
    if err != nil {
        fmt.Println("failed to template main", err)
        return err
    }
    err = main_temp.Execute(outfile, funcInfos)
    if err != nil {
        fmt.Println("failed to Execute main", err)
        return err
    }
    return err
}

func Run() {
    Impl_run_code()
    Impl_main_code()
}

在main函數內增長此部分調用

package main

import (
    "fmt"
    "gosolkit/templates"
    "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] == "2" {
        //build test code
        templates.Run()
    } else {
        Usage()
        os.Exit(0)
    }

}

若是以爲代碼看着太累,能夠來看視頻教程

Go語言打造以太坊智能合約開發框架



相關文章
相關標籤/搜索