package main import ( "bufio" "bytes" "fmt" "log" "os" "strings" "github.com/vapor/equity/compiler" ) var ( // generateInstPath is the directory (need to combine with GOPATH) for store generated contract instance generateInstPath = "/src/github.com/vapor/equity/instance/" ) func main() { if len(os.Args) != 2 { fmt.Println("command args: [command] [contract file_path]") os.Exit(0) } filename := os.Args[1] inputFile, inputError := os.Open(filename) if inputError != nil { fmt.Printf("An error occurred on opening the inputfile\n" + "Does the file exist?\n" + "Have you got acces to it?\n") os.Exit(0) } defer inputFile.Close() inputReader := bufio.NewReader(inputFile) contracts, err := compiler.Compile(inputReader) if err != nil { log.Fatal(err) } var packageName *string var midstr string var outstr []string //change the windows path into unix path filename = strings.Replace(filename, "\\", "/", -1) if strings.Contains(filename, "/") == true { outstr = strings.Split(filename, "/") midstr = outstr[len(outstr)-1] } else { midstr = filename } //check whether the filename contains point flag if strings.Contains(midstr, ".") == true { outstr = strings.Split(midstr, ".") packageName = &outstr[0] } else { packageName = &midstr } header := new(bytes.Buffer) fmt.Fprintf(header, "package instance\n\n") imports := map[string]bool{ "bytes": true, "encoding/hex": true, "fmt": true, "github.com/vapor/equity/compiler": true, "github.com/vapor/protocol/vm": true, } buf := new(bytes.Buffer) if len(contracts) == 1 { fmt.Fprintf(buf, "// %sBodyBytes refer to contract's body\n", contracts[0].Name) fmt.Fprintf(buf, "var %sBodyBytes []byte\n\n", contracts[0].Name) } else { fmt.Fprintf(buf, "var (\n") for _, contract := range contracts { fmt.Fprintf(buf, "\t%sBodyBytes []byte\n", contract.Name) } fmt.Fprintf(buf, ")\n\n") } fmt.Fprintf(buf, "func init() {\n") for _, contract := range contracts { fmt.Fprintf(buf, "\t%sBodyBytes, _ = hex.DecodeString(\"%x\")\n", contract.Name, contract.Body) } fmt.Fprintf(buf, "}\n\n") for _, contract := range contracts { fmt.Fprintf(buf, "// contract %s(%s) locks %s\n", contract.Name, paramsStr(contract.Params), contract.Value) fmt.Fprintf(buf, "//\n") maxWidth := 0 for _, step := range contract.Steps { if len(step.Opcodes) > maxWidth { maxWidth = len(step.Opcodes) } } format := fmt.Sprintf("// %%-%d.%ds %%s\n", maxWidth, maxWidth) for _, step := range contract.Steps { fmt.Fprintf(buf, format, step.Opcodes, step.Stack) } fmt.Fprintf(buf, "\n") fmt.Fprintf(buf, "// PayTo%s instantiates contract %s as a program with specific arguments.\n", contract.Name, contract.Name) goParams, newImports := asGoParams(contract.Params) for _, imp := range newImports { imports[imp] = true } fmt.Fprintf(buf, "func PayTo%s(%s) ([]byte, error) {\n", contract.Name, goParams) fmt.Fprintf(buf, "\t_contractParams := []*compiler.Param{\n") for _, param := range contract.Params { fmt.Fprintf(buf, "\t\t{Name: \"%s\", Type: \"%s\"},\n", param.Name, param.Type) } fmt.Fprintf(buf, "\t}\n") fmt.Fprintf(buf, "\tvar _contractArgs []compiler.ContractArg\n") for _, param := range contract.Params { switch param.Type { case "Amount": fmt.Fprintf(buf, "\t_%s := int64(%s)\n", param.Name, param.Name) fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{I: &_%s})\n", param.Name) case "Asset": fmt.Fprintf(buf, "\t_%s := %s.Bytes()\n", param.Name, param.Name) fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{S: (*json.HexBytes)(&_%s)})\n", param.Name) case "Boolean": fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{B: &%s})\n", param.Name) case "Integer": fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{I: &%s})\n", param.Name) case "Hash", "Program", "PublicKey", "Signature", "String": fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{S: (*json.HexBytes)(&%s)})\n", param.Name) } } fmt.Fprintf(buf, "\treturn compiler.Instantiate(%sBodyBytes, _contractParams, %v, _contractArgs)\n", contract.Name, contract.Recursive) fmt.Fprintf(buf, "}\n\n") fmt.Fprintf(buf, "// ParsePayTo%s parses the arguments out of an instantiation of contract %s.\n", contract.Name, contract.Name) fmt.Fprintf(buf, "// If the input is not an instantiation of %s, returns an error.\n", contract.Name) fmt.Fprintf(buf, "func ParsePayTo%s(prog []byte) ([][]byte, error) {\n", contract.Name) fmt.Fprintf(buf, "\tvar result [][]byte\n") fmt.Fprintf(buf, "\tinsts, err := vm.ParseProgram(prog)\n") fmt.Fprintf(buf, "\tif err != nil {\n") fmt.Fprintf(buf, "\t\treturn nil, err\n") fmt.Fprintf(buf, "\t}\n") fmt.Fprintf(buf, "\tfor i := 0; i < %d; i++ {\n", len(contract.Params)) fmt.Fprintf(buf, "\t\tif len(insts) == 0 {\n") fmt.Fprintf(buf, "\t\t\treturn nil, fmt.Errorf(\"program too short\")\n") fmt.Fprintf(buf, "\t\t}\n") fmt.Fprintf(buf, "\t\tif !insts[0].IsPushdata() {\n") fmt.Fprintf(buf, "\t\t\treturn nil, fmt.Errorf(\"too few arguments\")\n") fmt.Fprintf(buf, "\t\t}\n") fmt.Fprintf(buf, "\t\tresult = append(result, insts[0].Data)\n") fmt.Fprintf(buf, "\t\tinsts = insts[1:]\n") fmt.Fprintf(buf, "\t}\n") if contract.Recursive { // args... body DEPTH OVER 0 CHECKPREDICATE fmt.Fprintf(buf, "\tif len(insts) == 0 {\n") fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"program too short\")\n") fmt.Fprintf(buf, "\t}\n") fmt.Fprintf(buf, "\tif !insts[0].IsPushdata() {\n") fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"too few arguments\")\n") fmt.Fprintf(buf, "\t}\n") fmt.Fprintf(buf, "\tif !bytes.Equal(%sBodyBytes, insts[0].Data) {\n", contract.Name) fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"body bytes do not match %s\")\n", contract.Name) fmt.Fprintf(buf, "\t}\n") fmt.Fprintf(buf, "\tinsts = insts[1:]\n") } // else args ... DEPTH body 0 CHECKPREDICATE fmt.Fprintf(buf, "\tif len(insts) != 4 {\n") fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"program too short\")\n") fmt.Fprintf(buf, "\t}\n") fmt.Fprintf(buf, "\tif insts[0].Op != vm.OP_DEPTH {\n") fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") fmt.Fprintf(buf, "\t}\n") if contract.Recursive { fmt.Fprintf(buf, "\tif insts[1].Op != vm.OP_OVER {\n") fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") fmt.Fprintf(buf, "\t}\n") } else { fmt.Fprintf(buf, "\tif !insts[1].IsPushdata() {\n") fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") fmt.Fprintf(buf, "\t}\n") fmt.Fprintf(buf, "\tif !bytes.Equal(%sBodyBytes, insts[1].Data) {\n", contract.Name) fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"body bytes do not match %s\")\n", contract.Name) fmt.Fprintf(buf, "\t}\n") } fmt.Fprintf(buf, "\tif !insts[2].IsPushdata() {\n") fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") fmt.Fprintf(buf, "\t}\n") fmt.Fprintf(buf, "\tv, err := vm.AsInt64(insts[2].Data)\n") fmt.Fprintf(buf, "\tif err != nil {\n") fmt.Fprintf(buf, "\t\treturn nil, err\n") fmt.Fprintf(buf, "\t}\n") fmt.Fprintf(buf, "\tif v != 0 {\n") fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") fmt.Fprintf(buf, "\t}\n") fmt.Fprintf(buf, "\tif insts[3].Op != vm.OP_CHECKPREDICATE {\n") fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n") fmt.Fprintf(buf, "\t}\n") fmt.Fprintf(buf, "\treturn result, nil\n") fmt.Fprintf(buf, "}\n\n") // TODO(bobg): RedeemFoo_Bar functions for marshaling the args to // the Bar clause of contract Foo. } fmt.Fprintf(header, "import (\n") for imp := range imports { fmt.Fprintf(header, "\t\"%s\"\n", imp) } fmt.Fprintf(header, ")\n\n") //get the Environment variables of GOPATH gopath := os.Getenv("GOPATH") path := gopath + generateInstPath //if the directory is not exist, create it _, err = os.Stat(path) if err != nil { if os.IsNotExist(err) { direrr := os.MkdirAll(path, os.ModePerm) if direrr != nil { log.Fatal(direrr) } fmt.Println("the path is create success") } else { log.Fatal(err) } } //store buf by create file file, _ := os.Create(path + *packageName + ".go") defer file.Close() file.Write(header.Bytes()) file.Write(buf.Bytes()) fmt.Printf("create file [%s] success!\n", *packageName+".go") } func paramsStr(params []*compiler.Param) string { var strs []string for _, p := range params { strs = append(strs, fmt.Sprintf("%s: %s", p.Name, p.Type)) } return strings.Join(strs, ", ") } func asGoParams(params []*compiler.Param) (goParams string, imports []string) { var strs []string strFlag := false for _, p := range params { var typ string switch p.Type { case "Amount": typ = "uint64" case "Asset": typ = "bc.AssetID" imports = append(imports, "github.com/vapor/protocol/bc") strFlag = true case "Boolean": typ = "bool" case "Hash": typ = "[]byte" strFlag = true case "Integer": typ = "int64" case "Program": typ = "[]byte" strFlag = true case "PublicKey": typ = "ed25519.PublicKey" imports = append(imports, "github.com/vapor/crypto/ed25519") strFlag = true case "Signature": typ = "[]byte" strFlag = true case "String": typ = "[]byte" strFlag = true } strs = append(strs, fmt.Sprintf("%s %s", p.Name, typ)) } if strFlag { imports = append(imports, "github.com/vapor/encoding/json") } return strings.Join(strs, ", "), imports }