OSDN Git Service

cp code from bytom repo
[bytom/equity.git] / compiler / cmd / equitycmd / equitycmd.go
1 package main
2
3 import (
4         "bytes"
5         "flag"
6         "fmt"
7         "log"
8         "os"
9         "strings"
10
11         "github.com/equity/compiler"
12 )
13
14 func main() {
15         packageName := flag.String("package", "main", "Go package name for generated file")
16         flag.Parse()
17
18         contracts, err := compiler.Compile(os.Stdin)
19         if err != nil {
20                 log.Fatal(err)
21         }
22
23         fmt.Printf("package %s\n\n", *packageName)
24
25         imports := map[string]bool{
26                 "bytes":        true,
27                 "encoding/hex": true,
28                 "fmt":          true,
29                 "github.com/bytom/equity/compiler": true,
30                 "github.com/bytom/protocol/vm":     true,
31         }
32
33         buf := new(bytes.Buffer)
34
35         if len(contracts) == 1 {
36                 fmt.Fprintf(buf, "var %s_body_bytes []byte\n\n", contracts[0].Name)
37         } else {
38                 fmt.Fprintf(buf, "var (\n")
39                 for _, contract := range contracts {
40                         fmt.Fprintf(buf, "\t%s_body_bytes []byte\n", contract.Name)
41                 }
42                 fmt.Fprintf(buf, ")\n\n")
43         }
44
45         fmt.Fprintf(buf, "func init() {\n")
46         for _, contract := range contracts {
47                 fmt.Fprintf(buf, "\t%s_body_bytes, _ = hex.DecodeString(\"%x\")\n", contract.Name, contract.Body)
48         }
49         fmt.Fprintf(buf, "}\n\n")
50
51         for _, contract := range contracts {
52                 fmt.Fprintf(buf, "// contract %s(%s) locks %s\n", contract.Name, paramsStr(contract.Params), contract.Value)
53                 fmt.Fprintf(buf, "//\n")
54                 maxWidth := 0
55                 for _, step := range contract.Steps {
56                         if len(step.Opcodes) > maxWidth {
57                                 maxWidth = len(step.Opcodes)
58                         }
59                 }
60                 format := fmt.Sprintf("// %%-%d.%ds  %%s\n", maxWidth, maxWidth)
61                 for _, step := range contract.Steps {
62                         fmt.Fprintf(buf, format, step.Opcodes, step.Stack)
63                 }
64                 fmt.Fprintf(buf, "\n")
65
66                 fmt.Fprintf(buf, "// PayTo%s instantiates contract %s as a program with specific arguments.\n", contract.Name, contract.Name)
67                 goParams, newImports := asGoParams(contract.Params)
68                 for _, imp := range newImports {
69                         imports[imp] = true
70                 }
71                 fmt.Fprintf(buf, "func PayTo%s(%s) ([]byte, error) {\n", contract.Name, goParams)
72                 fmt.Fprintf(buf, "\t_contractParams := []compiler.Param{\n")
73                 for _, param := range contract.Params {
74                         fmt.Fprintf(buf, "\t\t{Name: \"%s\", Type: \"%s\"},\n", param.Name, param.Type)
75                 }
76                 fmt.Fprintf(buf, "\t}\n")
77                 fmt.Fprintf(buf, "\tvar _contractArgs []compiler.ContractArg\n")
78                 for _, param := range contract.Params {
79                         switch param.Type {
80                         case "Amount":
81                                 fmt.Fprintf(buf, "\t_%s := int64(%s)\n", param.Name, param.Name)
82                                 fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{I: &_%s})\n", param.Name)
83                         case "Asset":
84                                 fmt.Fprintf(buf, "\t_%s := %s[:]\n", param.Name, param.Name)
85                                 fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{S: &_%s})\n", param.Name)
86                         case "Boolean", "Hash", "Program", "PublicKey", "Signature", "String":
87                                 fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{S: &%s})\n", param.Name)
88                         case "Integer":
89                                 fmt.Fprintf(buf, "\t_contractArgs = append(_contractArgs, compiler.ContractArg{I: &%s})\n", param.Name)
90                         }
91                 }
92                 fmt.Fprintf(buf, "\treturn compiler.Instantiate(_contractParams, %s_body_bytes, %v, _contractArgs)\n", contract.Name, contract.Recursive)
93                 fmt.Fprintf(buf, "}\n\n")
94
95                 fmt.Fprintf(buf, "// ParsePayTo%s parses the arguments out of an instantiation of contract %s.\n", contract.Name, contract.Name)
96                 fmt.Fprintf(buf, "// If the input is not an instantiation of %s, returns an error.\n", contract.Name)
97                 fmt.Fprintf(buf, "func ParsePayTo%s(prog []byte) ([][]byte, error) {\n", contract.Name)
98                 fmt.Fprintf(buf, "\tvar result [][]byte\n")
99                 fmt.Fprintf(buf, "\tinsts, err := vm.ParseProgram(prog)\n")
100                 fmt.Fprintf(buf, "\tif err != nil {\n")
101                 fmt.Fprintf(buf, "\t\treturn nil, err\n")
102                 fmt.Fprintf(buf, "\t}\n")
103                 fmt.Fprintf(buf, "\tfor i := 0; i < %d; i++ {\n", len(contract.Params))
104                 fmt.Fprintf(buf, "\t\tif len(insts) == 0 {\n")
105                 fmt.Fprintf(buf, "\t\t\treturn nil, fmt.Errorf(\"program too short\")\n")
106                 fmt.Fprintf(buf, "\t\t}\n")
107                 fmt.Fprintf(buf, "\t\tif !insts[0].IsPushdata() {\n")
108                 fmt.Fprintf(buf, "\t\t\treturn nil, fmt.Errorf(\"too few arguments\")\n")
109                 fmt.Fprintf(buf, "\t\t}\n")
110                 fmt.Fprintf(buf, "\t\tresult = append(result, insts[0].Data)\n")
111                 fmt.Fprintf(buf, "\t\tinsts = insts[1:]\n")
112                 fmt.Fprintf(buf, "\t}\n")
113                 if contract.Recursive {
114                         // args... body DEPTH OVER 0 CHECKPREDICATE
115                         fmt.Fprintf(buf, "\tif len(insts) == 0 {\n")
116                         fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"program too short\")\n")
117                         fmt.Fprintf(buf, "\t}\n")
118                         fmt.Fprintf(buf, "\tif !insts[0].IsPushdata() {\n")
119                         fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"too few arguments\")\n")
120                         fmt.Fprintf(buf, "\t}\n")
121                         fmt.Fprintf(buf, "\tif !bytes.Equal(%s_body_bytes, insts[0].Data) {\n", contract.Name)
122                         fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"body bytes do not match %s\")\n", contract.Name)
123                         fmt.Fprintf(buf, "\t}\n")
124                         fmt.Fprintf(buf, "\tinsts = insts[1:]\n")
125                 } // else args ... DEPTH body 0 CHECKPREDICATE
126                 fmt.Fprintf(buf, "\tif len(insts) != 4 {\n")
127                 fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"program too short\")\n")
128                 fmt.Fprintf(buf, "\t}\n")
129                 fmt.Fprintf(buf, "\tif insts[0].Op != vm.OP_DEPTH {\n")
130                 fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n")
131                 fmt.Fprintf(buf, "\t}\n")
132                 if contract.Recursive {
133                         fmt.Fprintf(buf, "\tif insts[1].Op != vm.OP_OVER {\n")
134                         fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n")
135                         fmt.Fprintf(buf, "\t}\n")
136                 } else {
137                         fmt.Fprintf(buf, "\tif !insts[1].IsPushdata() {\n")
138                         fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n")
139                         fmt.Fprintf(buf, "\t}\n")
140                         fmt.Fprintf(buf, "\tif !bytes.Equal(%s_body_bytes, insts[1].Data) {\n", contract.Name)
141                         fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"body bytes do not match %s\")\n", contract.Name)
142                         fmt.Fprintf(buf, "\t}\n")
143                 }
144                 fmt.Fprintf(buf, "\tif !insts[2].IsPushdata() {\n")
145                 fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n")
146                 fmt.Fprintf(buf, "\t}\n")
147                 fmt.Fprintf(buf, "\tv, err := vm.AsInt64(insts[2].Data)\n")
148                 fmt.Fprintf(buf, "\tif err != nil {\n")
149                 fmt.Fprintf(buf, "\t\treturn nil, err\n")
150                 fmt.Fprintf(buf, "\t}\n")
151                 fmt.Fprintf(buf, "\tif v != 0 {\n")
152                 fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n")
153                 fmt.Fprintf(buf, "\t}\n")
154                 fmt.Fprintf(buf, "\tif insts[3].Op != vm.OP_CHECKPREDICATE {\n")
155                 fmt.Fprintf(buf, "\t\treturn nil, fmt.Errorf(\"wrong program format\")\n")
156                 fmt.Fprintf(buf, "\t}\n")
157                 fmt.Fprintf(buf, "\treturn result, nil\n")
158                 fmt.Fprintf(buf, "}\n\n")
159
160                 // TODO(bobg): RedeemFoo_Bar functions for marshaling the args to
161                 // the Bar clause of contract Foo.
162         }
163
164         fmt.Printf("import (\n")
165         for imp := range imports {
166                 fmt.Printf("\t\"%s\"\n", imp)
167         }
168         fmt.Printf(")\n\n")
169
170         os.Stdout.Write(buf.Bytes())
171 }
172
173 func paramsStr(params []*compiler.Param) string {
174         var strs []string
175         for _, p := range params {
176                 strs = append(strs, fmt.Sprintf("%s: %s", p.Name, p.Type))
177         }
178         return strings.Join(strs, ", ")
179 }
180
181 func asGoParams(params []*compiler.Param) (goParams string, imports []string) {
182         var strs []string
183         for _, p := range params {
184                 var typ string
185                 switch p.Type {
186                 case "Amount":
187                         typ = "uint64"
188                 case "Asset":
189                         typ = "bc.AssetId"
190                         imports = append(imports, "github.com/bytom/protocol/bc")
191                 case "Boolean":
192                         typ = "bool"
193                 case "Hash":
194                         typ = "[]byte"
195                 case "Integer":
196                         typ = "int64"
197                 case "Program":
198                         typ = "[]byte"
199                 case "PublicKey":
200                         typ = "ed25519.PublicKey"
201                         imports = append(imports, "github.com/bytom/crypto/ed25519")
202                 case "Signature":
203                         typ = "[]byte"
204                 case "String":
205                         typ = "[]byte"
206                 }
207                 strs = append(strs, fmt.Sprintf("%s %s", p.Name, typ))
208         }
209         return strings.Join(strs, ", "), imports
210 }