github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/equity/compiler/cmd/equitycmd/equitycmd.go (about)

     1  package main
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"log"
     8  	"os"
     9  	"strings"
    10  
    11  	"github.com/bytom/bytom/equity/compiler"
    12  )
    13  
    14  var (
    15  	// generateInstPath is the directory (need to combine with GOPATH) for store generated contract instance
    16  	generateInstPath = "/src/github.com/bytom/bytom/equity/instance/"
    17  )
    18  
    19  func main() {
    20  	if len(os.Args) != 2 {
    21  		fmt.Println("command args: [command] [contract file_path]")
    22  		os.Exit(0)
    23  	}
    24  
    25  	filename := os.Args[1]
    26  	inputFile, inputError := os.Open(filename)
    27  	if inputError != nil {
    28  		fmt.Printf("An error occurred on opening the inputfile\n" +
    29  			"Does the file exist?\n" +
    30  			"Have you got acces to it?\n")
    31  		os.Exit(0)
    32  	}
    33  	defer inputFile.Close()
    34  
    35  	inputReader := bufio.NewReader(inputFile)
    36  	contracts, err := compiler.Compile(inputReader)
    37  	if err != nil {
    38  		log.Fatal(err)
    39  	}
    40  
    41  	var packageName *string
    42  	var midstr string
    43  	var outstr []string
    44  
    45  	//change the windows path into unix path
    46  	filename = strings.Replace(filename, "\\", "/", -1)
    47  	if strings.Contains(filename, "/") == true {
    48  		outstr = strings.Split(filename, "/")
    49  		midstr = outstr[len(outstr)-1]
    50  	} else {
    51  		midstr = filename
    52  	}
    53  
    54  	//check whether the filename contains point flag
    55  	if strings.Contains(midstr, ".") == true {
    56  		outstr = strings.Split(midstr, ".")
    57  		packageName = &outstr[0]
    58  	} else {
    59  		packageName = &midstr
    60  	}
    61  
    62  	header := new(bytes.Buffer)
    63  	fmt.Fprintf(header, "package instance\n\n")
    64  
    65  	imports := map[string]bool{
    66  		"bytes":                                  true,
    67  		"encoding/hex":                           true,
    68  		"fmt":                                    true,
    69  		"github.com/bytom/bytom/equity/compiler": true,
    70  		"github.com/bytom/bytom/protocol/vm":     true,
    71  	}
    72  
    73  	buf := new(bytes.Buffer)
    74  
    75  	if len(contracts) == 1 {
    76  		fmt.Fprintf(buf, "// %sBodyBytes refer to contract's body\n", contracts[0].Name)
    77  		fmt.Fprintf(buf, "var %sBodyBytes []byte\n\n", contracts[0].Name)
    78  	} else {
    79  		fmt.Fprintf(buf, "var (\n")
    80  		for _, contract := range contracts {
    81  			fmt.Fprintf(buf, "\t%sBodyBytes []byte\n", contract.Name)
    82  		}
    83  		fmt.Fprintf(buf, ")\n\n")
    84  	}
    85  
    86  	fmt.Fprintf(buf, "func init() {\n")
    87  	for _, contract := range contracts {
    88  		fmt.Fprintf(buf, "\t%sBodyBytes, _ = hex.DecodeString(\"%x\")\n", contract.Name, contract.Body)
    89  	}
    90  	fmt.Fprintf(buf, "}\n\n")
    91  
    92  	for _, contract := range contracts {
    93  		fmt.Fprintf(buf, "// contract %s(%s) locks %s\n", contract.Name, paramsStr(contract.Params), contract.Value)
    94  		fmt.Fprintf(buf, "//\n")
    95  		maxWidth := 0
    96  		for _, step := range contract.Steps {
    97  			if len(step.Opcodes) > maxWidth {
    98  				maxWidth = len(step.Opcodes)
    99  			}
   100  		}
   101  		format := fmt.Sprintf("// %%-%d.%ds  %%s\n", maxWidth, maxWidth)
   102  		for _, step := range contract.Steps {
   103  			fmt.Fprintf(buf, format, step.Opcodes, step.Stack)
   104  		}
   105  		fmt.Fprintf(buf, "\n")
   106  
   107  		fmt.Fprintf(buf, "// PayTo%s instantiates contract %s as a program with specific arguments.\n", contract.Name, contract.Name)
   108  		goParams, newImports := asGoParams(contract.Params)
   109  		for _, imp := range newImports {
   110  			imports[imp] = true
   111  		}
   112  		fmt.Fprintf(buf, "func PayTo%s(%s) ([]byte, error) {\n", contract.Name, goParams)
   113  		fmt.Fprintf(buf, "\t_contractParams := []*compiler.Param{\n")
   114  		for _, param := range contract.Params {
   115  			fmt.Fprintf(buf, "\t\t{Name: \"%s\", Type: \"%s\"},\n", param.Name, param.Type)
   116  		}
   117  		fmt.Fprintf(buf, "\t}\n")
   118  		fmt.Fprintf(buf, "\tvar _contractArgs []compiler.ContractArg\n")
   119  		for _, param := range contract.Params {
   120  			switch param.Type {
   121  			case "Amount":
   122  				fmt.Fprintf(buf, "\t_%s := int64(%s)\n", param.Name, param.Name)
   123  				fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{I: &_%s})\n", param.Name)
   124  			case "Asset":
   125  				fmt.Fprintf(buf, "\t_%s := %s.Bytes()\n", param.Name, param.Name)
   126  				fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{S: (*json.HexBytes)(&_%s)})\n", param.Name)
   127  			case "Boolean":
   128  				fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{B: &%s})\n", param.Name)
   129  			case "Integer":
   130  				fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{I: &%s})\n", param.Name)
   131  			case "Hash", "Program", "PublicKey", "Signature", "String":
   132  				fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{S: (*json.HexBytes)(&%s)})\n", param.Name)
   133  			}
   134  		}
   135  		fmt.Fprintf(buf, "\treturn compiler.Instantiate(%sBodyBytes, _contractParams, %v, _contractArgs)\n", contract.Name, contract.Recursive)
   136  		fmt.Fprintf(buf, "}\n\n")
   137  
   138  		fmt.Fprintf(buf, "// ParsePayTo%s parses the arguments out of an instantiation of contract %s.\n", contract.Name, contract.Name)
   139  		fmt.Fprintf(buf, "// If the input is not an instantiation of %s, returns an error.\n", contract.Name)
   140  		fmt.Fprintf(buf, "func ParsePayTo%s(prog []byte) ([][]byte, error) {\n", contract.Name)
   141  		fmt.Fprintf(buf, "\tvar result [][]byte\n")
   142  		fmt.Fprintf(buf, "\tinsts, err := vm.ParseProgram(prog)\n")
   143  		fmt.Fprintf(buf, "\tif err != nil {\n")
   144  		fmt.Fprintf(buf, "\t\treturn nil, err\n")
   145  		fmt.Fprintf(buf, "\t}\n")
   146  		fmt.Fprintf(buf, "\tfor i := 0; i < %d; i++ {\n", len(contract.Params))
   147  		fmt.Fprintf(buf, "\t\tif len(insts) == 0 {\n")
   148  		fmt.Fprintf(buf, "\t\t\treturn nil, fmt.Errorf(\"program too short\")\n")
   149  		fmt.Fprintf(buf, "\t\t}\n")
   150  		fmt.Fprintf(buf, "\t\tif !insts[0].IsPushdata() {\n")
   151  		fmt.Fprintf(buf, "\t\t\treturn nil, fmt.Errorf(\"too few arguments\")\n")
   152  		fmt.Fprintf(buf, "\t\t}\n")
   153  		fmt.Fprintf(buf, "\t\tresult = append(result, insts[0].Data)\n")
   154  		fmt.Fprintf(buf, "\t\tinsts = insts[1:]\n")
   155  		fmt.Fprintf(buf, "\t}\n")
   156  		if contract.Recursive {
   157  			// args... body DEPTH OVER 0 CHECKPREDICATE
   158  			fmt.Fprintf(buf, "\tif len(insts) == 0 {\n")
   159  			fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"program too short\")\n")
   160  			fmt.Fprintf(buf, "\t}\n")
   161  			fmt.Fprintf(buf, "\tif !insts[0].IsPushdata() {\n")
   162  			fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"too few arguments\")\n")
   163  			fmt.Fprintf(buf, "\t}\n")
   164  			fmt.Fprintf(buf, "\tif !bytes.Equal(%sBodyBytes, insts[0].Data) {\n", contract.Name)
   165  			fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"body bytes do not match %s\")\n", contract.Name)
   166  			fmt.Fprintf(buf, "\t}\n")
   167  			fmt.Fprintf(buf, "\tinsts = insts[1:]\n")
   168  		} // else args ... DEPTH body 0 CHECKPREDICATE
   169  		fmt.Fprintf(buf, "\tif len(insts) != 4 {\n")
   170  		fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"program too short\")\n")
   171  		fmt.Fprintf(buf, "\t}\n")
   172  		fmt.Fprintf(buf, "\tif insts[0].Op != vm.OP_DEPTH {\n")
   173  		fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n")
   174  		fmt.Fprintf(buf, "\t}\n")
   175  		if contract.Recursive {
   176  			fmt.Fprintf(buf, "\tif insts[1].Op != vm.OP_OVER {\n")
   177  			fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n")
   178  			fmt.Fprintf(buf, "\t}\n")
   179  		} else {
   180  			fmt.Fprintf(buf, "\tif !insts[1].IsPushdata() {\n")
   181  			fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n")
   182  			fmt.Fprintf(buf, "\t}\n")
   183  			fmt.Fprintf(buf, "\tif !bytes.Equal(%sBodyBytes, insts[1].Data) {\n", contract.Name)
   184  			fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"body bytes do not match %s\")\n", contract.Name)
   185  			fmt.Fprintf(buf, "\t}\n")
   186  		}
   187  		fmt.Fprintf(buf, "\tif !insts[2].IsPushdata() {\n")
   188  		fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n")
   189  		fmt.Fprintf(buf, "\t}\n")
   190  		fmt.Fprintf(buf, "\tv, err := vm.AsInt64(insts[2].Data)\n")
   191  		fmt.Fprintf(buf, "\tif err != nil {\n")
   192  		fmt.Fprintf(buf, "\t\treturn nil, err\n")
   193  		fmt.Fprintf(buf, "\t}\n")
   194  		fmt.Fprintf(buf, "\tif v != 0 {\n")
   195  		fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n")
   196  		fmt.Fprintf(buf, "\t}\n")
   197  		fmt.Fprintf(buf, "\tif insts[3].Op != vm.OP_CHECKPREDICATE {\n")
   198  		fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n")
   199  		fmt.Fprintf(buf, "\t}\n")
   200  		fmt.Fprintf(buf, "\treturn result, nil\n")
   201  		fmt.Fprintf(buf, "}\n\n")
   202  
   203  		// TODO(bobg): RedeemFoo_Bar functions for marshaling the args to
   204  		// the Bar clause of contract Foo.
   205  	}
   206  
   207  	fmt.Fprintf(header, "import (\n")
   208  	for imp := range imports {
   209  		fmt.Fprintf(header, "\t\"%s\"\n", imp)
   210  	}
   211  	fmt.Fprintf(header, ")\n\n")
   212  
   213  	//get the Environment variables of GOPATH
   214  	gopath := os.Getenv("GOPATH")
   215  	path := gopath + generateInstPath
   216  
   217  	//if the directory is not exist, create it
   218  	_, err = os.Stat(path)
   219  	if err != nil {
   220  		if os.IsNotExist(err) {
   221  			direrr := os.MkdirAll(path, os.ModePerm)
   222  			if direrr != nil {
   223  				log.Fatal(direrr)
   224  			}
   225  			fmt.Println("the path is create success")
   226  		} else {
   227  			log.Fatal(err)
   228  		}
   229  	}
   230  
   231  	//store buf by create file
   232  	file, _ := os.Create(path + *packageName + ".go")
   233  	defer file.Close()
   234  	file.Write(header.Bytes())
   235  	file.Write(buf.Bytes())
   236  	fmt.Printf("create file [%s] success!\n", *packageName+".go")
   237  }
   238  
   239  func paramsStr(params []*compiler.Param) string {
   240  	var strs []string
   241  	for _, p := range params {
   242  		strs = append(strs, fmt.Sprintf("%s: %s", p.Name, p.Type))
   243  	}
   244  	return strings.Join(strs, ", ")
   245  }
   246  
   247  func asGoParams(params []*compiler.Param) (goParams string, imports []string) {
   248  	var strs []string
   249  	strFlag := false
   250  	for _, p := range params {
   251  		var typ string
   252  		switch p.Type {
   253  		case "Amount":
   254  			typ = "uint64"
   255  		case "Asset":
   256  			typ = "bc.AssetID"
   257  			imports = append(imports, "github.com/bytom/bytom/protocol/bc")
   258  			strFlag = true
   259  		case "Boolean":
   260  			typ = "bool"
   261  		case "Hash":
   262  			typ = "[]byte"
   263  			strFlag = true
   264  		case "Integer":
   265  			typ = "int64"
   266  		case "Program":
   267  			typ = "[]byte"
   268  			strFlag = true
   269  		case "PublicKey":
   270  			typ = "ed25519.PublicKey"
   271  			imports = append(imports, "github.com/bytom/bytom/crypto/ed25519")
   272  			strFlag = true
   273  		case "Signature":
   274  			typ = "[]byte"
   275  			strFlag = true
   276  		case "String":
   277  			typ = "[]byte"
   278  			strFlag = true
   279  		}
   280  		strs = append(strs, fmt.Sprintf("%s %s", p.Name, typ))
   281  	}
   282  
   283  	if strFlag {
   284  		imports = append(imports, "github.com/bytom/bytom/encoding/json")
   285  	}
   286  	return strings.Join(strs, ", "), imports
   287  }