forked from TrueCloudLab/neoneo-go
vm: move Emit* functions to a separate package
Also strip 'Emit' prefix because 'emit' is now in the package name.
This commit is contained in:
parent
ab14a4619d
commit
4d8a3a359b
7 changed files with 198 additions and 196 deletions
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/smartcontract/trigger"
|
"github.com/CityOfZion/neo-go/pkg/smartcontract/trigger"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm/emit"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -1486,7 +1487,7 @@ func ScriptFromWitness(hash util.Uint160, witness *transaction.Witness) ([]byte,
|
||||||
|
|
||||||
if len(verification) == 0 {
|
if len(verification) == 0 {
|
||||||
bb := new(bytes.Buffer)
|
bb := new(bytes.Buffer)
|
||||||
err := vm.EmitAppCall(bb, hash, false)
|
err := emit.AppCall(bb, hash, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/io"
|
"github.com/CityOfZion/neo-go/pkg/io"
|
||||||
"github.com/CityOfZion/neo-go/pkg/smartcontract"
|
"github.com/CityOfZion/neo-go/pkg/smartcontract"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
"github.com/CityOfZion/neo-go/pkg/vm/emit"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
||||||
errs "github.com/pkg/errors"
|
errs "github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
@ -114,19 +114,19 @@ func CreateDeploymentScript(avm []byte, contract *ContractDetails) ([]byte, erro
|
||||||
var props smartcontract.PropertyState
|
var props smartcontract.PropertyState
|
||||||
|
|
||||||
script := new(bytes.Buffer)
|
script := new(bytes.Buffer)
|
||||||
if err := vm.EmitBytes(script, []byte(contract.Description)); err != nil {
|
if err := emit.Bytes(script, []byte(contract.Description)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := vm.EmitBytes(script, []byte(contract.Email)); err != nil {
|
if err := emit.Bytes(script, []byte(contract.Email)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := vm.EmitBytes(script, []byte(contract.Author)); err != nil {
|
if err := emit.Bytes(script, []byte(contract.Author)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := vm.EmitBytes(script, []byte(contract.Version)); err != nil {
|
if err := emit.Bytes(script, []byte(contract.Version)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := vm.EmitBytes(script, []byte(contract.ProjectName)); err != nil {
|
if err := emit.Bytes(script, []byte(contract.ProjectName)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if contract.HasStorage {
|
if contract.HasStorage {
|
||||||
|
@ -138,23 +138,23 @@ func CreateDeploymentScript(avm []byte, contract *ContractDetails) ([]byte, erro
|
||||||
if contract.IsPayable {
|
if contract.IsPayable {
|
||||||
props |= smartcontract.IsPayable
|
props |= smartcontract.IsPayable
|
||||||
}
|
}
|
||||||
if err := vm.EmitInt(script, int64(props)); err != nil {
|
if err := emit.Int(script, int64(props)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := vm.EmitInt(script, int64(contract.ReturnType)); err != nil {
|
if err := emit.Int(script, int64(contract.ReturnType)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
params := make([]byte, len(contract.Parameters))
|
params := make([]byte, len(contract.Parameters))
|
||||||
for k := range contract.Parameters {
|
for k := range contract.Parameters {
|
||||||
params[k] = byte(contract.Parameters[k])
|
params[k] = byte(contract.Parameters[k])
|
||||||
}
|
}
|
||||||
if err := vm.EmitBytes(script, params); err != nil {
|
if err := emit.Bytes(script, params); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := vm.EmitBytes(script, avm); err != nil {
|
if err := emit.Bytes(script, avm); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := vm.EmitSyscall(script, "Neo.Contract.Create"); err != nil {
|
if err := emit.Syscall(script, "Neo.Contract.Create"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return script.Bytes(), nil
|
return script.Bytes(), nil
|
||||||
|
@ -174,7 +174,7 @@ func expandArrayIntoScript(script *bytes.Buffer, slice []Param) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := vm.EmitBytes(script, str); err != nil {
|
if err := emit.Bytes(script, str); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case String:
|
case String:
|
||||||
|
@ -182,7 +182,7 @@ func expandArrayIntoScript(script *bytes.Buffer, slice []Param) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := vm.EmitString(script, str); err != nil {
|
if err := emit.String(script, str); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case Hash160:
|
case Hash160:
|
||||||
|
@ -190,7 +190,7 @@ func expandArrayIntoScript(script *bytes.Buffer, slice []Param) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := vm.EmitBytes(script, hash.BytesBE()); err != nil {
|
if err := emit.Bytes(script, hash.BytesBE()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case Hash256:
|
case Hash256:
|
||||||
|
@ -198,7 +198,7 @@ func expandArrayIntoScript(script *bytes.Buffer, slice []Param) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := vm.EmitBytes(script, hash.BytesBE()); err != nil {
|
if err := emit.Bytes(script, hash.BytesBE()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case PublicKey:
|
case PublicKey:
|
||||||
|
@ -210,7 +210,7 @@ func expandArrayIntoScript(script *bytes.Buffer, slice []Param) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := vm.EmitBytes(script, key.Bytes()); err != nil {
|
if err := emit.Bytes(script, key.Bytes()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case Integer:
|
case Integer:
|
||||||
|
@ -218,7 +218,7 @@ func expandArrayIntoScript(script *bytes.Buffer, slice []Param) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := vm.EmitInt(script, int64(val)); err != nil {
|
if err := emit.Int(script, int64(val)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case Boolean:
|
case Boolean:
|
||||||
|
@ -228,9 +228,9 @@ func expandArrayIntoScript(script *bytes.Buffer, slice []Param) error {
|
||||||
}
|
}
|
||||||
switch str {
|
switch str {
|
||||||
case "true":
|
case "true":
|
||||||
err = vm.EmitInt(script, 1)
|
err = emit.Int(script, 1)
|
||||||
case "false":
|
case "false":
|
||||||
err = vm.EmitInt(script, 0)
|
err = emit.Int(script, 0)
|
||||||
default:
|
default:
|
||||||
err = errors.New("wrong boolean value")
|
err = errors.New("wrong boolean value")
|
||||||
}
|
}
|
||||||
|
@ -251,7 +251,7 @@ func CreateFunctionInvocationScript(contract util.Uint160, params Params) ([]byt
|
||||||
for i := len(params) - 1; i >= 0; i-- {
|
for i := len(params) - 1; i >= 0; i-- {
|
||||||
switch params[i].Type {
|
switch params[i].Type {
|
||||||
case stringT:
|
case stringT:
|
||||||
if err := vm.EmitString(script, params[i].String()); err != nil {
|
if err := emit.String(script, params[i].String()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case numberT:
|
case numberT:
|
||||||
|
@ -259,7 +259,7 @@ func CreateFunctionInvocationScript(contract util.Uint160, params Params) ([]byt
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := vm.EmitString(script, strconv.Itoa(num)); err != nil {
|
if err := emit.String(script, strconv.Itoa(num)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case arrayT:
|
case arrayT:
|
||||||
|
@ -271,18 +271,18 @@ func CreateFunctionInvocationScript(contract util.Uint160, params Params) ([]byt
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = vm.EmitInt(script, int64(len(slice)))
|
err = emit.Int(script, int64(len(slice)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = vm.EmitOpcode(script, opcode.PACK)
|
err = emit.Opcode(script, opcode.PACK)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := vm.EmitAppCall(script, contract, false); err != nil {
|
if err := emit.AppCall(script, contract, false); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return script.Bytes(), nil
|
return script.Bytes(), nil
|
||||||
|
@ -298,7 +298,7 @@ func CreateInvocationScript(contract util.Uint160, funcParams []Param) ([]byte,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err = vm.EmitAppCall(script, contract, false); err != nil {
|
if err = emit.AppCall(script, contract, false); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return script.Bytes(), nil
|
return script.Bytes(), nil
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
"github.com/CityOfZion/neo-go/pkg/vm/emit"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,19 +23,19 @@ func CreateMultiSigRedeemScript(m int, publicKeys keys.PublicKeys) ([]byte, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
if err := vm.EmitInt(buf, int64(m)); err != nil {
|
if err := emit.Int(buf, int64(m)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sort.Sort(publicKeys)
|
sort.Sort(publicKeys)
|
||||||
for _, pubKey := range publicKeys {
|
for _, pubKey := range publicKeys {
|
||||||
if err := vm.EmitBytes(buf, pubKey.Bytes()); err != nil {
|
if err := emit.Bytes(buf, pubKey.Bytes()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := vm.EmitInt(buf, int64(len(publicKeys))); err != nil {
|
if err := emit.Int(buf, int64(len(publicKeys))); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := vm.EmitOpcode(buf, opcode.CHECKMULTISIG); err != nil {
|
if err := emit.Opcode(buf, opcode.CHECKMULTISIG); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
151
pkg/vm/emit.go
151
pkg/vm/emit.go
|
@ -1,151 +0,0 @@
|
||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Emit a VM Instruction with data to the given buffer.
|
|
||||||
func Emit(w *bytes.Buffer, op opcode.Opcode, b []byte) error {
|
|
||||||
if err := w.WriteByte(byte(op)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err := w.Write(b)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitOpcode emits a single VM Instruction the given buffer.
|
|
||||||
func EmitOpcode(w io.ByteWriter, op opcode.Opcode) error {
|
|
||||||
return w.WriteByte(byte(op))
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitBool emits a bool type the given buffer.
|
|
||||||
func EmitBool(w io.ByteWriter, ok bool) error {
|
|
||||||
if ok {
|
|
||||||
return EmitOpcode(w, opcode.PUSHT)
|
|
||||||
}
|
|
||||||
return EmitOpcode(w, opcode.PUSHF)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitInt emits a int type to the given buffer.
|
|
||||||
func EmitInt(w *bytes.Buffer, i int64) error {
|
|
||||||
if i == -1 {
|
|
||||||
return EmitOpcode(w, opcode.PUSHM1)
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
return EmitOpcode(w, opcode.PUSHF)
|
|
||||||
}
|
|
||||||
if i > 0 && i < 16 {
|
|
||||||
val := opcode.Opcode(int(opcode.PUSH1) - 1 + int(i))
|
|
||||||
return EmitOpcode(w, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
bInt := big.NewInt(i)
|
|
||||||
val := util.ArrayReverse(bInt.Bytes())
|
|
||||||
return EmitBytes(w, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitString emits a string to the given buffer.
|
|
||||||
func EmitString(w *bytes.Buffer, s string) error {
|
|
||||||
return EmitBytes(w, []byte(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitBytes emits a byte array to the given buffer.
|
|
||||||
func EmitBytes(w *bytes.Buffer, b []byte) error {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
n = len(b)
|
|
||||||
)
|
|
||||||
|
|
||||||
if n <= int(opcode.PUSHBYTES75) {
|
|
||||||
return Emit(w, opcode.Opcode(n), b)
|
|
||||||
} else if n < 0x100 {
|
|
||||||
err = Emit(w, opcode.PUSHDATA1, []byte{byte(n)})
|
|
||||||
} else if n < 0x10000 {
|
|
||||||
buf := make([]byte, 2)
|
|
||||||
binary.LittleEndian.PutUint16(buf, uint16(n))
|
|
||||||
err = Emit(w, opcode.PUSHDATA2, buf)
|
|
||||||
} else {
|
|
||||||
buf := make([]byte, 4)
|
|
||||||
binary.LittleEndian.PutUint32(buf, uint32(n))
|
|
||||||
err = Emit(w, opcode.PUSHDATA4, buf)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = w.Write(b)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitSyscall emits the syscall API to the given buffer.
|
|
||||||
// Syscall API string cannot be 0.
|
|
||||||
func EmitSyscall(w *bytes.Buffer, api string) error {
|
|
||||||
if len(api) == 0 {
|
|
||||||
return errors.New("syscall api cannot be of length 0")
|
|
||||||
}
|
|
||||||
buf := make([]byte, len(api)+1)
|
|
||||||
buf[0] = byte(len(api))
|
|
||||||
copy(buf[1:], api)
|
|
||||||
return Emit(w, opcode.SYSCALL, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitCall emits a call Instruction with label to the given buffer.
|
|
||||||
func EmitCall(w *bytes.Buffer, op opcode.Opcode, label int16) error {
|
|
||||||
return EmitJmp(w, op, label)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitJmp emits a jump Instruction along with label to the given buffer.
|
|
||||||
func EmitJmp(w *bytes.Buffer, op opcode.Opcode, label int16) error {
|
|
||||||
if !isInstructionJmp(op) {
|
|
||||||
return fmt.Errorf("opcode %s is not a jump or call type", op.String())
|
|
||||||
}
|
|
||||||
buf := make([]byte, 2)
|
|
||||||
binary.LittleEndian.PutUint16(buf, uint16(label))
|
|
||||||
return Emit(w, op, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitAppCall emits an appcall, if tailCall is true, tailCall opcode will be
|
|
||||||
// emitted instead.
|
|
||||||
func EmitAppCall(w *bytes.Buffer, scriptHash util.Uint160, tailCall bool) error {
|
|
||||||
op := opcode.APPCALL
|
|
||||||
if tailCall {
|
|
||||||
op = opcode.TAILCALL
|
|
||||||
}
|
|
||||||
return Emit(w, op, scriptHash.BytesBE())
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitAppCallWithOperationAndData emits an appcall with the given operation and data.
|
|
||||||
func EmitAppCallWithOperationAndData(w *bytes.Buffer, scriptHash util.Uint160, operation string, data []byte) error {
|
|
||||||
if err := EmitBytes(w, data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := EmitString(w, operation); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return EmitAppCall(w, scriptHash, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitAppCallWithOperation emits an appcall with the given operation.
|
|
||||||
func EmitAppCallWithOperation(w *bytes.Buffer, scriptHash util.Uint160, operation string) error {
|
|
||||||
if err := EmitBool(w, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := EmitString(w, operation); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return EmitAppCall(w, scriptHash, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isInstructionJmp(op opcode.Opcode) bool {
|
|
||||||
if op == opcode.JMP || op == opcode.JMPIFNOT || op == opcode.JMPIF || op == opcode.CALL {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
151
pkg/vm/emit/emit.go
Normal file
151
pkg/vm/emit/emit.go
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
package emit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Instruction emits a VM Instruction with data to the given buffer.
|
||||||
|
func Instruction(w *bytes.Buffer, op opcode.Opcode, b []byte) error {
|
||||||
|
if err := w.WriteByte(byte(op)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := w.Write(b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opcode emits a single VM Instruction without arguments to the given buffer.
|
||||||
|
func Opcode(w io.ByteWriter, op opcode.Opcode) error {
|
||||||
|
return w.WriteByte(byte(op))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool emits a bool type the given buffer.
|
||||||
|
func Bool(w io.ByteWriter, ok bool) error {
|
||||||
|
if ok {
|
||||||
|
return Opcode(w, opcode.PUSHT)
|
||||||
|
}
|
||||||
|
return Opcode(w, opcode.PUSHF)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int emits a int type to the given buffer.
|
||||||
|
func Int(w *bytes.Buffer, i int64) error {
|
||||||
|
if i == -1 {
|
||||||
|
return Opcode(w, opcode.PUSHM1)
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
return Opcode(w, opcode.PUSHF)
|
||||||
|
}
|
||||||
|
if i > 0 && i < 16 {
|
||||||
|
val := opcode.Opcode(int(opcode.PUSH1) - 1 + int(i))
|
||||||
|
return Opcode(w, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
bInt := big.NewInt(i)
|
||||||
|
val := util.ArrayReverse(bInt.Bytes())
|
||||||
|
return Bytes(w, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String emits a string to the given buffer.
|
||||||
|
func String(w *bytes.Buffer, s string) error {
|
||||||
|
return Bytes(w, []byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes emits a byte array to the given buffer.
|
||||||
|
func Bytes(w *bytes.Buffer, b []byte) error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
n = len(b)
|
||||||
|
)
|
||||||
|
|
||||||
|
if n <= int(opcode.PUSHBYTES75) {
|
||||||
|
return Instruction(w, opcode.Opcode(n), b)
|
||||||
|
} else if n < 0x100 {
|
||||||
|
err = Instruction(w, opcode.PUSHDATA1, []byte{byte(n)})
|
||||||
|
} else if n < 0x10000 {
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
binary.LittleEndian.PutUint16(buf, uint16(n))
|
||||||
|
err = Instruction(w, opcode.PUSHDATA2, buf)
|
||||||
|
} else {
|
||||||
|
buf := make([]byte, 4)
|
||||||
|
binary.LittleEndian.PutUint32(buf, uint32(n))
|
||||||
|
err = Instruction(w, opcode.PUSHDATA4, buf)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = w.Write(b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Syscall emits the syscall API to the given buffer.
|
||||||
|
// Syscall API string cannot be 0.
|
||||||
|
func Syscall(w *bytes.Buffer, api string) error {
|
||||||
|
if len(api) == 0 {
|
||||||
|
return errors.New("syscall api cannot be of length 0")
|
||||||
|
}
|
||||||
|
buf := make([]byte, len(api)+1)
|
||||||
|
buf[0] = byte(len(api))
|
||||||
|
copy(buf[1:], api)
|
||||||
|
return Instruction(w, opcode.SYSCALL, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call emits a call Instruction with label to the given buffer.
|
||||||
|
func Call(w *bytes.Buffer, op opcode.Opcode, label int16) error {
|
||||||
|
return Jmp(w, op, label)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jmp emits a jump Instruction along with label to the given buffer.
|
||||||
|
func Jmp(w *bytes.Buffer, op opcode.Opcode, label int16) error {
|
||||||
|
if !isInstructionJmp(op) {
|
||||||
|
return fmt.Errorf("opcode %s is not a jump or call type", op.String())
|
||||||
|
}
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
binary.LittleEndian.PutUint16(buf, uint16(label))
|
||||||
|
return Instruction(w, op, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppCall emits an appcall, if tailCall is true, tailCall opcode will be
|
||||||
|
// emitted instead.
|
||||||
|
func AppCall(w *bytes.Buffer, scriptHash util.Uint160, tailCall bool) error {
|
||||||
|
op := opcode.APPCALL
|
||||||
|
if tailCall {
|
||||||
|
op = opcode.TAILCALL
|
||||||
|
}
|
||||||
|
return Instruction(w, op, scriptHash.BytesBE())
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppCallWithOperationAndData emits an appcall with the given operation and data.
|
||||||
|
func AppCallWithOperationAndData(w *bytes.Buffer, scriptHash util.Uint160, operation string, data []byte) error {
|
||||||
|
if err := Bytes(w, data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := String(w, operation); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return AppCall(w, scriptHash, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppCallWithOperation emits an appcall with the given operation.
|
||||||
|
func AppCallWithOperation(w *bytes.Buffer, scriptHash util.Uint160, operation string) error {
|
||||||
|
if err := Bool(w, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := String(w, operation); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return AppCall(w, scriptHash, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isInstructionJmp(op opcode.Opcode) bool {
|
||||||
|
if op == opcode.JMP || op == opcode.JMPIFNOT || op == opcode.JMPIF || op == opcode.CALL {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package vm
|
package emit
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -11,22 +11,22 @@ import (
|
||||||
|
|
||||||
func TestEmitInt(t *testing.T) {
|
func TestEmitInt(t *testing.T) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
EmitInt(buf, 10)
|
Int(buf, 10)
|
||||||
assert.Equal(t, opcode.Opcode(buf.Bytes()[0]), opcode.PUSH10)
|
assert.Equal(t, opcode.Opcode(buf.Bytes()[0]), opcode.PUSH10)
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
EmitInt(buf, 100)
|
Int(buf, 100)
|
||||||
assert.Equal(t, buf.Bytes()[0], uint8(1))
|
assert.Equal(t, buf.Bytes()[0], uint8(1))
|
||||||
assert.Equal(t, buf.Bytes()[1], uint8(100))
|
assert.Equal(t, buf.Bytes()[1], uint8(100))
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
EmitInt(buf, 1000)
|
Int(buf, 1000)
|
||||||
assert.Equal(t, buf.Bytes()[0], uint8(2))
|
assert.Equal(t, buf.Bytes()[0], uint8(2))
|
||||||
assert.Equal(t, buf.Bytes()[1:3], []byte{0xe8, 0x03})
|
assert.Equal(t, buf.Bytes()[1:3], []byte{0xe8, 0x03})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmitBool(t *testing.T) {
|
func TestEmitBool(t *testing.T) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
EmitBool(buf, true)
|
Bool(buf, true)
|
||||||
EmitBool(buf, false)
|
Bool(buf, false)
|
||||||
assert.Equal(t, opcode.Opcode(buf.Bytes()[0]), opcode.PUSH1)
|
assert.Equal(t, opcode.Opcode(buf.Bytes()[0]), opcode.PUSH1)
|
||||||
assert.Equal(t, opcode.Opcode(buf.Bytes()[1]), opcode.PUSH0)
|
assert.Equal(t, opcode.Opcode(buf.Bytes()[1]), opcode.PUSH0)
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ func TestEmitBool(t *testing.T) {
|
||||||
func TestEmitString(t *testing.T) {
|
func TestEmitString(t *testing.T) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
str := "City Of Zion"
|
str := "City Of Zion"
|
||||||
EmitString(buf, str)
|
String(buf, str)
|
||||||
assert.Equal(t, buf.Len(), len(str)+1)
|
assert.Equal(t, buf.Len(), len(str)+1)
|
||||||
assert.Equal(t, buf.Bytes()[1:], []byte(str))
|
assert.Equal(t, buf.Bytes()[1:], []byte(str))
|
||||||
}
|
}
|
||||||
|
@ -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)
|
Syscall(buf, syscall)
|
||||||
assert.Equal(t, opcode.Opcode(buf.Bytes()[0]), opcode.SYSCALL)
|
assert.Equal(t, opcode.Opcode(buf.Bytes()[0]), opcode.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))
|
||||||
|
@ -58,7 +58,7 @@ 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, opcode.JMP, 100)
|
Call(buf, opcode.JMP, 100)
|
||||||
assert.Equal(t, opcode.Opcode(buf.Bytes()[0]), opcode.JMP)
|
assert.Equal(t, opcode.Opcode(buf.Bytes()[0]), opcode.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))
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm/emit"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -31,8 +32,8 @@ func TestInteropHook(t *testing.T) {
|
||||||
v.RegisterInteropGetter(fooInteropGetter)
|
v.RegisterInteropGetter(fooInteropGetter)
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
EmitSyscall(buf, "foo")
|
emit.Syscall(buf, "foo")
|
||||||
EmitOpcode(buf, opcode.RET)
|
emit.Opcode(buf, opcode.RET)
|
||||||
v.Load(buf.Bytes())
|
v.Load(buf.Bytes())
|
||||||
runVM(t, v)
|
runVM(t, v)
|
||||||
assert.Equal(t, 1, v.estack.Len())
|
assert.Equal(t, 1, v.estack.Len())
|
||||||
|
@ -47,8 +48,8 @@ func TestInteropHookViaID(t *testing.T) {
|
||||||
fooid := InteropNameToID([]byte("foo"))
|
fooid := InteropNameToID([]byte("foo"))
|
||||||
var id = make([]byte, 4)
|
var id = make([]byte, 4)
|
||||||
binary.LittleEndian.PutUint32(id, fooid)
|
binary.LittleEndian.PutUint32(id, fooid)
|
||||||
_ = EmitSyscall(buf, string(id))
|
_ = emit.Syscall(buf, string(id))
|
||||||
_ = EmitOpcode(buf, opcode.RET)
|
_ = emit.Opcode(buf, opcode.RET)
|
||||||
v.Load(buf.Bytes())
|
v.Load(buf.Bytes())
|
||||||
runVM(t, v)
|
runVM(t, v)
|
||||||
assert.Equal(t, 1, v.estack.Len())
|
assert.Equal(t, 1, v.estack.Len())
|
||||||
|
@ -133,7 +134,7 @@ func TestPushBytes1to75(t *testing.T) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
for i := 1; i <= 75; i++ {
|
for i := 1; i <= 75; i++ {
|
||||||
b := randomBytes(i)
|
b := randomBytes(i)
|
||||||
EmitBytes(buf, b)
|
emit.Bytes(buf, b)
|
||||||
vm := load(buf.Bytes())
|
vm := load(buf.Bytes())
|
||||||
err := vm.Step()
|
err := vm.Step()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
Loading…
Reference in a new issue