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  }