package executor import ( "errors" "fmt" "io" "log" "os" "time" "github.com/gosuri/uitable" "github.com/perlin-network/life/compiler" "github.com/perlin-network/life/exec" "github.com/rogpeppe/go-internal/txtar" "within.website/olin/abi/cwa" ) var ( ErrNoCWAMain = errors.New("executor: no cwa_main function defined") ) type Config struct { exec.VMConfig Name string FuncName string Env map[string]string Binary []byte Stdin io.Reader Stdout, Stderr io.Writer LogSink io.Writer } type Result struct { Status int64 GasUsed uint64 SyscallCount int64 ExecDur time.Duration PagesUsed int StartTime time.Time } func (r Result) ToFile() txtar.File { table := uitable.New() table.AddRow("Status", fmt.Sprint(r.Status)) table.AddRow("Gas Used", fmt.Sprint(r.GasUsed)) table.AddRow("Syscall Count", fmt.Sprint(r.SyscallCount)) table.AddRow("Exec Duration", r.ExecDur.String()) table.AddRow("Pages Used", fmt.Sprint(r.PagesUsed)) return txtar.File{ Name: "vmstats.txt", Data: []byte(table.String()), } } func Run(c Config) (*Result, error) { p := cwa.NewProcess(c.Name, nil, c.Env) p.Stdin = c.Stdin p.Stdout = c.Stdout p.Stderr = c.Stderr p.Logger = log.New(io.MultiWriter(c.LogSink, os.Stdout), c.Name+" ", log.LstdFlags) gp := &compiler.SimpleGasPolicy{GasPerInstruction: 1} vm, err := exec.NewVirtualMachine(c.Binary, c.VMConfig, p, gp) if err != nil { return nil, err } main, ok := vm.GetFunctionExport(c.FuncName) if !ok { return nil, ErrNoCWAMain } begin := time.Now() ret, err := vm.Run(main) if err != nil { return nil, err } dur := time.Since(begin) p.Logger.Printf("[SYSTEM] return status: %v", ret) return &Result{ Status: ret, GasUsed: vm.Gas, SyscallCount: p.SyscallCount(), ExecDur: dur, PagesUsed: len(vm.Memory) / 65536, StartTime: begin, }, nil }