OSDN Git Service

Hulk did something
[bytom/vapor.git] / equity / compiler / cmd / equitycmd / equitycmd.go
1 package main
2
3 import (
4         "bufio"
5         "bytes"
6         "fmt"
7         "log"
8         "os"
9         "strings"
10
11         "github.com/vapor/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/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/vapor/equity/compiler": true,
70                 "github.com/vapor/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/vapor/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/vapor/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/vapor/encoding/json")
285         }
286         return strings.Join(strs, ", "), imports
287 }