github.com/sixexorg/magnetic-ring@v0.0.0-20191119090307-31705a21e419/vm/cvm/cmd/cvm.go (about) 1 package main 2 3 import ( 4 "crypto/rand" 5 "encoding/gob" 6 "encoding/hex" 7 "fmt" 8 "github.com/sixexorg/magnetic-ring/vm/cvm" 9 "github.com/sixexorg/magnetic-ring/vm/cvm/ast" 10 "github.com/sixexorg/magnetic-ring/vm/cvm/parse" 11 "github.com/urfave/cli" 12 "io" 13 "os" 14 "path" 15 ) 16 17 const ( 18 contractDir = "./contract" 19 ) 20 21 func init() { 22 gob.Register(ast.ExprBase{}) 23 gob.Register(ast.LocalAssignStmt{}) 24 gob.Register(ast.AssignStmt{}) 25 gob.Register(ast.TableExpr{}) 26 gob.Register(ast.FuncCallStmt{}) 27 gob.Register(ast.NumberExpr{}) 28 gob.Register(ast.StringExpr{}) 29 gob.Register(ast.NilExpr{}) 30 gob.Register(ast.IdentExpr{}) 31 gob.Register(ast.AttrGetExpr{}) 32 gob.Register(ast.FuncCallExpr{}) 33 gob.Register(ast.ParList{}) 34 gob.Register(ast.ArithmeticOpExpr{}) 35 gob.Register(ast.FunctionExpr{}) 36 gob.Register(ast.ReturnStmt{}) 37 gob.Register(ast.LogicalOpExpr{}) 38 gob.Register(ast.IfStmt{}) 39 gob.Register(ast.GenericForStmt{}) 40 gob.Register(ast.NumberForStmt{}) 41 gob.Register(ast.Comma3Expr{}) 42 gob.Register(ast.TrueExpr{}) 43 gob.Register(ast.FalseExpr{}) 44 gob.Register(ast.WhileStmt{}) 45 gob.Register(ast.UnaryLenOpExpr{}) 46 gob.Register(ast.UnaryMinusOpExpr{}) 47 gob.Register(ast.UnaryNotOpExpr{}) 48 gob.Register(ast.BreakStmt{}) 49 gob.Register(ast.RelationalOpExpr{}) 50 gob.Register(ast.FuncDefStmt{}) 51 gob.Register(ast.StringConcatOpExpr{}) 52 } 53 54 func main() { 55 app := newApp() 56 if err := app.Run(os.Args); err != nil { 57 //logger.Error("start app failed. %v", err) 58 } 59 60 // L := cvm.NewState() 61 // defer L.Close() 62 // if err := L.DoFile("vm/cvm/cmd/test.lua"); err != nil { 63 // panic(err) 64 // } 65 66 return 67 } 68 69 func address() string { 70 var buf [32]byte 71 rand.Read(buf[:]) 72 return hex.EncodeToString(buf[:]) 73 } 74 75 func deploy(ctx *cli.Context) (err error) { 76 filePath := ctx.Args().Get(0) 77 fp, err := os.Open(filePath) 78 if err != nil { 79 fmt.Printf("open file failed. casue: %v\n", err) 80 return err 81 } 82 defer fp.Close() 83 84 chunk, err := parse.Parse(fp, "") 85 if err != nil { 86 fmt.Printf("Abstract syntax tree: %v\n", err) 87 return 88 } 89 90 for _, stmt := range chunk { 91 if callStmt, ok := stmt.(*ast.FuncCallStmt); ok { 92 expr := callStmt.Expr.(*ast.FuncCallExpr) 93 if expr.Func.(*ast.IdentExpr).Value == "require" { 94 continue 95 } 96 fmt.Printf("function call is forbiden. start line: %d, end line: %d\n", callStmt.Line(), callStmt.LastLine()) 97 return err 98 } 99 } 100 proto, err := cvm.Compile(chunk, "") 101 if err != nil { 102 fmt.Printf("compile failed. cause: %v\n", err) 103 return err 104 } 105 vm := cvm.NewState() 106 lfunc := vm.NewFunctionFromProto(proto) 107 vm.Push(lfunc) 108 vm.Call(0, 1) 109 ret := vm.Get(vm.GetTop()) 110 vm.Pop(vm.GetTop()) 111 vm.SetGlobal("contract", ret) 112 113 fcStmt := &ast.FuncCallStmt{ 114 Expr: &ast.FuncCallExpr{ 115 Func: &ast.IdentExpr{ 116 Value: "init", 117 }, 118 }, 119 } 120 121 internalProto, err := cvm.Compile([]ast.Stmt{fcStmt}, "internal") 122 if err != nil { 123 fmt.Printf("Built-in initialization error %v\n", err) 124 return err 125 } 126 127 lfunc = vm.NewFunctionFromProto(internalProto) 128 vm.Push(lfunc) 129 if err = vm.PCall(0, cvm.MultRet, nil); err != nil { 130 fmt.Printf("Failed to execute script %v\n", err) 131 return err 132 } 133 134 if _, err := os.Stat(contractDir); os.IsNotExist(err) { 135 if err = os.Mkdir(contractDir, 0755); err != nil { 136 os.Exit(1) 137 } 138 } 139 140 data := ast.Serialize(chunk) 141 if err != nil { 142 fmt.Printf("contrace serializion failed. cause: %v\n", err) 143 return nil 144 } 145 address := address() 146 fpParsed, err := os.Create(path.Join(contractDir, address)) 147 if err != nil { 148 fmt.Printf("create file failed. cause: %v\n", err) 149 return nil 150 } 151 defer fpParsed.Close() 152 153 _, err = fpParsed.Write(data) 154 if err != nil { 155 fmt.Printf("Save error: %v\n", err) 156 return err 157 } 158 159 // After the parsing and saving, you still need to initialize the work. 160 // Initialization should be done before saving to a file. Make sure the initialization is working. 161 // The contract is successfully deployed after the operation is successful. 162 163 fmt.Printf("Successful contract deployment: %s\n", address) 164 return nil 165 } 166 167 func invoke(ctx *cli.Context) (err error) { 168 filePath := ctx.Args().Get(0) 169 funcName := ctx.Args().Get(1) 170 fpParsed, err := os.Open(path.Join(contractDir, filePath)) 171 if err != nil { 172 fmt.Printf("create file failed. cause: %v\n", err) 173 return nil 174 } 175 defer fpParsed.Close() 176 177 chunk2 := make([]byte, 0) 178 buf := make([]byte, 1024) 179 for { 180 n, err := fpParsed.Read(buf) 181 if err != nil && err != io.EOF { 182 panic(err) 183 } 184 if 0 == n { 185 break 186 } 187 chunk2 = append(chunk2, buf[:n]...) 188 } 189 190 chunk := ast.Deserizlize(chunk2) 191 if err != nil { 192 fmt.Printf("deserialize chunk failed. cause: %v\n", err) 193 return err 194 } 195 fmt.Printf("Reload %v\n", chunk) 196 197 proto, err := cvm.Compile(chunk, filePath) 198 if err != nil { 199 fmt.Printf("compile failed. cause: %v\n", err) 200 return err 201 } 202 vm := cvm.NewState() 203 lfunc := vm.NewFunctionFromProto(proto) 204 vm.Push(lfunc) 205 vm.Call(0, 1) 206 ret := vm.Get(vm.GetTop()) 207 vm.Pop(vm.GetTop()) 208 vm.SetGlobal("contract", ret) 209 210 fcStmt := &ast.FuncCallStmt{ 211 Expr: &ast.FuncCallExpr{ 212 Func: &ast.IdentExpr{ 213 Value: funcName, 214 }, 215 }, 216 } 217 218 internalProto, err := cvm.Compile([]ast.Stmt{fcStmt}, "internal") 219 if err != nil { 220 fmt.Printf("Built-in initialization error %v\n", err) 221 return err 222 } 223 224 lfunc = vm.NewFunctionFromProto(internalProto) 225 vm.Push(lfunc) 226 if err = vm.PCall(0, cvm.MultRet, nil); err != nil { 227 fmt.Printf("Failed to execute script %v\n", err) 228 return err 229 } 230 231 return nil 232 } 233 234 func check(ctx *cli.Context) (err error) { 235 filePath := ctx.Args().Get(0) 236 fp, err := os.Open(filePath) 237 if err != nil { 238 fmt.Printf("open file failed when check your contract. casue: %v\n", err) 239 return err 240 } 241 defer fp.Close() 242 243 chunk, err := parse.Parse(fp, "") 244 if err != nil { 245 fmt.Printf("Abstract syntax tree failed: %v\n", err) 246 return 247 } 248 249 defInit := false 250 for _, stmt := range chunk { 251 if callStmt, ok := stmt.(*ast.FuncCallStmt); ok { 252 expr := callStmt.Expr.(*ast.FuncCallExpr) 253 if expr.Func.(*ast.IdentExpr).Value == "require" { 254 continue 255 } 256 fmt.Printf("function call is forbiden. start line: %d, end line: %d\n", callStmt.Line(), callStmt.LastLine()) 257 return err 258 } 259 if funcDef, ok := stmt.(*ast.FuncDefStmt); ok { 260 if expr, ok := funcDef.Name.Func.(*ast.IdentExpr); ok { 261 if expr.Value == "init" { 262 defInit = true 263 } 264 } 265 } 266 } 267 if !defInit { 268 fmt.Printf("need define function `init`") 269 return err 270 } 271 return nil 272 } 273 func newApp() *cli.App { 274 app := cli.NewApp() 275 //app.Action = run 276 277 app.Version = "0.0.1" 278 app.Name = "cvm" 279 app.Usage = "command line interface" 280 app.Author = "crystal.org" 281 app.Copyright = "Copyright 2017-2019 The crystal.la Authors" 282 app.Email = "develop@crystal.la" 283 app.Description = "LUA-based virtual machine" 284 285 app.Commands = []cli.Command{ 286 { 287 Name: "check", 288 Action: check, 289 Usage: "Check a stmart contract before deploy it", 290 }, 291 { 292 Name: "deploy", 293 Action: deploy, 294 Usage: "Deploy a stmart contract", 295 }, 296 { 297 Name: "invoke", 298 Action: invoke, 299 Usage: "Invoke a stmart contract after deploy it", 300 }, 301 } 302 return app 303 }