vm: switch from Opcode to Instruction

Which matches the changes done in neo-storm and also make this codebase closer
to the `dev` branch (that also uses Instruction).
This commit is contained in:
Roman Khimov 2019-08-14 15:40:31 +03:00
parent 5f3256d0e0
commit 33c512032f
15 changed files with 482 additions and 483 deletions

View file

@ -35,7 +35,7 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*Block, error) {
NextConsensus: nextConsensus, NextConsensus: nextConsensus,
Script: &transaction.Witness{ Script: &transaction.Witness{
InvocationScript: []byte{}, InvocationScript: []byte{},
VerificationScript: []byte{byte(vm.Opusht)}, VerificationScript: []byte{byte(vm.PUSHT)},
}, },
} }
@ -82,7 +82,7 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*Block, error) {
Scripts: []*transaction.Witness{ Scripts: []*transaction.Witness{
{ {
InvocationScript: []byte{}, InvocationScript: []byte{},
VerificationScript: []byte{byte(vm.Opusht)}, VerificationScript: []byte{byte(vm.PUSHT)},
}, },
}, },
}, },
@ -97,7 +97,7 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*Block, error) {
} }
func governingTokenTX() *transaction.Transaction { func governingTokenTX() *transaction.Transaction {
admin, _ := util.Uint160FromScript([]byte{byte(vm.Opusht)}) admin, _ := util.Uint160FromScript([]byte{byte(vm.PUSHT)})
registerTX := &transaction.RegisterTX{ registerTX := &transaction.RegisterTX{
AssetType: transaction.GoverningToken, AssetType: transaction.GoverningToken,
Name: "[{\"lang\":\"zh-CN\",\"name\":\"小蚁股\"},{\"lang\":\"en\",\"name\":\"AntShare\"}]", Name: "[{\"lang\":\"zh-CN\",\"name\":\"小蚁股\"},{\"lang\":\"en\",\"name\":\"AntShare\"}]",
@ -120,7 +120,7 @@ func governingTokenTX() *transaction.Transaction {
} }
func utilityTokenTX() *transaction.Transaction { func utilityTokenTX() *transaction.Transaction {
admin, _ := util.Uint160FromScript([]byte{byte(vm.Opushf)}) admin, _ := util.Uint160FromScript([]byte{byte(vm.PUSHF)})
registerTX := &transaction.RegisterTX{ registerTX := &transaction.RegisterTX{
AssetType: transaction.UtilityToken, AssetType: transaction.UtilityToken,
Name: "[{\"lang\":\"zh-CN\",\"name\":\"小蚁币\"},{\"lang\":\"en\",\"name\":\"AntCoin\"}]", Name: "[{\"lang\":\"zh-CN\",\"name\":\"小蚁币\"},{\"lang\":\"en\",\"name\":\"AntCoin\"}]",

View file

@ -34,7 +34,7 @@ func CreateMultiSigRedeemScript(m int, publicKeys crypto.PublicKeys) ([]byte, er
if err := vm.EmitInt(buf, int64(len(publicKeys))); err != nil { if err := vm.EmitInt(buf, int64(len(publicKeys))); err != nil {
return nil, err return nil, err
} }
if err := vm.EmitOpcode(buf, vm.Ocheckmultisig); err != nil { if err := vm.EmitOpcode(buf, vm.CHECKMULTISIG); err != nil {
return nil, err return nil, err
} }

View file

@ -24,7 +24,7 @@ func TestCreateMultiSigRedeemScript(t *testing.T) {
buf := bytes.NewBuffer(out) buf := bytes.NewBuffer(out)
b, _ := buf.ReadByte() b, _ := buf.ReadByte()
assert.Equal(t, vm.Opush3, vm.Opcode(b)) assert.Equal(t, vm.PUSH3, vm.Instruction(b))
for i := 0; i < len(validators); i++ { for i := 0; i < len(validators); i++ {
b, err := util.ReadVarBytes(buf) b, err := util.ReadVarBytes(buf)
@ -35,7 +35,7 @@ func TestCreateMultiSigRedeemScript(t *testing.T) {
} }
b, _ = buf.ReadByte() b, _ = buf.ReadByte()
assert.Equal(t, vm.Opush3, vm.Opcode(b)) assert.Equal(t, vm.PUSH3, vm.Instruction(b))
b, _ = buf.ReadByte() b, _ = buf.ReadByte()
assert.Equal(t, vm.Ocheckmultisig, vm.Opcode(b)) assert.Equal(t, vm.CHECKMULTISIG, vm.Instruction(b))
} }

View file

@ -87,18 +87,18 @@ func (c *codegen) emitLoadLocal(name string) {
} }
func (c *codegen) emitLoadLocalPos(pos int) { func (c *codegen) emitLoadLocalPos(pos int) {
emitOpcode(c.prog, vm.Ofromaltstack) emitOpcode(c.prog, vm.FROMALTSTACK)
emitOpcode(c.prog, vm.Odup) emitOpcode(c.prog, vm.DUP)
emitOpcode(c.prog, vm.Otoaltstack) emitOpcode(c.prog, vm.TOALTSTACK)
emitInt(c.prog, int64(pos)) emitInt(c.prog, int64(pos))
emitOpcode(c.prog, vm.Opickitem) emitOpcode(c.prog, vm.PICKITEM)
} }
func (c *codegen) emitStoreLocal(pos int) { func (c *codegen) emitStoreLocal(pos int) {
emitOpcode(c.prog, vm.Ofromaltstack) emitOpcode(c.prog, vm.FROMALTSTACK)
emitOpcode(c.prog, vm.Odup) emitOpcode(c.prog, vm.DUP)
emitOpcode(c.prog, vm.Otoaltstack) emitOpcode(c.prog, vm.TOALTSTACK)
if pos < 0 { if pos < 0 {
log.Fatalf("invalid position to store local: %d", pos) log.Fatalf("invalid position to store local: %d", pos)
@ -106,19 +106,19 @@ func (c *codegen) emitStoreLocal(pos int) {
emitInt(c.prog, int64(pos)) emitInt(c.prog, int64(pos))
emitInt(c.prog, 2) emitInt(c.prog, 2)
emitOpcode(c.prog, vm.Oroll) emitOpcode(c.prog, vm.ROLL)
emitOpcode(c.prog, vm.Osetitem) emitOpcode(c.prog, vm.SETITEM)
} }
func (c *codegen) emitLoadField(i int) { func (c *codegen) emitLoadField(i int) {
emitInt(c.prog, int64(i)) emitInt(c.prog, int64(i))
emitOpcode(c.prog, vm.Opickitem) emitOpcode(c.prog, vm.PICKITEM)
} }
func (c *codegen) emitStoreStructField(i int) { func (c *codegen) emitStoreStructField(i int) {
emitInt(c.prog, int64(i)) emitInt(c.prog, int64(i))
emitOpcode(c.prog, vm.Orot) emitOpcode(c.prog, vm.ROT)
emitOpcode(c.prog, vm.Osetitem) emitOpcode(c.prog, vm.SETITEM)
} }
// convertGlobals will traverse the AST and only convert global declarations. // convertGlobals will traverse the AST and only convert global declarations.
@ -155,8 +155,8 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl) {
// All globals copied into the scope of the function need to be added // All globals copied into the scope of the function need to be added
// to the stack size of the function. // to the stack size of the function.
emitInt(c.prog, f.stackSize()+countGlobals(file)) emitInt(c.prog, f.stackSize()+countGlobals(file))
emitOpcode(c.prog, vm.Onewarray) emitOpcode(c.prog, vm.NEWARRAY)
emitOpcode(c.prog, vm.Otoaltstack) emitOpcode(c.prog, vm.TOALTSTACK)
// We need to handle methods, which in Go, is just syntactic sugar. // We need to handle methods, which in Go, is just syntactic sugar.
// The method receiver will be passed in as first argument. // The method receiver will be passed in as first argument.
@ -193,9 +193,9 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl) {
// If this function returns the void (no return stmt) we will cleanup its junk on the stack. // If this function returns the void (no return stmt) we will cleanup its junk on the stack.
if !hasReturnStmt(decl) { if !hasReturnStmt(decl) {
emitOpcode(c.prog, vm.Ofromaltstack) emitOpcode(c.prog, vm.FROMALTSTACK)
emitOpcode(c.prog, vm.Odrop) emitOpcode(c.prog, vm.DROP)
emitOpcode(c.prog, vm.Oret) emitOpcode(c.prog, vm.RET)
} }
} }
@ -262,17 +262,17 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
// To be backwards compatible we will put them them in. // To be backwards compatible we will put them them in.
// See issue #65 (https://github.com/CityOfZion/neo-go/issues/65) // See issue #65 (https://github.com/CityOfZion/neo-go/issues/65)
l := c.newLabel() l := c.newLabel()
emitJmp(c.prog, vm.Ojmp, int16(l)) emitJmp(c.prog, vm.JMP, int16(l))
c.setLabel(l) c.setLabel(l)
if len(n.Results) > 0 { if len(n.Results) > 0 {
ast.Walk(c, n.Results[0]) ast.Walk(c, n.Results[0])
} }
emitOpcode(c.prog, vm.Onop) // @OPTIMIZE emitOpcode(c.prog, vm.NOP) // @OPTIMIZE
emitOpcode(c.prog, vm.Ofromaltstack) emitOpcode(c.prog, vm.FROMALTSTACK)
emitOpcode(c.prog, vm.Odrop) // Cleanup the stack. emitOpcode(c.prog, vm.DROP) // Cleanup the stack.
emitOpcode(c.prog, vm.Oret) emitOpcode(c.prog, vm.RET)
return nil return nil
case *ast.IfStmt: case *ast.IfStmt:
@ -280,7 +280,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
lElse := c.newLabel() lElse := c.newLabel()
if n.Cond != nil { if n.Cond != nil {
ast.Walk(c, n.Cond) ast.Walk(c, n.Cond)
emitJmp(c.prog, vm.Ojmpifnot, int16(lElse)) emitJmp(c.prog, vm.JMPIFNOT, int16(lElse))
} }
c.setLabel(lIf) c.setLabel(lIf)
@ -288,7 +288,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
if n.Else != nil { if n.Else != nil {
// TODO: handle else statements. // TODO: handle else statements.
// emitJmp(c.prog, vm.Ojmp, int16(lEnd)) // emitJmp(c.prog, vm.JMP, int16(lEnd))
} }
c.setLabel(lElse) c.setLabel(lElse)
if n.Else != nil { if n.Else != nil {
@ -327,7 +327,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.emitLoadConst(c.typeInfo.Types[n.Elts[i]]) c.emitLoadConst(c.typeInfo.Types[n.Elts[i]])
} }
emitInt(c.prog, int64(ln)) emitInt(c.prog, int64(ln))
emitOpcode(c.prog, vm.Opack) emitOpcode(c.prog, vm.PACK)
return nil return nil
} }
@ -342,13 +342,13 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
switch n.Op { switch n.Op {
case token.LAND: case token.LAND:
ast.Walk(c, n.X) ast.Walk(c, n.X)
emitJmp(c.prog, vm.Ojmpifnot, int16(len(c.l)-1)) emitJmp(c.prog, vm.JMPIFNOT, int16(len(c.l)-1))
ast.Walk(c, n.Y) ast.Walk(c, n.Y)
return nil return nil
case token.LOR: case token.LOR:
ast.Walk(c, n.X) ast.Walk(c, n.X)
emitJmp(c.prog, vm.Ojmpif, int16(len(c.l)-2)) emitJmp(c.prog, vm.JMPIF, int16(len(c.l)-2))
ast.Walk(c, n.Y) ast.Walk(c, n.Y)
return nil return nil
@ -414,18 +414,18 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
// Do not swap for builtin functions. // Do not swap for builtin functions.
if !isBuiltin { if !isBuiltin {
if numArgs == 2 { if numArgs == 2 {
emitOpcode(c.prog, vm.Oswap) emitOpcode(c.prog, vm.SWAP)
} }
if numArgs == 3 { if numArgs == 3 {
emitInt(c.prog, 2) emitInt(c.prog, 2)
emitOpcode(c.prog, vm.Oxswap) emitOpcode(c.prog, vm.XSWAP)
} }
} }
// c# compiler adds a NOP (0x61) before every function call. Dont think its relevant // c# compiler adds a NOP (0x61) before every function call. Dont think its relevant
// and we could easily removed it, but to be consistent with the original compiler I // and we could easily removed it, but to be consistent with the original compiler I
// will put them in. ^^ // will put them in. ^^
emitOpcode(c.prog, vm.Onop) emitOpcode(c.prog, vm.NOP)
// Check builtin first to avoid nil pointer on funcScope! // Check builtin first to avoid nil pointer on funcScope!
switch { switch {
@ -436,13 +436,13 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
case isSyscall(f.name): case isSyscall(f.name):
c.convertSyscall(f.name) c.convertSyscall(f.name)
default: default:
emitCall(c.prog, vm.Ocall, int16(f.label)) emitCall(c.prog, vm.CALL, int16(f.label))
} }
// If we are not assigning this function to a variable we need to drop // If we are not assigning this function to a variable we need to drop
// (cleanup) the top stack item. It's not a void but you get the point \o/. // (cleanup) the top stack item. It's not a void but you get the point \o/.
if _, ok := c.scope.voidCalls[n]; ok { if _, ok := c.scope.voidCalls[n]; ok {
emitOpcode(c.prog, vm.Odrop) emitOpcode(c.prog, vm.DROP)
} }
return nil return nil
@ -490,7 +490,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.emitLoadField(int(val)) c.emitLoadField(int(val))
default: default:
ast.Walk(c, n.Index) ast.Walk(c, n.Index)
emitOpcode(c.prog, vm.Opickitem) // just pickitem here emitOpcode(c.prog, vm.PICKITEM) // just pickitem here
} }
return nil return nil
@ -508,14 +508,14 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
ast.Walk(c, n.Cond) ast.Walk(c, n.Cond)
// Jump if the condition is false // Jump if the condition is false
emitJmp(c.prog, vm.Ojmpifnot, int16(fend)) emitJmp(c.prog, vm.JMPIFNOT, int16(fend))
// Walk body followed by the iterator (post stmt). // Walk body followed by the iterator (post stmt).
ast.Walk(c, n.Body) ast.Walk(c, n.Body)
ast.Walk(c, n.Post) ast.Walk(c, n.Post)
// Jump back to condition. // Jump back to condition.
emitJmp(c.prog, vm.Ojmp, int16(fstart)) emitJmp(c.prog, vm.JMP, int16(fstart))
c.setLabel(fend) c.setLabel(fend)
return nil return nil
@ -537,7 +537,7 @@ func (c *codegen) convertSyscall(name string) {
log.Fatalf("unknown VM syscall api: %s", name) log.Fatalf("unknown VM syscall api: %s", name)
} }
emitSyscall(c.prog, api) emitSyscall(c.prog, api)
emitOpcode(c.prog, vm.Onop) // @OPTIMIZE emitOpcode(c.prog, vm.NOP) // @OPTIMIZE
} }
func (c *codegen) convertBuiltin(expr *ast.CallExpr) { func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
@ -554,20 +554,20 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
arg := expr.Args[0] arg := expr.Args[0]
typ := c.typeInfo.Types[arg].Type typ := c.typeInfo.Types[arg].Type
if isStringType(typ) { if isStringType(typ) {
emitOpcode(c.prog, vm.Osize) emitOpcode(c.prog, vm.SIZE)
} else { } else {
emitOpcode(c.prog, vm.Oarraysize) emitOpcode(c.prog, vm.ARRAYSIZE)
} }
case "append": case "append":
emitOpcode(c.prog, vm.Oappend) emitOpcode(c.prog, vm.APPEND)
case "SHA256": case "SHA256":
emitOpcode(c.prog, vm.Osha256) emitOpcode(c.prog, vm.SHA256)
case "SHA1": case "SHA1":
emitOpcode(c.prog, vm.Osha1) emitOpcode(c.prog, vm.SHA1)
case "Hash256": case "Hash256":
emitOpcode(c.prog, vm.Ohash256) emitOpcode(c.prog, vm.HASH256)
case "Hash160": case "Hash160":
emitOpcode(c.prog, vm.Ohash160) emitOpcode(c.prog, vm.HASH160)
case "FromAddress": case "FromAddress":
// We can be sure that this is a ast.BasicLit just containing a simple // We can be sure that this is a ast.BasicLit just containing a simple
// address string. Note that the string returned from calling Value will // address string. Note that the string returned from calling Value will
@ -601,10 +601,10 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
log.Fatalf("the given literal is not of type struct: %v", lit) log.Fatalf("the given literal is not of type struct: %v", lit)
} }
emitOpcode(c.prog, vm.Onop) emitOpcode(c.prog, vm.NOP)
emitInt(c.prog, int64(strct.NumFields())) emitInt(c.prog, int64(strct.NumFields()))
emitOpcode(c.prog, vm.Onewstruct) emitOpcode(c.prog, vm.NEWSTRUCT)
emitOpcode(c.prog, vm.Otoaltstack) emitOpcode(c.prog, vm.TOALTSTACK)
// We need to locally store all the fields, even if they are not initialized. // We need to locally store all the fields, even if they are not initialized.
// We will initialize all fields to their "zero" value. // We will initialize all fields to their "zero" value.
@ -633,45 +633,45 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
c.emitLoadConst(typeAndVal) c.emitLoadConst(typeAndVal)
c.emitStoreLocal(i) c.emitStoreLocal(i)
} }
emitOpcode(c.prog, vm.Ofromaltstack) emitOpcode(c.prog, vm.FROMALTSTACK)
} }
func (c *codegen) convertToken(tok token.Token) { func (c *codegen) convertToken(tok token.Token) {
switch tok { switch tok {
case token.ADD_ASSIGN: case token.ADD_ASSIGN:
emitOpcode(c.prog, vm.Oadd) emitOpcode(c.prog, vm.ADD)
case token.SUB_ASSIGN: case token.SUB_ASSIGN:
emitOpcode(c.prog, vm.Osub) emitOpcode(c.prog, vm.SUB)
case token.MUL_ASSIGN: case token.MUL_ASSIGN:
emitOpcode(c.prog, vm.Omul) emitOpcode(c.prog, vm.MUL)
case token.QUO_ASSIGN: case token.QUO_ASSIGN:
emitOpcode(c.prog, vm.Odiv) emitOpcode(c.prog, vm.DIV)
case token.ADD: case token.ADD:
emitOpcode(c.prog, vm.Oadd) emitOpcode(c.prog, vm.ADD)
case token.SUB: case token.SUB:
emitOpcode(c.prog, vm.Osub) emitOpcode(c.prog, vm.SUB)
case token.MUL: case token.MUL:
emitOpcode(c.prog, vm.Omul) emitOpcode(c.prog, vm.MUL)
case token.QUO: case token.QUO:
emitOpcode(c.prog, vm.Odiv) emitOpcode(c.prog, vm.DIV)
case token.LSS: case token.LSS:
emitOpcode(c.prog, vm.Olt) emitOpcode(c.prog, vm.LT)
case token.LEQ: case token.LEQ:
emitOpcode(c.prog, vm.Olte) emitOpcode(c.prog, vm.LTE)
case token.GTR: case token.GTR:
emitOpcode(c.prog, vm.Ogt) emitOpcode(c.prog, vm.GT)
case token.GEQ: case token.GEQ:
emitOpcode(c.prog, vm.Ogte) emitOpcode(c.prog, vm.GTE)
case token.EQL: case token.EQL:
emitOpcode(c.prog, vm.Onumequal) emitOpcode(c.prog, vm.NUMEQUAL)
case token.NEQ: case token.NEQ:
emitOpcode(c.prog, vm.Onumnotequal) emitOpcode(c.prog, vm.NUMNOTEQUAL)
case token.DEC: case token.DEC:
emitOpcode(c.prog, vm.Odec) emitOpcode(c.prog, vm.DEC)
case token.INC: case token.INC:
emitOpcode(c.prog, vm.Oinc) emitOpcode(c.prog, vm.INC)
case token.NOT: case token.NOT:
emitOpcode(c.prog, vm.Onot) emitOpcode(c.prog, vm.NOT)
default: default:
log.Fatalf("compiler could not convert token: %s", tok) log.Fatalf("compiler could not convert token: %s", tok)
} }
@ -750,8 +750,8 @@ func (c *codegen) writeJumps() {
b := c.prog.Bytes() b := c.prog.Bytes()
for i, op := range b { for i, op := range b {
j := i + 1 j := i + 1
switch vm.Opcode(op) { switch vm.Instruction(op) {
case vm.Ojmp, vm.Ojmpifnot, vm.Ojmpif, vm.Ocall: case vm.JMP, vm.JMPIFNOT, vm.JMPIF, vm.CALL:
index := int16(binary.LittleEndian.Uint16(b[j : j+2])) index := int16(binary.LittleEndian.Uint16(b[j : j+2]))
if int(index) > len(c.l) || int(index) < 0 { if int(index) > len(c.l) || int(index) < 0 {
continue continue

View file

@ -112,7 +112,7 @@ func DumpOpcode(src string) error {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0) w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
fmt.Fprintln(w, "INDEX\tOPCODE\tDESC\t") fmt.Fprintln(w, "INDEX\tOPCODE\tDESC\t")
for i := 0; i < len(b); i++ { for i := 0; i < len(b); i++ {
fmt.Fprintf(w, "%d\t0x%2x\t%s\t\n", i, b[i], vm.Opcode(b[i])) fmt.Fprintf(w, "%d\t0x%2x\t%s\t\n", i, b[i], vm.Instruction(b[i]))
} }
w.Flush() w.Flush()
return nil return nil

View file

@ -12,7 +12,7 @@ import (
"github.com/CityOfZion/neo-go/pkg/vm" "github.com/CityOfZion/neo-go/pkg/vm"
) )
func emit(w *bytes.Buffer, op vm.Opcode, b []byte) error { func emit(w *bytes.Buffer, op vm.Instruction, b []byte) error {
if err := w.WriteByte(byte(op)); err != nil { if err := w.WriteByte(byte(op)); err != nil {
return err return err
} }
@ -20,26 +20,26 @@ func emit(w *bytes.Buffer, op vm.Opcode, b []byte) error {
return err return err
} }
func emitOpcode(w io.ByteWriter, op vm.Opcode) error { func emitOpcode(w io.ByteWriter, op vm.Instruction) error {
return w.WriteByte(byte(op)) return w.WriteByte(byte(op))
} }
func emitBool(w io.ByteWriter, ok bool) error { func emitBool(w io.ByteWriter, ok bool) error {
if ok { if ok {
return emitOpcode(w, vm.Opusht) return emitOpcode(w, vm.PUSHT)
} }
return emitOpcode(w, vm.Opushf) return emitOpcode(w, vm.PUSHF)
} }
func emitInt(w *bytes.Buffer, i int64) error { func emitInt(w *bytes.Buffer, i int64) error {
if i == -1 { if i == -1 {
return emitOpcode(w, vm.Opushm1) return emitOpcode(w, vm.PUSHM1)
} }
if i == 0 { if i == 0 {
return emitOpcode(w, vm.Opushf) return emitOpcode(w, vm.PUSHF)
} }
if i > 0 && i < 16 { if i > 0 && i < 16 {
val := vm.Opcode(int(vm.Opush1) - 1 + int(i)) val := vm.Instruction(int(vm.PUSH1) - 1 + int(i))
return emitOpcode(w, val) return emitOpcode(w, val)
} }
@ -59,18 +59,18 @@ func emitBytes(w *bytes.Buffer, b []byte) error {
) )
switch { switch {
case n <= int(vm.Opushbytes75): case n <= int(vm.PUSHBYTES75):
return emit(w, vm.Opcode(n), b) return emit(w, vm.Instruction(n), b)
case n < 0x100: case n < 0x100:
err = emit(w, vm.Opushdata1, []byte{byte(n)}) err = emit(w, vm.PUSHDATA1, []byte{byte(n)})
case n < 0x10000: case n < 0x10000:
buf := make([]byte, 2) buf := make([]byte, 2)
binary.LittleEndian.PutUint16(buf, uint16(n)) binary.LittleEndian.PutUint16(buf, uint16(n))
err = emit(w, vm.Opushdata2, buf) err = emit(w, vm.PUSHDATA2, buf)
default: default:
buf := make([]byte, 4) buf := make([]byte, 4)
binary.LittleEndian.PutUint32(buf, uint32(n)) binary.LittleEndian.PutUint32(buf, uint32(n))
err = emit(w, vm.Opushdata4, buf) err = emit(w, vm.PUSHDATA4, buf)
} }
if err != nil { if err != nil {
return err return err
@ -86,15 +86,15 @@ func emitSyscall(w *bytes.Buffer, api string) error {
buf := make([]byte, len(api)+1) buf := make([]byte, len(api)+1)
buf[0] = byte(len(api)) buf[0] = byte(len(api))
copy(buf[1:], []byte(api)) copy(buf[1:], []byte(api))
return emit(w, vm.Osyscall, buf) return emit(w, vm.SYSCALL, buf)
} }
func emitCall(w *bytes.Buffer, op vm.Opcode, label int16) error { func emitCall(w *bytes.Buffer, op vm.Instruction, label int16) error {
return emitJmp(w, op, label) return emitJmp(w, op, label)
} }
func emitJmp(w *bytes.Buffer, op vm.Opcode, label int16) error { func emitJmp(w *bytes.Buffer, op vm.Instruction, label int16) error {
if !isOpcodeJmp(op) { if !isInstructionJmp(op) {
return fmt.Errorf("opcode %s is not a jump or call type", op) return fmt.Errorf("opcode %s is not a jump or call type", op)
} }
buf := make([]byte, 2) buf := make([]byte, 2)
@ -102,8 +102,8 @@ func emitJmp(w *bytes.Buffer, op vm.Opcode, label int16) error {
return emit(w, op, buf) return emit(w, op, buf)
} }
func isOpcodeJmp(op vm.Opcode) bool { func isInstructionJmp(op vm.Instruction) bool {
if op == vm.Ojmp || op == vm.Ojmpifnot || op == vm.Ojmpif || op == vm.Ocall { if op == vm.JMP || op == vm.JMPIFNOT || op == vm.JMPIF || op == vm.CALL {
return true return true
} }
return false return false

View file

@ -26,12 +26,12 @@ func NewContext(b []byte) *Context {
} }
// Next return the next instruction to execute. // Next return the next instruction to execute.
func (c *Context) Next() Opcode { func (c *Context) Next() Instruction {
c.ip++ c.ip++
if c.ip >= len(c.prog) { if c.ip >= len(c.prog) {
return Oret return RET
} }
return Opcode(c.prog[c.ip]) return Instruction(c.prog[c.ip])
} }
// IP returns the absolute instruction without taking 0 into account. // IP returns the absolute instruction without taking 0 into account.
@ -47,11 +47,11 @@ func (c *Context) LenInstr() int {
} }
// CurrInstr returns the current instruction and opcode. // CurrInstr returns the current instruction and opcode.
func (c *Context) CurrInstr() (int, Opcode) { func (c *Context) CurrInstr() (int, Instruction) {
if c.ip < 0 { if c.ip < 0 {
return c.ip, Opcode(0x00) return c.ip, Instruction(0x00)
} }
return c.ip, Opcode(c.prog[c.ip]) return c.ip, Instruction(c.prog[c.ip])
} }
// Copy returns an new exact copy of c. // Copy returns an new exact copy of c.

View file

@ -11,8 +11,8 @@ import (
"github.com/CityOfZion/neo-go/pkg/util" "github.com/CityOfZion/neo-go/pkg/util"
) )
// Emit a VM Opcode with data to the given buffer. // Emit a VM Instruction with data to the given buffer.
func Emit(w *bytes.Buffer, op Opcode, b []byte) error { func Emit(w *bytes.Buffer, op Instruction, b []byte) error {
if err := w.WriteByte(byte(op)); err != nil { if err := w.WriteByte(byte(op)); err != nil {
return err return err
} }
@ -20,29 +20,29 @@ func Emit(w *bytes.Buffer, op Opcode, b []byte) error {
return err return err
} }
// EmitOpcode emits a single VM Opcode the given buffer. // EmitOpcode emits a single VM Instruction the given buffer.
func EmitOpcode(w io.ByteWriter, op Opcode) error { func EmitOpcode(w io.ByteWriter, op Instruction) error {
return w.WriteByte(byte(op)) return w.WriteByte(byte(op))
} }
// EmitBool emits a bool type the given buffer. // EmitBool emits a bool type the given buffer.
func EmitBool(w io.ByteWriter, ok bool) error { func EmitBool(w io.ByteWriter, ok bool) error {
if ok { if ok {
return EmitOpcode(w, Opusht) return EmitOpcode(w, PUSHT)
} }
return EmitOpcode(w, Opushf) return EmitOpcode(w, PUSHF)
} }
// EmitInt emits a int type to the given buffer. // EmitInt emits a int type to the given buffer.
func EmitInt(w *bytes.Buffer, i int64) error { func EmitInt(w *bytes.Buffer, i int64) error {
if i == -1 { if i == -1 {
return EmitOpcode(w, Opushm1) return EmitOpcode(w, PUSHM1)
} }
if i == 0 { if i == 0 {
return EmitOpcode(w, Opushf) return EmitOpcode(w, PUSHF)
} }
if i > 0 && i < 16 { if i > 0 && i < 16 {
val := Opcode(int(Opush1) - 1 + int(i)) val := Instruction(int(PUSH1) - 1 + int(i))
return EmitOpcode(w, val) return EmitOpcode(w, val)
} }
@ -63,18 +63,18 @@ func EmitBytes(w *bytes.Buffer, b []byte) error {
n = len(b) n = len(b)
) )
if n <= int(Opushbytes75) { if n <= int(PUSHBYTES75) {
return Emit(w, Opcode(n), b) return Emit(w, Instruction(n), b)
} else if n < 0x100 { } else if n < 0x100 {
err = Emit(w, Opushdata1, []byte{byte(n)}) err = Emit(w, PUSHDATA1, []byte{byte(n)})
} else if n < 0x10000 { } else if n < 0x10000 {
buf := make([]byte, 2) buf := make([]byte, 2)
binary.LittleEndian.PutUint16(buf, uint16(n)) binary.LittleEndian.PutUint16(buf, uint16(n))
err = Emit(w, Opushdata2, buf) err = Emit(w, PUSHDATA2, buf)
} else { } else {
buf := make([]byte, 4) buf := make([]byte, 4)
binary.LittleEndian.PutUint32(buf, uint32(n)) binary.LittleEndian.PutUint32(buf, uint32(n))
err = Emit(w, Opushdata4, buf) err = Emit(w, PUSHDATA4, buf)
} }
if err != nil { if err != nil {
return err return err
@ -92,17 +92,17 @@ func EmitSyscall(w *bytes.Buffer, api string) error {
buf := make([]byte, len(api)+1) buf := make([]byte, len(api)+1)
buf[0] = byte(len(api)) buf[0] = byte(len(api))
copy(buf[1:], []byte(api)) copy(buf[1:], []byte(api))
return Emit(w, Osyscall, buf) return Emit(w, SYSCALL, buf)
} }
// EmitCall emits a call Opcode with label to the given buffer. // EmitCall emits a call Instruction with label to the given buffer.
func EmitCall(w *bytes.Buffer, op Opcode, label int16) error { func EmitCall(w *bytes.Buffer, op Instruction, label int16) error {
return EmitJmp(w, op, label) return EmitJmp(w, op, label)
} }
// EmitJmp emits a jump Opcode along with label to the given buffer. // EmitJmp emits a jump Instruction along with label to the given buffer.
func EmitJmp(w *bytes.Buffer, op Opcode, label int16) error { func EmitJmp(w *bytes.Buffer, op Instruction, label int16) error {
if !isOpcodeJmp(op) { if !isInstructionJmp(op) {
return fmt.Errorf("opcode %s is not a jump or call type", op.String()) return fmt.Errorf("opcode %s is not a jump or call type", op.String())
} }
buf := make([]byte, 2) buf := make([]byte, 2)
@ -113,9 +113,9 @@ func EmitJmp(w *bytes.Buffer, op Opcode, label int16) error {
// EmitAppCall emits an appcall, if tailCall is true, tailCall opcode will be // EmitAppCall emits an appcall, if tailCall is true, tailCall opcode will be
// emitted instead. // emitted instead.
func EmitAppCall(w *bytes.Buffer, scriptHash util.Uint160, tailCall bool) error { func EmitAppCall(w *bytes.Buffer, scriptHash util.Uint160, tailCall bool) error {
op := Oappcall op := APPCALL
if tailCall { if tailCall {
op = Otailcall op = TAILCALL
} }
return Emit(w, op, scriptHash.Bytes()) return Emit(w, op, scriptHash.Bytes())
} }
@ -142,8 +142,8 @@ func EmitAppCallWithOperation(w *bytes.Buffer, scriptHash util.Uint160, operatio
return EmitAppCall(w, scriptHash, false) return EmitAppCall(w, scriptHash, false)
} }
func isOpcodeJmp(op Opcode) bool { func isInstructionJmp(op Instruction) bool {
if op == Ojmp || op == Ojmpifnot || op == Ojmpif || op == Ocall { if op == JMP || op == JMPIFNOT || op == JMPIF || op == CALL {
return true return true
} }
return false return false

View file

@ -11,7 +11,7 @@ import (
func TestEmitInt(t *testing.T) { func TestEmitInt(t *testing.T) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
EmitInt(buf, 10) EmitInt(buf, 10)
assert.Equal(t, Opcode(buf.Bytes()[0]), Opush10) assert.Equal(t, Instruction(buf.Bytes()[0]), PUSH10)
buf.Reset() buf.Reset()
EmitInt(buf, 100) EmitInt(buf, 100)
assert.Equal(t, buf.Bytes()[0], uint8(1)) assert.Equal(t, buf.Bytes()[0], uint8(1))
@ -26,8 +26,8 @@ func TestEmitBool(t *testing.T) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
EmitBool(buf, true) EmitBool(buf, true)
EmitBool(buf, false) EmitBool(buf, false)
assert.Equal(t, Opcode(buf.Bytes()[0]), Opush1) assert.Equal(t, Instruction(buf.Bytes()[0]), PUSH1)
assert.Equal(t, Opcode(buf.Bytes()[1]), Opush0) assert.Equal(t, Instruction(buf.Bytes()[1]), PUSH0)
} }
func TestEmitString(t *testing.T) { func TestEmitString(t *testing.T) {
@ -48,7 +48,7 @@ func TestEmitSyscall(t *testing.T) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
for _, syscall := range syscalls { for _, syscall := range syscalls {
EmitSyscall(buf, syscall) EmitSyscall(buf, syscall)
assert.Equal(t, Opcode(buf.Bytes()[0]), Osyscall) assert.Equal(t, Instruction(buf.Bytes()[0]), SYSCALL)
assert.Equal(t, buf.Bytes()[1], uint8(len(syscall))) assert.Equal(t, buf.Bytes()[1], uint8(len(syscall)))
assert.Equal(t, buf.Bytes()[2:], []byte(syscall)) assert.Equal(t, buf.Bytes()[2:], []byte(syscall))
buf.Reset() buf.Reset()
@ -57,8 +57,8 @@ func TestEmitSyscall(t *testing.T) {
func TestEmitCall(t *testing.T) { func TestEmitCall(t *testing.T) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
EmitCall(buf, Ojmp, 100) EmitCall(buf, JMP, 100)
assert.Equal(t, Opcode(buf.Bytes()[0]), Ojmp) assert.Equal(t, Instruction(buf.Bytes()[0]), JMP)
label := binary.LittleEndian.Uint16(buf.Bytes()[1:3]) label := binary.LittleEndian.Uint16(buf.Bytes()[1:3])
assert.Equal(t, label, uint16(100)) assert.Equal(t, label, uint16(100))
} }

View file

@ -0,0 +1,118 @@
// Code generated by "stringer -type=Instruction"; DO NOT EDIT.
package vm
import "strconv"
const _Instruction_name = "PUSH0PUSHBYTES1PUSHBYTES75PUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPIFJMPIFNOTCALLRETAPPCALLSYSCALLTAILCALLDUPFROMALTSTACKTOALTSTACKFROMALTSTACKXDROPXSWAPXTUCKDEPTHDROPDUPNIPOVERPICKROLLROTSWAPTUCKCATSUBSTRLEFTRIGHTSIZEINVERTANDORXOREQUALINCDECSIGNNEGATEABSNOTNZADDSUBMULDIVMODSHLSHRBOOLANDBOOLORNUMEQUALNUMNOTEQUALLTGTLTEGTEMINMAXWITHINSHA1SHA256HASH160HASH256CHECKSIGCHECKMULTISIGARRAYSIZEPACKUNPACKPICKITEMSETITEMNEWARRAYNEWSTRUCTAPPENDREVERSEREMOVETHROWTHROWIFNOT"
var _Instruction_map = map[Instruction]string{
0: _Instruction_name[0:5],
1: _Instruction_name[5:15],
75: _Instruction_name[15:26],
76: _Instruction_name[26:35],
77: _Instruction_name[35:44],
78: _Instruction_name[44:53],
79: _Instruction_name[53:59],
81: _Instruction_name[59:64],
82: _Instruction_name[64:69],
83: _Instruction_name[69:74],
84: _Instruction_name[74:79],
85: _Instruction_name[79:84],
86: _Instruction_name[84:89],
87: _Instruction_name[89:94],
88: _Instruction_name[94:99],
89: _Instruction_name[99:104],
90: _Instruction_name[104:110],
91: _Instruction_name[110:116],
92: _Instruction_name[116:122],
93: _Instruction_name[122:128],
94: _Instruction_name[128:134],
95: _Instruction_name[134:140],
96: _Instruction_name[140:146],
97: _Instruction_name[146:149],
98: _Instruction_name[149:152],
99: _Instruction_name[152:157],
100: _Instruction_name[157:165],
101: _Instruction_name[165:169],
102: _Instruction_name[169:172],
103: _Instruction_name[172:179],
104: _Instruction_name[179:186],
105: _Instruction_name[186:194],
106: _Instruction_name[194:209],
107: _Instruction_name[209:219],
108: _Instruction_name[219:231],
109: _Instruction_name[231:236],
114: _Instruction_name[236:241],
115: _Instruction_name[241:246],
116: _Instruction_name[246:251],
117: _Instruction_name[251:255],
118: _Instruction_name[255:258],
119: _Instruction_name[258:261],
120: _Instruction_name[261:265],
121: _Instruction_name[265:269],
122: _Instruction_name[269:273],
123: _Instruction_name[273:276],
124: _Instruction_name[276:280],
125: _Instruction_name[280:284],
126: _Instruction_name[284:287],
127: _Instruction_name[287:293],
128: _Instruction_name[293:297],
129: _Instruction_name[297:302],
130: _Instruction_name[302:306],
131: _Instruction_name[306:312],
132: _Instruction_name[312:315],
133: _Instruction_name[315:317],
134: _Instruction_name[317:320],
135: _Instruction_name[320:325],
139: _Instruction_name[325:328],
140: _Instruction_name[328:331],
141: _Instruction_name[331:335],
143: _Instruction_name[335:341],
144: _Instruction_name[341:344],
145: _Instruction_name[344:347],
146: _Instruction_name[347:349],
147: _Instruction_name[349:352],
148: _Instruction_name[352:355],
149: _Instruction_name[355:358],
150: _Instruction_name[358:361],
151: _Instruction_name[361:364],
152: _Instruction_name[364:367],
153: _Instruction_name[367:370],
154: _Instruction_name[370:377],
155: _Instruction_name[377:383],
156: _Instruction_name[383:391],
158: _Instruction_name[391:402],
159: _Instruction_name[402:404],
160: _Instruction_name[404:406],
161: _Instruction_name[406:409],
162: _Instruction_name[409:412],
163: _Instruction_name[412:415],
164: _Instruction_name[415:418],
165: _Instruction_name[418:424],
167: _Instruction_name[424:428],
168: _Instruction_name[428:434],
169: _Instruction_name[434:441],
170: _Instruction_name[441:448],
172: _Instruction_name[448:456],
174: _Instruction_name[456:469],
192: _Instruction_name[469:478],
193: _Instruction_name[478:482],
194: _Instruction_name[482:488],
195: _Instruction_name[488:496],
196: _Instruction_name[496:503],
197: _Instruction_name[503:511],
198: _Instruction_name[511:520],
200: _Instruction_name[520:526],
201: _Instruction_name[526:533],
202: _Instruction_name[533:539],
240: _Instruction_name[539:544],
241: _Instruction_name[544:554],
}
func (i Instruction) String() string {
if str, ok := _Instruction_map[i]; ok {
return str
}
return "Instruction(" + strconv.FormatInt(int64(i), 10) + ")"
}

130
pkg/vm/instructions.go Normal file
View file

@ -0,0 +1,130 @@
package vm
//go:generate stringer -type=Instruction
// Instruction represents an single operation for the NEO virtual machine.
type Instruction byte
// Viable list of supported instruction constants.
const (
// Constants
PUSH0 Instruction = 0x00
PUSHF Instruction = PUSH0
PUSHBYTES1 Instruction = 0x01
PUSHBYTES75 Instruction = 0x4B
PUSHDATA1 Instruction = 0x4C
PUSHDATA2 Instruction = 0x4D
PUSHDATA4 Instruction = 0x4E
PUSHM1 Instruction = 0x4F
PUSH1 Instruction = 0x51
PUSHT Instruction = PUSH1
PUSH2 Instruction = 0x52
PUSH3 Instruction = 0x53
PUSH4 Instruction = 0x54
PUSH5 Instruction = 0x55
PUSH6 Instruction = 0x56
PUSH7 Instruction = 0x57
PUSH8 Instruction = 0x58
PUSH9 Instruction = 0x59
PUSH10 Instruction = 0x5A
PUSH11 Instruction = 0x5B
PUSH12 Instruction = 0x5C
PUSH13 Instruction = 0x5D
PUSH14 Instruction = 0x5E
PUSH15 Instruction = 0x5F
PUSH16 Instruction = 0x60
// Flow control
NOP Instruction = 0x61
JMP Instruction = 0x62
JMPIF Instruction = 0x63
JMPIFNOT Instruction = 0x64
CALL Instruction = 0x65
RET Instruction = 0x66
APPCALL Instruction = 0x67
SYSCALL Instruction = 0x68
TAILCALL Instruction = 0x69
// Stack
DUPFROMALTSTACK Instruction = 0x6A
TOALTSTACK Instruction = 0x6B
FROMALTSTACK Instruction = 0x6C
XDROP Instruction = 0x6D
XSWAP Instruction = 0x72
XTUCK Instruction = 0x73
DEPTH Instruction = 0x74
DROP Instruction = 0x75
DUP Instruction = 0x76
NIP Instruction = 0x77
OVER Instruction = 0x78
PICK Instruction = 0x79
ROLL Instruction = 0x7A
ROT Instruction = 0x7B
SWAP Instruction = 0x7C
TUCK Instruction = 0x7D
// Splice
CAT Instruction = 0x7E
SUBSTR Instruction = 0x7F
LEFT Instruction = 0x80
RIGHT Instruction = 0x81
SIZE Instruction = 0x82
// Bitwise logic
INVERT Instruction = 0x83
AND Instruction = 0x84
OR Instruction = 0x85
XOR Instruction = 0x86
EQUAL Instruction = 0x87
// Arithmetic
INC Instruction = 0x8B
DEC Instruction = 0x8C
SIGN Instruction = 0x8D
NEGATE Instruction = 0x8F
ABS Instruction = 0x90
NOT Instruction = 0x91
NZ Instruction = 0x92
ADD Instruction = 0x93
SUB Instruction = 0x94
MUL Instruction = 0x95
DIV Instruction = 0x96
MOD Instruction = 0x97
SHL Instruction = 0x98
SHR Instruction = 0x99
BOOLAND Instruction = 0x9A
BOOLOR Instruction = 0x9B
NUMEQUAL Instruction = 0x9C
NUMNOTEQUAL Instruction = 0x9E
LT Instruction = 0x9F
GT Instruction = 0xA0
LTE Instruction = 0xA1
GTE Instruction = 0xA2
MIN Instruction = 0xA3
MAX Instruction = 0xA4
WITHIN Instruction = 0xA5
// Crypto
SHA1 Instruction = 0xA7
SHA256 Instruction = 0xA8
HASH160 Instruction = 0xA9
HASH256 Instruction = 0xAA
CHECKSIG Instruction = 0xAC
CHECKMULTISIG Instruction = 0xAE
// Array
ARRAYSIZE Instruction = 0xC0
PACK Instruction = 0xC1
UNPACK Instruction = 0xC2
PICKITEM Instruction = 0xC3
SETITEM Instruction = 0xC4
NEWARRAY Instruction = 0xC5
NEWSTRUCT Instruction = 0xC6
APPEND Instruction = 0xC8
REVERSE Instruction = 0xC9
REMOVE Instruction = 0xCA
// Exceptions
THROW Instruction = 0xF0
THROWIFNOT Instruction = 0xF1
)

View file

@ -1,131 +0,0 @@
package vm
//go:generate stringer -type=Opcode
// Opcode is an single operational instruction for the GO NEO virtual machine.
type Opcode byte
// List of supported opcodes.
const (
// Constants
Opush0 Opcode = 0x00 // An empty array of bytes is pushed onto the stack.
Opushf = Opush0
Opushbytes1 Opcode = 0x01 // 0x01-0x4B The next opcode bytes is data to be pushed onto the stack
Opushbytes75 Opcode = 0x4B
Opushdata1 Opcode = 0x4C // The next byte contains the number of bytes to be pushed onto the stack.
Opushdata2 Opcode = 0x4D // The next two bytes contain the number of bytes to be pushed onto the stack.
Opushdata4 Opcode = 0x4E // The next four bytes contain the number of bytes to be pushed onto the stack.
Opushm1 Opcode = 0x4F // The number -1 is pushed onto the stack.
Opush1 Opcode = 0x51
Opusht = Opush1
Opush2 Opcode = 0x52 // The number 2 is pushed onto the stack.
Opush3 Opcode = 0x53 // The number 3 is pushed onto the stack.
Opush4 Opcode = 0x54 // The number 4 is pushed onto the stack.
Opush5 Opcode = 0x55 // The number 5 is pushed onto the stack.
Opush6 Opcode = 0x56 // The number 6 is pushed onto the stack.
Opush7 Opcode = 0x57 // The number 7 is pushed onto the stack.
Opush8 Opcode = 0x58 // The number 8 is pushed onto the stack.
Opush9 Opcode = 0x59 // The number 9 is pushed onto the stack.
Opush10 Opcode = 0x5A // The number 10 is pushed onto the stack.
Opush11 Opcode = 0x5B // The number 11 is pushed onto the stack.
Opush12 Opcode = 0x5C // The number 12 is pushed onto the stack.
Opush13 Opcode = 0x5D // The number 13 is pushed onto the stack.
Opush14 Opcode = 0x5E // The number 14 is pushed onto the stack.
Opush15 Opcode = 0x5F // The number 15 is pushed onto the stack.
Opush16 Opcode = 0x60 // The number 16 is pushed onto the stack.
// Flow control
Onop Opcode = 0x61 // No operation.
Ojmp Opcode = 0x62
Ojmpif Opcode = 0x63
Ojmpifnot Opcode = 0x64
Ocall Opcode = 0x65
Oret Opcode = 0x66
Oappcall Opcode = 0x67
Osyscall Opcode = 0x68
Otailcall Opcode = 0x69
// The stack
Odupfromaltstack Opcode = 0x6A
Otoaltstack Opcode = 0x6B // Puts the input onto the top of the alt stack. Removes it from the main stack.
Ofromaltstack Opcode = 0x6C // Puts the input onto the top of the main stack. Removes it from the alt stack.
Oxdrop Opcode = 0x6D
Oxswap Opcode = 0x72
Oxtuck Opcode = 0x73
Odepth Opcode = 0x74 // Puts the number of stack items onto the stack.
Odrop Opcode = 0x75 // Removes the top stack item.
Odup Opcode = 0x76 // Duplicates the top stack item.
Onip Opcode = 0x77 // Removes the second-to-top stack item.
Oover Opcode = 0x78 // Copies the second-to-top stack item to the top.
Opick Opcode = 0x79 // The item n back in the stack is copied to the top.
Oroll Opcode = 0x7A // The item n back in the stack is moved to the top.
Orot Opcode = 0x7B // The top three items on the stack are rotated to the left.
Oswap Opcode = 0x7C // The top two items on the stack are swapped.
Otuck Opcode = 0x7D // The item at the top of the stack is copied and inserted before the second-to-top item.
// Splice
Ocat Opcode = 0x7E // Concatenates two strings.
Osubstr Opcode = 0x7F // Returns a section of a string.
Oleft Opcode = 0x80 // Keeps only characters left of the specified point in a string.
Oright Opcode = 0x81 // Keeps only characters right of the specified point in a string.
Osize Opcode = 0x82 // Returns the length of the input string.
// Bitwise logic
Oinvert Opcode = 0x83 // Flips all of the bits in the input.
Oand Opcode = 0x84 // Boolean and between each bit in the inputs.
Oor Opcode = 0x85 // Boolean or between each bit in the inputs.
Oxor Opcode = 0x86 // Boolean exclusive or between each bit in the inputs.
Oequal Opcode = 0x87 // Returns 1 if the inputs are exactly equal, 0 otherwise.
// Arithmetic
// Note: Arithmetic inputs are limited to signed 32-bit integers, but may overflow their output.
Oinc Opcode = 0x8B // 1 is added to the input.
Odec Opcode = 0x8C // 1 is subtracted from the input.
Osign Opcode = 0x8D
Onegate Opcode = 0x8F // The sign of the input is flipped.
Oabs Opcode = 0x90 // The input is made positive.
Onot Opcode = 0x91 // If the input is 0 or 1, it is flipped. Otherwise the output will be 0.
Onz Opcode = 0x92 // Returns 0 if the input is 0. 1 otherwise.
Oadd Opcode = 0x93 // a is added to b.
Osub Opcode = 0x94 // b is subtracted from a.
Omul Opcode = 0x95 // a is multiplied by b.
Odiv Opcode = 0x96 // a is divided by b.
Omod Opcode = 0x97 // Returns the remainder after dividing a by b.
Oshl Opcode = 0x98 // Shifts a left b bits, preserving sign.
Oshr Opcode = 0x99 // Shifts a right b bits, preserving sign.
Obooland Opcode = 0x9A // If both a and b are not 0, the output is 1. Otherwise 0.
Oboolor Opcode = 0x9B // If a or b is not 0, the output is 1. Otherwise 0.
Onumequal Opcode = 0x9C // Returns 1 if the numbers are equal, 0 otherwise.
Onumnotequal Opcode = 0x9E // Returns 1 if the numbers are not equal, 0 otherwise.
Olt Opcode = 0x9F // Returns 1 if a is less than b, 0 otherwise.
Ogt Opcode = 0xA0 // Returns 1 if a is greater than b, 0 otherwise.
Olte Opcode = 0xA1 // Returns 1 if a is less than or equal to b, 0 otherwise.
Ogte Opcode = 0xA2 // Returns 1 if a is greater than or equal to b, 0 otherwise.
Omin Opcode = 0xA3 // Returns the smaller of a and b.
Omax Opcode = 0xA4 // Returns the larger of a and b.
Owithin Opcode = 0xA5 // Returns 1 if x is within the specified range (left-inclusive), 0 otherwise.
// Crypto
Osha1 Opcode = 0xA7 // The input is hashed using SHA-1.
Osha256 Opcode = 0xA8 // The input is hashed using SHA-256.
Ohash160 Opcode = 0xA9
Ohash256 Opcode = 0xAA
Ochecksig Opcode = 0xAC
Ocheckmultisig Opcode = 0xAE
// array
Oarraysize Opcode = 0xC0
Opack Opcode = 0xC1
Ounpack Opcode = 0xC2
Opickitem Opcode = 0xC3
Osetitem Opcode = 0xC4
Onewarray Opcode = 0xC5 // Pops size from stack and creates a new array with that size, and pushes the array into the stack
Onewstruct Opcode = 0xC6
Oappend Opcode = 0xC8
Oreverse Opcode = 0xC9
Oremove Opcode = 0xCA
// exceptions
Othrow Opcode = 0xF0
Othrowifnot Opcode = 0xF1
)

View file

@ -1,118 +0,0 @@
// Code generated by "stringer -type=Opcode"; DO NOT EDIT.
package vm
import "strconv"
const _Opcode_name = "Opush0Opushbytes1Opushbytes75Opushdata1Opushdata2Opushdata4Opushm1Opush1Opush2Opush3Opush4Opush5Opush6Opush7Opush8Opush9Opush10Opush11Opush12Opush13Opush14Opush15Opush16OnopOjmpOjmpifOjmpifnotOcallOretOappcallOsyscallOtailcallOdupfromaltstackOtoaltstackOfromaltstackOxdropOxswapOxtuckOdepthOdropOdupOnipOoverOpickOrollOrotOswapOtuckOcatOsubstrOleftOrightOsizeOinvertOandOorOxorOequalOincOdecOsignOnegateOabsOnotOnzOaddOsubOmulOdivOmodOshlOshrOboolandOboolorOnumequalOnumnotequalOltOgtOlteOgteOminOmaxOwithinOsha1Osha256Ohash160Ohash256OchecksigOcheckmultisigOarraysizeOpackOunpackOpickitemOsetitemOnewarrayOnewstructOappendOreverseOremoveOthrowOthrowifnot"
var _Opcode_map = map[Opcode]string{
0: _Opcode_name[0:6],
1: _Opcode_name[6:17],
75: _Opcode_name[17:29],
76: _Opcode_name[29:39],
77: _Opcode_name[39:49],
78: _Opcode_name[49:59],
79: _Opcode_name[59:66],
81: _Opcode_name[66:72],
82: _Opcode_name[72:78],
83: _Opcode_name[78:84],
84: _Opcode_name[84:90],
85: _Opcode_name[90:96],
86: _Opcode_name[96:102],
87: _Opcode_name[102:108],
88: _Opcode_name[108:114],
89: _Opcode_name[114:120],
90: _Opcode_name[120:127],
91: _Opcode_name[127:134],
92: _Opcode_name[134:141],
93: _Opcode_name[141:148],
94: _Opcode_name[148:155],
95: _Opcode_name[155:162],
96: _Opcode_name[162:169],
97: _Opcode_name[169:173],
98: _Opcode_name[173:177],
99: _Opcode_name[177:183],
100: _Opcode_name[183:192],
101: _Opcode_name[192:197],
102: _Opcode_name[197:201],
103: _Opcode_name[201:209],
104: _Opcode_name[209:217],
105: _Opcode_name[217:226],
106: _Opcode_name[226:242],
107: _Opcode_name[242:253],
108: _Opcode_name[253:266],
109: _Opcode_name[266:272],
114: _Opcode_name[272:278],
115: _Opcode_name[278:284],
116: _Opcode_name[284:290],
117: _Opcode_name[290:295],
118: _Opcode_name[295:299],
119: _Opcode_name[299:303],
120: _Opcode_name[303:308],
121: _Opcode_name[308:313],
122: _Opcode_name[313:318],
123: _Opcode_name[318:322],
124: _Opcode_name[322:327],
125: _Opcode_name[327:332],
126: _Opcode_name[332:336],
127: _Opcode_name[336:343],
128: _Opcode_name[343:348],
129: _Opcode_name[348:354],
130: _Opcode_name[354:359],
131: _Opcode_name[359:366],
132: _Opcode_name[366:370],
133: _Opcode_name[370:373],
134: _Opcode_name[373:377],
135: _Opcode_name[377:383],
139: _Opcode_name[383:387],
140: _Opcode_name[387:391],
141: _Opcode_name[391:396],
143: _Opcode_name[396:403],
144: _Opcode_name[403:407],
145: _Opcode_name[407:411],
146: _Opcode_name[411:414],
147: _Opcode_name[414:418],
148: _Opcode_name[418:422],
149: _Opcode_name[422:426],
150: _Opcode_name[426:430],
151: _Opcode_name[430:434],
152: _Opcode_name[434:438],
153: _Opcode_name[438:442],
154: _Opcode_name[442:450],
155: _Opcode_name[450:457],
156: _Opcode_name[457:466],
158: _Opcode_name[466:478],
159: _Opcode_name[478:481],
160: _Opcode_name[481:484],
161: _Opcode_name[484:488],
162: _Opcode_name[488:492],
163: _Opcode_name[492:496],
164: _Opcode_name[496:500],
165: _Opcode_name[500:507],
167: _Opcode_name[507:512],
168: _Opcode_name[512:519],
169: _Opcode_name[519:527],
170: _Opcode_name[527:535],
172: _Opcode_name[535:544],
174: _Opcode_name[544:558],
192: _Opcode_name[558:568],
193: _Opcode_name[568:573],
194: _Opcode_name[573:580],
195: _Opcode_name[580:589],
196: _Opcode_name[589:597],
197: _Opcode_name[597:606],
198: _Opcode_name[606:616],
200: _Opcode_name[616:623],
201: _Opcode_name[623:631],
202: _Opcode_name[631:638],
240: _Opcode_name[638:644],
241: _Opcode_name[644:655],
}
func (i Opcode) String() string {
if str, ok := _Opcode_map[i]; ok {
return str
}
return "Opcode(" + strconv.FormatInt(int64(i), 10) + ")"
}

View file

@ -104,7 +104,7 @@ func (v *VM) PrintOps() {
} else { } else {
cursor = "" cursor = ""
} }
fmt.Fprintf(w, "%d\t0x%2x\t%s\t%s\n", i, prog[i], Opcode(prog[i]).String(), cursor) fmt.Fprintf(w, "%d\t0x%2x\t%s\t%s\n", i, prog[i], Instruction(prog[i]).String(), cursor)
} }
w.Flush() w.Flush()
@ -228,7 +228,7 @@ func (v *VM) Step() {
} }
// execute performs an instruction cycle in the VM. Acting on the instruction (opcode). // execute performs an instruction cycle in the VM. Acting on the instruction (opcode).
func (v *VM) execute(ctx *Context, op Opcode) { func (v *VM) execute(ctx *Context, op Instruction) {
// Instead of polluting the whole VM logic with error handling, we will recover // Instead of polluting the whole VM logic with error handling, we will recover
// each panic at a central point, putting the VM in a fault state. // each panic at a central point, putting the VM in a fault state.
defer func() { defer func() {
@ -239,57 +239,57 @@ func (v *VM) execute(ctx *Context, op Opcode) {
} }
}() }()
if op >= Opushbytes1 && op <= Opushbytes75 { if op >= PUSHBYTES1 && op <= PUSHBYTES75 {
b := ctx.readBytes(int(op)) b := ctx.readBytes(int(op))
v.estack.PushVal(b) v.estack.PushVal(b)
return return
} }
switch op { switch op {
case Opushm1, Opush1, Opush2, Opush3, Opush4, Opush5, case PUSHM1, PUSH1, PUSH2, PUSH3, PUSH4, PUSH5,
Opush6, Opush7, Opush8, Opush9, Opush10, Opush11, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11,
Opush12, Opush13, Opush14, Opush15, Opush16: PUSH12, PUSH13, PUSH14, PUSH15, PUSH16:
val := int(op) - int(Opush1) + 1 val := int(op) - int(PUSH1) + 1
v.estack.PushVal(val) v.estack.PushVal(val)
case Opush0: case PUSH0:
v.estack.PushVal(0) v.estack.PushVal(0)
case Opushdata1: case PUSHDATA1:
n := ctx.readByte() n := ctx.readByte()
b := ctx.readBytes(int(n)) b := ctx.readBytes(int(n))
v.estack.PushVal(b) v.estack.PushVal(b)
case Opushdata2: case PUSHDATA2:
n := ctx.readUint16() n := ctx.readUint16()
b := ctx.readBytes(int(n)) b := ctx.readBytes(int(n))
v.estack.PushVal(b) v.estack.PushVal(b)
case Opushdata4: case PUSHDATA4:
n := ctx.readUint32() n := ctx.readUint32()
b := ctx.readBytes(int(n)) b := ctx.readBytes(int(n))
v.estack.PushVal(b) v.estack.PushVal(b)
// Stack operations. // Stack operations.
case Otoaltstack: case TOALTSTACK:
v.astack.Push(v.estack.Pop()) v.astack.Push(v.estack.Pop())
case Ofromaltstack: case FROMALTSTACK:
v.estack.Push(v.astack.Pop()) v.estack.Push(v.astack.Pop())
case Odupfromaltstack: case DUPFROMALTSTACK:
v.estack.Push(v.astack.Dup(0)) v.estack.Push(v.astack.Dup(0))
case Odup: case DUP:
v.estack.Push(v.estack.Dup(0)) v.estack.Push(v.estack.Dup(0))
case Oswap: case SWAP:
a := v.estack.Pop() a := v.estack.Pop()
b := v.estack.Pop() b := v.estack.Pop()
v.estack.Push(a) v.estack.Push(a)
v.estack.Push(b) v.estack.Push(b)
case Oxswap: case XSWAP:
n := int(v.estack.Pop().BigInt().Int64()) n := int(v.estack.Pop().BigInt().Int64())
if n < 0 { if n < 0 {
panic("XSWAP: invalid length") panic("XSWAP: invalid length")
@ -305,7 +305,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
b.value = aval b.value = aval
} }
case Otuck: case TUCK:
n := int(v.estack.Pop().BigInt().Int64()) n := int(v.estack.Pop().BigInt().Int64())
if n <= 0 { if n <= 0 {
panic("OTUCK: invalid length") panic("OTUCK: invalid length")
@ -313,7 +313,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
v.estack.InsertAt(v.estack.Peek(0), n) v.estack.InsertAt(v.estack.Peek(0), n)
case Orot: case ROT:
c := v.estack.Pop() c := v.estack.Pop()
b := v.estack.Pop() b := v.estack.Pop()
a := v.estack.Pop() a := v.estack.Pop()
@ -322,21 +322,21 @@ func (v *VM) execute(ctx *Context, op Opcode) {
v.estack.Push(c) v.estack.Push(c)
v.estack.Push(a) v.estack.Push(a)
case Odepth: case DEPTH:
v.estack.PushVal(v.estack.Len()) v.estack.PushVal(v.estack.Len())
case Onip: case NIP:
elem := v.estack.Pop() elem := v.estack.Pop()
_ = v.estack.Pop() _ = v.estack.Pop()
v.estack.Push(elem) v.estack.Push(elem)
case Oover: case OVER:
b := v.estack.Pop() b := v.estack.Pop()
a := v.estack.Peek(0) a := v.estack.Peek(0)
v.estack.Push(b) v.estack.Push(b)
v.estack.Push(a) v.estack.Push(a)
case Oroll: case ROLL:
n := int(v.estack.Pop().BigInt().Int64()) n := int(v.estack.Pop().BigInt().Int64())
if n < 0 { if n < 0 {
panic("negative stack item returned") panic("negative stack item returned")
@ -345,105 +345,105 @@ func (v *VM) execute(ctx *Context, op Opcode) {
v.estack.Push(v.estack.RemoveAt(n)) v.estack.Push(v.estack.RemoveAt(n))
} }
case Odrop: case DROP:
v.estack.Pop() v.estack.Pop()
case Oequal: case EQUAL:
panic("TODO EQUAL") panic("TODO EQUAL")
// Bit operations. // Bit operations.
case Oand: case AND:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).And(b, a)) v.estack.PushVal(new(big.Int).And(b, a))
case Oor: case OR:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Or(b, a)) v.estack.PushVal(new(big.Int).Or(b, a))
case Oxor: case XOR:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Xor(b, a)) v.estack.PushVal(new(big.Int).Xor(b, a))
// Numeric operations. // Numeric operations.
case Oadd: case ADD:
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Add(a, b)) v.estack.PushVal(new(big.Int).Add(a, b))
case Osub: case SUB:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Sub(a, b)) v.estack.PushVal(new(big.Int).Sub(a, b))
case Odiv: case DIV:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Div(a, b)) v.estack.PushVal(new(big.Int).Div(a, b))
case Omul: case MUL:
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Mul(a, b)) v.estack.PushVal(new(big.Int).Mul(a, b))
case Omod: case MOD:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Mod(a, b)) v.estack.PushVal(new(big.Int).Mod(a, b))
case Oshl: case SHL:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Lsh(a, uint(b.Int64()))) v.estack.PushVal(new(big.Int).Lsh(a, uint(b.Int64())))
case Oshr: case SHR:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Rsh(a, uint(b.Int64()))) v.estack.PushVal(new(big.Int).Rsh(a, uint(b.Int64())))
case Obooland: case BOOLAND:
b := v.estack.Pop().Bool() b := v.estack.Pop().Bool()
a := v.estack.Pop().Bool() a := v.estack.Pop().Bool()
v.estack.PushVal(a && b) v.estack.PushVal(a && b)
case Oboolor: case BOOLOR:
b := v.estack.Pop().Bool() b := v.estack.Pop().Bool()
a := v.estack.Pop().Bool() a := v.estack.Pop().Bool()
v.estack.PushVal(a || b) v.estack.PushVal(a || b)
case Onumequal: case NUMEQUAL:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(a.Cmp(b) == 0) v.estack.PushVal(a.Cmp(b) == 0)
case Onumnotequal: case NUMNOTEQUAL:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(a.Cmp(b) != 0) v.estack.PushVal(a.Cmp(b) != 0)
case Olt: case LT:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(a.Cmp(b) == -1) v.estack.PushVal(a.Cmp(b) == -1)
case Ogt: case GT:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(a.Cmp(b) == 1) v.estack.PushVal(a.Cmp(b) == 1)
case Olte: case LTE:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(a.Cmp(b) <= 0) v.estack.PushVal(a.Cmp(b) <= 0)
case Ogte: case GTE:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
v.estack.PushVal(a.Cmp(b) >= 0) v.estack.PushVal(a.Cmp(b) >= 0)
case Omin: case MIN:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
val := a val := a
@ -452,7 +452,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
} }
v.estack.PushVal(val) v.estack.PushVal(val)
case Omax: case MAX:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
val := a val := a
@ -461,52 +461,52 @@ func (v *VM) execute(ctx *Context, op Opcode) {
} }
v.estack.PushVal(val) v.estack.PushVal(val)
case Owithin: case WITHIN:
b := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt()
a := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt()
x := v.estack.Pop().BigInt() x := v.estack.Pop().BigInt()
v.estack.PushVal(a.Cmp(x) <= 0 && x.Cmp(b) == -1) v.estack.PushVal(a.Cmp(x) <= 0 && x.Cmp(b) == -1)
case Oinc: case INC:
x := v.estack.Pop().BigInt() x := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Add(x, big.NewInt(1))) v.estack.PushVal(new(big.Int).Add(x, big.NewInt(1)))
case Odec: case DEC:
x := v.estack.Pop().BigInt() x := v.estack.Pop().BigInt()
v.estack.PushVal(new(big.Int).Sub(x, big.NewInt(1))) v.estack.PushVal(new(big.Int).Sub(x, big.NewInt(1)))
case Osign: case SIGN:
x := v.estack.Pop().BigInt() x := v.estack.Pop().BigInt()
v.estack.PushVal(x.Sign()) v.estack.PushVal(x.Sign())
case Onegate: case NEGATE:
x := v.estack.Pop().BigInt() x := v.estack.Pop().BigInt()
v.estack.PushVal(x.Neg(x)) v.estack.PushVal(x.Neg(x))
case Oabs: case ABS:
x := v.estack.Pop().BigInt() x := v.estack.Pop().BigInt()
v.estack.PushVal(x.Abs(x)) v.estack.PushVal(x.Abs(x))
case Onot: case NOT:
x := v.estack.Pop().Bool() x := v.estack.Pop().Bool()
v.estack.PushVal(!x) v.estack.PushVal(!x)
case Onz: case NZ:
panic("todo NZ") panic("todo NZ")
// x := v.estack.Pop().BigInt() // x := v.estack.Pop().BigInt()
// Object operations. // Object operations.
case Onewarray: case NEWARRAY:
n := v.estack.Pop().BigInt().Int64() n := v.estack.Pop().BigInt().Int64()
items := make([]StackItem, n) items := make([]StackItem, n)
v.estack.PushVal(&ArrayItem{items}) v.estack.PushVal(&ArrayItem{items})
case Onewstruct: case NEWSTRUCT:
n := v.estack.Pop().BigInt().Int64() n := v.estack.Pop().BigInt().Int64()
items := make([]StackItem, n) items := make([]StackItem, n)
v.estack.PushVal(&StructItem{items}) v.estack.PushVal(&StructItem{items})
case Oappend: case APPEND:
itemElem := v.estack.Pop() itemElem := v.estack.Pop()
arrElem := v.estack.Pop() arrElem := v.estack.Pop()
@ -522,11 +522,11 @@ func (v *VM) execute(ctx *Context, op Opcode) {
panic("APPEND: not of underlying type Array") panic("APPEND: not of underlying type Array")
} }
case Oreverse: case REVERSE:
case Oremove: case REMOVE:
case Opack: case PACK:
n := int(v.estack.Pop().BigInt().Int64()) n := int(v.estack.Pop().BigInt().Int64())
if n < 0 || n > v.estack.Len() { if n < 0 || n > v.estack.Len() {
panic("OPACK: invalid length") panic("OPACK: invalid length")
@ -539,10 +539,10 @@ func (v *VM) execute(ctx *Context, op Opcode) {
v.estack.PushVal(items) v.estack.PushVal(items)
case Ounpack: case UNPACK:
panic("TODO") panic("TODO")
case Opickitem: case PICKITEM:
var ( var (
key = v.estack.Pop() key = v.estack.Pop()
obj = v.estack.Pop() obj = v.estack.Pop()
@ -562,7 +562,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
panic("PICKITEM: unknown type") panic("PICKITEM: unknown type")
} }
case Osetitem: case SETITEM:
var ( var (
item = v.estack.Pop().value item = v.estack.Pop().value
key = v.estack.Pop() key = v.estack.Pop()
@ -582,7 +582,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
panic(fmt.Sprintf("SETITEM: invalid item type %s", t)) panic(fmt.Sprintf("SETITEM: invalid item type %s", t))
} }
case Oarraysize: case ARRAYSIZE:
elem := v.estack.Pop() elem := v.estack.Pop()
// Cause there is no native (byte) item type here, hence we need to check // Cause there is no native (byte) item type here, hence we need to check
// the type of the item for array size operations. // the type of the item for array size operations.
@ -595,7 +595,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
panic("ARRAYSIZE: item not of type []StackItem") panic("ARRAYSIZE: item not of type []StackItem")
} }
case Osize: case SIZE:
elem := v.estack.Pop() elem := v.estack.Pop()
arr, ok := elem.value.Value().([]uint8) arr, ok := elem.value.Value().([]uint8)
if !ok { if !ok {
@ -603,7 +603,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
} }
v.estack.PushVal(len(arr)) v.estack.PushVal(len(arr))
case Ojmp, Ojmpif, Ojmpifnot: case JMP, JMPIF, JMPIFNOT:
var ( var (
rOffset = int16(ctx.readUint16()) rOffset = int16(ctx.readUint16())
offset = ctx.ip + int(rOffset) - 3 // sizeOf(int16 + uint8) offset = ctx.ip + int(rOffset) - 3 // sizeOf(int16 + uint8)
@ -612,9 +612,9 @@ func (v *VM) execute(ctx *Context, op Opcode) {
panic(fmt.Sprintf("JMP: invalid offset %d ip at %d", offset, ctx.ip)) panic(fmt.Sprintf("JMP: invalid offset %d ip at %d", offset, ctx.ip))
} }
cond := true cond := true
if op > Ojmp { if op > JMP {
cond = v.estack.Pop().Bool() cond = v.estack.Pop().Bool()
if op == Ojmpifnot { if op == JMPIFNOT {
cond = !cond cond = !cond
} }
} }
@ -622,12 +622,12 @@ func (v *VM) execute(ctx *Context, op Opcode) {
ctx.ip = offset ctx.ip = offset
} }
case Ocall: case CALL:
v.istack.PushVal(ctx.Copy()) v.istack.PushVal(ctx.Copy())
ctx.ip += 2 ctx.ip += 2
v.execute(v.Context(), Ojmp) v.execute(v.Context(), JMP)
case Osyscall: case SYSCALL:
api := ctx.readVarBytes() api := ctx.readVarBytes()
ifunc, ok := v.interop[string(api)] ifunc, ok := v.interop[string(api)]
if !ok { if !ok {
@ -637,7 +637,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
panic(fmt.Sprintf("failed to invoke syscall: %s", err)) panic(fmt.Sprintf("failed to invoke syscall: %s", err))
} }
case Oappcall, Otailcall: case APPCALL, TAILCALL:
if len(v.scripts) == 0 { if len(v.scripts) == 0 {
panic("script table is empty") panic("script table is empty")
} }
@ -652,32 +652,32 @@ func (v *VM) execute(ctx *Context, op Opcode) {
panic("could not find script") panic("could not find script")
} }
if op == Otailcall { if op == TAILCALL {
_ = v.istack.Pop() _ = v.istack.Pop()
} }
v.LoadScript(script) v.LoadScript(script)
case Oret: case RET:
_ = v.istack.Pop() _ = v.istack.Pop()
if v.istack.Len() == 0 { if v.istack.Len() == 0 {
v.state = haltState v.state = haltState
} }
// Cryptographic operations. // Cryptographic operations.
case Osha1: case SHA1:
b := v.estack.Pop().Bytes() b := v.estack.Pop().Bytes()
sha := sha1.New() sha := sha1.New()
sha.Write(b) sha.Write(b)
v.estack.PushVal(sha.Sum(nil)) v.estack.PushVal(sha.Sum(nil))
case Osha256: case SHA256:
b := v.estack.Pop().Bytes() b := v.estack.Pop().Bytes()
sha := sha256.New() sha := sha256.New()
sha.Write(b) sha.Write(b)
v.estack.PushVal(sha.Sum(nil)) v.estack.PushVal(sha.Sum(nil))
case Ohash160: case HASH160:
b := v.estack.Pop().Bytes() b := v.estack.Pop().Bytes()
sha := sha256.New() sha := sha256.New()
sha.Write(b) sha.Write(b)
@ -686,7 +686,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
ripemd.Write(h) ripemd.Write(h)
v.estack.PushVal(ripemd.Sum(nil)) v.estack.PushVal(ripemd.Sum(nil))
case Ohash256: case HASH256:
b := v.estack.Pop().Bytes() b := v.estack.Pop().Bytes()
sha := sha256.New() sha := sha256.New()
sha.Write(b) sha.Write(b)
@ -695,19 +695,19 @@ func (v *VM) execute(ctx *Context, op Opcode) {
sha.Write(h) sha.Write(h)
v.estack.PushVal(sha.Sum(nil)) v.estack.PushVal(sha.Sum(nil))
case Ochecksig: case CHECKSIG:
// pubkey := v.estack.Pop().Bytes() // pubkey := v.estack.Pop().Bytes()
// sig := v.estack.Pop().Bytes() // sig := v.estack.Pop().Bytes()
case Ocheckmultisig: case CHECKMULTISIG:
case Onop: case NOP:
// unlucky ^^ // unlucky ^^
case Othrow: case THROW:
panic("THROW") panic("THROW")
case Othrowifnot: case THROWIFNOT:
if !v.estack.Pop().Bool() { if !v.estack.Pop().Bool() {
panic("THROWIFNOT") panic("THROWIFNOT")
} }

View file

@ -20,7 +20,7 @@ func TestInteropHook(t *testing.T) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
EmitSyscall(buf, "foo") EmitSyscall(buf, "foo")
EmitOpcode(buf, Oret) EmitOpcode(buf, RET)
v.Load(buf.Bytes()) v.Load(buf.Bytes())
v.Run() v.Run()
assert.Equal(t, 1, v.estack.Len()) assert.Equal(t, 1, v.estack.Len())
@ -51,7 +51,7 @@ func TestPushBytes1to75(t *testing.T) {
assert.IsType(t, elem.Bytes(), b) assert.IsType(t, elem.Bytes(), b)
assert.Equal(t, 0, vm.estack.Len()) assert.Equal(t, 0, vm.estack.Len())
vm.execute(nil, Oret) vm.execute(nil, RET)
assert.Equal(t, 0, vm.astack.Len()) assert.Equal(t, 0, vm.astack.Len())
assert.Equal(t, 0, vm.istack.Len()) assert.Equal(t, 0, vm.istack.Len())
@ -61,7 +61,7 @@ func TestPushBytes1to75(t *testing.T) {
func TestPushm1to16(t *testing.T) { func TestPushm1to16(t *testing.T) {
var prog []byte var prog []byte
for i := int(Opushm1); i <= int(Opush16); i++ { for i := int(PUSHM1); i <= int(PUSH16); i++ {
if i == 80 { if i == 80 {
continue // opcode layout we got here. continue // opcode layout we got here.
} }
@ -69,7 +69,7 @@ func TestPushm1to16(t *testing.T) {
} }
vm := load(prog) vm := load(prog)
for i := int(Opushm1); i <= int(Opush16); i++ { for i := int(PUSHM1); i <= int(PUSH16); i++ {
if i == 80 { if i == 80 {
continue // nice opcode layout we got here. continue // nice opcode layout we got here.
} }
@ -77,7 +77,7 @@ func TestPushm1to16(t *testing.T) {
elem := vm.estack.Pop() elem := vm.estack.Pop()
assert.IsType(t, &BigIntegerItem{}, elem.value) assert.IsType(t, &BigIntegerItem{}, elem.value)
val := i - int(Opush1) + 1 val := i - int(PUSH1) + 1
assert.Equal(t, elem.BigInt().Int64(), int64(val)) assert.Equal(t, elem.BigInt().Int64(), int64(val))
} }
} }
@ -95,7 +95,7 @@ func TestPushData4(t *testing.T) {
} }
func TestAdd(t *testing.T) { func TestAdd(t *testing.T) {
prog := makeProgram(Oadd) prog := makeProgram(ADD)
vm := load(prog) vm := load(prog)
vm.estack.PushVal(4) vm.estack.PushVal(4)
vm.estack.PushVal(2) vm.estack.PushVal(2)
@ -104,7 +104,7 @@ func TestAdd(t *testing.T) {
} }
func TestMul(t *testing.T) { func TestMul(t *testing.T) {
prog := makeProgram(Omul) prog := makeProgram(MUL)
vm := load(prog) vm := load(prog)
vm.estack.PushVal(4) vm.estack.PushVal(4)
vm.estack.PushVal(2) vm.estack.PushVal(2)
@ -113,7 +113,7 @@ func TestMul(t *testing.T) {
} }
func TestDiv(t *testing.T) { func TestDiv(t *testing.T) {
prog := makeProgram(Odiv) prog := makeProgram(DIV)
vm := load(prog) vm := load(prog)
vm.estack.PushVal(4) vm.estack.PushVal(4)
vm.estack.PushVal(2) vm.estack.PushVal(2)
@ -122,7 +122,7 @@ func TestDiv(t *testing.T) {
} }
func TestSub(t *testing.T) { func TestSub(t *testing.T) {
prog := makeProgram(Osub) prog := makeProgram(SUB)
vm := load(prog) vm := load(prog)
vm.estack.PushVal(4) vm.estack.PushVal(4)
vm.estack.PushVal(2) vm.estack.PushVal(2)
@ -131,7 +131,7 @@ func TestSub(t *testing.T) {
} }
func TestLT(t *testing.T) { func TestLT(t *testing.T) {
prog := makeProgram(Olt) prog := makeProgram(LT)
vm := load(prog) vm := load(prog)
vm.estack.PushVal(4) vm.estack.PushVal(4)
vm.estack.PushVal(3) vm.estack.PushVal(3)
@ -140,7 +140,7 @@ func TestLT(t *testing.T) {
} }
func TestLTE(t *testing.T) { func TestLTE(t *testing.T) {
prog := makeProgram(Olte) prog := makeProgram(LTE)
vm := load(prog) vm := load(prog)
vm.estack.PushVal(2) vm.estack.PushVal(2)
vm.estack.PushVal(3) vm.estack.PushVal(3)
@ -149,7 +149,7 @@ func TestLTE(t *testing.T) {
} }
func TestGT(t *testing.T) { func TestGT(t *testing.T) {
prog := makeProgram(Ogt) prog := makeProgram(GT)
vm := load(prog) vm := load(prog)
vm.estack.PushVal(9) vm.estack.PushVal(9)
vm.estack.PushVal(3) vm.estack.PushVal(3)
@ -159,7 +159,7 @@ func TestGT(t *testing.T) {
} }
func TestGTE(t *testing.T) { func TestGTE(t *testing.T) {
prog := makeProgram(Ogte) prog := makeProgram(GTE)
vm := load(prog) vm := load(prog)
vm.estack.PushVal(3) vm.estack.PushVal(3)
vm.estack.PushVal(3) vm.estack.PushVal(3)
@ -168,7 +168,7 @@ func TestGTE(t *testing.T) {
} }
func TestDepth(t *testing.T) { func TestDepth(t *testing.T) {
prog := makeProgram(Odepth) prog := makeProgram(DEPTH)
vm := load(prog) vm := load(prog)
vm.estack.PushVal(1) vm.estack.PushVal(1)
vm.estack.PushVal(2) vm.estack.PushVal(2)
@ -178,7 +178,7 @@ func TestDepth(t *testing.T) {
} }
func TestNumEqual(t *testing.T) { func TestNumEqual(t *testing.T) {
prog := makeProgram(Onumequal) prog := makeProgram(NUMEQUAL)
vm := load(prog) vm := load(prog)
vm.estack.PushVal(1) vm.estack.PushVal(1)
vm.estack.PushVal(2) vm.estack.PushVal(2)
@ -187,7 +187,7 @@ func TestNumEqual(t *testing.T) {
} }
func TestNumNotEqual(t *testing.T) { func TestNumNotEqual(t *testing.T) {
prog := makeProgram(Onumnotequal) prog := makeProgram(NUMNOTEQUAL)
vm := load(prog) vm := load(prog)
vm.estack.PushVal(2) vm.estack.PushVal(2)
vm.estack.PushVal(2) vm.estack.PushVal(2)
@ -196,7 +196,7 @@ func TestNumNotEqual(t *testing.T) {
} }
func TestINC(t *testing.T) { func TestINC(t *testing.T) {
prog := makeProgram(Oinc) prog := makeProgram(INC)
vm := load(prog) vm := load(prog)
vm.estack.PushVal(1) vm.estack.PushVal(1)
vm.Run() vm.Run()
@ -204,13 +204,13 @@ func TestINC(t *testing.T) {
} }
func TestAppCall(t *testing.T) { func TestAppCall(t *testing.T) {
prog := []byte{byte(Oappcall)} prog := []byte{byte(APPCALL)}
hash := util.Uint160{} hash := util.Uint160{}
prog = append(prog, hash.Bytes()...) prog = append(prog, hash.Bytes()...)
prog = append(prog, byte(Oret)) prog = append(prog, byte(RET))
vm := load(prog) vm := load(prog)
vm.scripts[hash] = makeProgram(Odepth) vm.scripts[hash] = makeProgram(DEPTH)
vm.estack.PushVal(2) vm.estack.PushVal(2)
vm.Run() vm.Run()
@ -231,12 +231,12 @@ func TestSimpleCall(t *testing.T) {
assert.Equal(t, result, int(vm.estack.Pop().BigInt().Int64())) assert.Equal(t, result, int(vm.estack.Pop().BigInt().Int64()))
} }
func makeProgram(opcodes ...Opcode) []byte { func makeProgram(opcodes ...Instruction) []byte {
prog := make([]byte, len(opcodes)+1) // Oret prog := make([]byte, len(opcodes)+1) // RET
for i := 0; i < len(opcodes); i++ { for i := 0; i < len(opcodes); i++ {
prog[i] = byte(opcodes[i]) prog[i] = byte(opcodes[i])
} }
prog[len(prog)-1] = byte(Oret) prog[len(prog)-1] = byte(RET)
return prog return prog
} }