Merge pull request #354 from nspcc-dev/some-vm-opcodes-and-fixes
Implements NZ, PICK, TUCK, XDROP, INVERT. Fixes XTUCK.
This commit is contained in:
commit
c899ec3038
3 changed files with 313 additions and 30 deletions
|
@ -132,21 +132,15 @@ func (s *Stack) insert(e, at *Element) *Element {
|
|||
return e
|
||||
}
|
||||
|
||||
// InsertBefore will insert the element before the mark on the stack.
|
||||
func (s *Stack) InsertBefore(e, mark *Element) *Element {
|
||||
if mark == nil {
|
||||
return nil
|
||||
}
|
||||
return s.insert(e, mark.prev)
|
||||
}
|
||||
|
||||
// InsertAt will insert the given item (n) deep on the stack.
|
||||
// Be very careful using it and _always_ check both e and n before invocation
|
||||
// as it will silently do wrong things otherwise.
|
||||
func (s *Stack) InsertAt(e *Element, n int) *Element {
|
||||
before := s.Peek(n)
|
||||
before := s.Peek(n - 1)
|
||||
if before == nil {
|
||||
return nil
|
||||
}
|
||||
return s.InsertBefore(e, before)
|
||||
return s.insert(e, before)
|
||||
}
|
||||
|
||||
// Push pushes the given element on the stack.
|
||||
|
|
79
pkg/vm/vm.go
79
pkg/vm/vm.go
|
@ -290,6 +290,26 @@ func (v *VM) execute(ctx *Context, op Instruction) {
|
|||
v.estack.Push(a)
|
||||
v.estack.Push(b)
|
||||
|
||||
case TUCK:
|
||||
a := v.estack.Dup(0)
|
||||
if a == nil {
|
||||
panic("no top-level element found")
|
||||
}
|
||||
if v.estack.Len() < 2 {
|
||||
panic("can't TUCK with a one-element stack")
|
||||
}
|
||||
v.estack.InsertAt(a, 2)
|
||||
|
||||
case XDROP:
|
||||
n := int(v.estack.Pop().BigInt().Int64())
|
||||
if n < 0 {
|
||||
panic("invalid length")
|
||||
}
|
||||
e := v.estack.RemoveAt(n)
|
||||
if e == nil {
|
||||
panic("bad index")
|
||||
}
|
||||
|
||||
case XSWAP:
|
||||
n := int(v.estack.Pop().BigInt().Int64())
|
||||
if n < 0 {
|
||||
|
@ -306,13 +326,19 @@ func (v *VM) execute(ctx *Context, op Instruction) {
|
|||
b.value = aval
|
||||
}
|
||||
|
||||
case TUCK:
|
||||
case XTUCK:
|
||||
n := int(v.estack.Pop().BigInt().Int64())
|
||||
if n <= 0 {
|
||||
panic("OTUCK: invalid length")
|
||||
if n < 0 {
|
||||
panic("XTUCK: invalid length")
|
||||
}
|
||||
|
||||
v.estack.InsertAt(v.estack.Peek(0), n)
|
||||
a := v.estack.Dup(0)
|
||||
if a == nil {
|
||||
panic("no top-level element found")
|
||||
}
|
||||
if n > v.estack.Len() {
|
||||
panic("can't push to the position specified")
|
||||
}
|
||||
v.estack.InsertAt(a, n)
|
||||
|
||||
case ROT:
|
||||
c := v.estack.Pop()
|
||||
|
@ -333,10 +359,27 @@ func (v *VM) execute(ctx *Context, op Instruction) {
|
|||
|
||||
case OVER:
|
||||
b := v.estack.Pop()
|
||||
if b == nil {
|
||||
panic("no top-level element found")
|
||||
}
|
||||
a := v.estack.Peek(0)
|
||||
if a == nil {
|
||||
panic("no second element found")
|
||||
}
|
||||
v.estack.Push(b)
|
||||
v.estack.Push(a)
|
||||
|
||||
case PICK:
|
||||
n := int(v.estack.Pop().BigInt().Int64())
|
||||
if n < 0 {
|
||||
panic("negative stack item returned")
|
||||
}
|
||||
a := v.estack.Peek(n)
|
||||
if a == nil {
|
||||
panic("no nth element found")
|
||||
}
|
||||
v.estack.Push(a)
|
||||
|
||||
case ROLL:
|
||||
n := int(v.estack.Pop().BigInt().Int64())
|
||||
if n < 0 {
|
||||
|
@ -359,6 +402,11 @@ func (v *VM) execute(ctx *Context, op Instruction) {
|
|||
v.estack.PushVal(reflect.DeepEqual(a,b))
|
||||
|
||||
// Bit operations.
|
||||
case INVERT:
|
||||
// inplace
|
||||
a := v.estack.Peek(0).BigInt()
|
||||
a.Not(a)
|
||||
|
||||
case AND:
|
||||
b := v.estack.Pop().BigInt()
|
||||
a := v.estack.Pop().BigInt()
|
||||
|
@ -499,8 +547,8 @@ func (v *VM) execute(ctx *Context, op Instruction) {
|
|||
v.estack.PushVal(!x)
|
||||
|
||||
case NZ:
|
||||
panic("todo NZ")
|
||||
// x := v.estack.Pop().BigInt()
|
||||
x := v.estack.Pop().BigInt()
|
||||
v.estack.PushVal(x.Cmp(big.NewInt(0)) != 0)
|
||||
|
||||
// Object operations.
|
||||
case NEWARRAY:
|
||||
|
@ -529,10 +577,6 @@ func (v *VM) execute(ctx *Context, op Instruction) {
|
|||
panic("APPEND: not of underlying type Array")
|
||||
}
|
||||
|
||||
case REVERSE:
|
||||
|
||||
case REMOVE:
|
||||
|
||||
case PACK:
|
||||
n := int(v.estack.Pop().BigInt().Int64())
|
||||
if n < 0 || n > v.estack.Len() {
|
||||
|
@ -546,9 +590,6 @@ func (v *VM) execute(ctx *Context, op Instruction) {
|
|||
|
||||
v.estack.PushVal(items)
|
||||
|
||||
case UNPACK:
|
||||
panic("TODO")
|
||||
|
||||
case PICKITEM:
|
||||
var (
|
||||
key = v.estack.Pop()
|
||||
|
@ -671,6 +712,10 @@ func (v *VM) execute(ctx *Context, op Instruction) {
|
|||
v.state = haltState
|
||||
}
|
||||
|
||||
case CAT, SUBSTR, LEFT, RIGHT, CHECKSIG, CHECKMULTISIG,
|
||||
UNPACK, REVERSE, REMOVE:
|
||||
panic("unimplemented")
|
||||
|
||||
// Cryptographic operations.
|
||||
case SHA1:
|
||||
b := v.estack.Pop().Bytes()
|
||||
|
@ -690,12 +735,6 @@ func (v *VM) execute(ctx *Context, op Instruction) {
|
|||
b := v.estack.Pop().Bytes()
|
||||
v.estack.PushVal(hash.DoubleSha256(b).Bytes())
|
||||
|
||||
case CHECKSIG:
|
||||
// pubkey := v.estack.Pop().Bytes()
|
||||
// sig := v.estack.Pop().Bytes()
|
||||
|
||||
case CHECKMULTISIG:
|
||||
|
||||
case NOP:
|
||||
// unlucky ^^
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ func TestInteropHook(t *testing.T) {
|
|||
EmitOpcode(buf, RET)
|
||||
v.Load(buf.Bytes())
|
||||
v.Run()
|
||||
assert.Equal(t, false, v.state.HasFlag(faultState))
|
||||
assert.Equal(t, 1, v.estack.Len())
|
||||
assert.Equal(t, big.NewInt(1), v.estack.Pop().value.Value())
|
||||
}
|
||||
|
@ -100,6 +101,7 @@ func TestAdd(t *testing.T) {
|
|||
vm.estack.PushVal(4)
|
||||
vm.estack.PushVal(2)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, int64(6), vm.estack.Pop().BigInt().Int64())
|
||||
}
|
||||
|
||||
|
@ -109,6 +111,7 @@ func TestMul(t *testing.T) {
|
|||
vm.estack.PushVal(4)
|
||||
vm.estack.PushVal(2)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, int64(8), vm.estack.Pop().BigInt().Int64())
|
||||
}
|
||||
|
||||
|
@ -118,6 +121,7 @@ func TestDiv(t *testing.T) {
|
|||
vm.estack.PushVal(4)
|
||||
vm.estack.PushVal(2)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, int64(2), vm.estack.Pop().BigInt().Int64())
|
||||
}
|
||||
|
||||
|
@ -127,6 +131,7 @@ func TestSub(t *testing.T) {
|
|||
vm.estack.PushVal(4)
|
||||
vm.estack.PushVal(2)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, int64(2), vm.estack.Pop().BigInt().Int64())
|
||||
}
|
||||
|
||||
|
@ -136,6 +141,7 @@ func TestLT(t *testing.T) {
|
|||
vm.estack.PushVal(4)
|
||||
vm.estack.PushVal(3)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, false, vm.estack.Pop().Bool())
|
||||
}
|
||||
|
||||
|
@ -145,6 +151,7 @@ func TestLTE(t *testing.T) {
|
|||
vm.estack.PushVal(2)
|
||||
vm.estack.PushVal(3)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, true, vm.estack.Pop().Bool())
|
||||
}
|
||||
|
||||
|
@ -154,6 +161,7 @@ func TestGT(t *testing.T) {
|
|||
vm.estack.PushVal(9)
|
||||
vm.estack.PushVal(3)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, true, vm.estack.Pop().Bool())
|
||||
|
||||
}
|
||||
|
@ -164,6 +172,7 @@ func TestGTE(t *testing.T) {
|
|||
vm.estack.PushVal(3)
|
||||
vm.estack.PushVal(3)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, true, vm.estack.Pop().Bool())
|
||||
}
|
||||
|
||||
|
@ -174,6 +183,7 @@ func TestDepth(t *testing.T) {
|
|||
vm.estack.PushVal(2)
|
||||
vm.estack.PushVal(3)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, int64(3), vm.estack.Pop().BigInt().Int64())
|
||||
}
|
||||
|
||||
|
@ -183,6 +193,7 @@ func TestNumEqual(t *testing.T) {
|
|||
vm.estack.PushVal(1)
|
||||
vm.estack.PushVal(2)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, false, vm.estack.Pop().Bool())
|
||||
}
|
||||
|
||||
|
@ -192,6 +203,7 @@ func TestNumNotEqual(t *testing.T) {
|
|||
vm.estack.PushVal(2)
|
||||
vm.estack.PushVal(2)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, false, vm.estack.Pop().Bool())
|
||||
}
|
||||
|
||||
|
@ -200,6 +212,7 @@ func TestINC(t *testing.T) {
|
|||
vm := load(prog)
|
||||
vm.estack.PushVal(1)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, big.NewInt(2), vm.estack.Pop().BigInt())
|
||||
}
|
||||
|
||||
|
@ -214,6 +227,7 @@ func TestAppCall(t *testing.T) {
|
|||
vm.estack.PushVal(2)
|
||||
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
elem := vm.estack.Pop() // depth should be 1
|
||||
assert.Equal(t, int64(1), elem.BigInt().Int64())
|
||||
}
|
||||
|
@ -228,9 +242,245 @@ func TestSimpleCall(t *testing.T) {
|
|||
}
|
||||
vm := load(prog)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, result, int(vm.estack.Pop().BigInt().Int64()))
|
||||
}
|
||||
|
||||
func TestNZtrue(t *testing.T) {
|
||||
prog := makeProgram(NZ)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(1)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, true, vm.estack.Pop().Bool())
|
||||
}
|
||||
|
||||
func TestNZfalse(t *testing.T) {
|
||||
prog := makeProgram(NZ)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(0)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, false, vm.estack.Pop().Bool())
|
||||
}
|
||||
|
||||
func TestPICKbadNoitem(t *testing.T) {
|
||||
prog := makeProgram(PICK)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(1)
|
||||
vm.Run()
|
||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||
}
|
||||
|
||||
func TestPICKbadNegative(t *testing.T) {
|
||||
prog := makeProgram(PICK)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(-1)
|
||||
vm.Run()
|
||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||
}
|
||||
|
||||
func TestPICKgood(t *testing.T) {
|
||||
prog := makeProgram(PICK)
|
||||
result := 2
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(0)
|
||||
vm.estack.PushVal(1)
|
||||
vm.estack.PushVal(result)
|
||||
vm.estack.PushVal(3)
|
||||
vm.estack.PushVal(4)
|
||||
vm.estack.PushVal(5)
|
||||
vm.estack.PushVal(3)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, int64(result), vm.estack.Pop().BigInt().Int64())
|
||||
}
|
||||
|
||||
func TestXTUCKbadNoitem(t *testing.T) {
|
||||
prog := makeProgram(XTUCK)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(1)
|
||||
vm.Run()
|
||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||
}
|
||||
|
||||
func TestXTUCKbadNoN(t *testing.T) {
|
||||
prog := makeProgram(XTUCK)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(1)
|
||||
vm.estack.PushVal(2)
|
||||
vm.Run()
|
||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||
}
|
||||
|
||||
func TestXTUCKbadNegative(t *testing.T) {
|
||||
prog := makeProgram(XTUCK)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(-1)
|
||||
vm.Run()
|
||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||
}
|
||||
|
||||
func TestXTUCKgood(t *testing.T) {
|
||||
prog := makeProgram(XTUCK)
|
||||
topelement := 5
|
||||
xtuckdepth := 3
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(0)
|
||||
vm.estack.PushVal(1)
|
||||
vm.estack.PushVal(2)
|
||||
vm.estack.PushVal(3)
|
||||
vm.estack.PushVal(4)
|
||||
vm.estack.PushVal(topelement)
|
||||
vm.estack.PushVal(xtuckdepth)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, int64(topelement), vm.estack.Peek(0).BigInt().Int64())
|
||||
assert.Equal(t, int64(topelement), vm.estack.Peek(xtuckdepth).BigInt().Int64())
|
||||
}
|
||||
|
||||
func TestTUCKbadNoitems(t *testing.T) {
|
||||
prog := makeProgram(TUCK)
|
||||
vm := load(prog)
|
||||
vm.Run()
|
||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||
}
|
||||
|
||||
func TestTUCKbadNoitem(t *testing.T) {
|
||||
prog := makeProgram(TUCK)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(1)
|
||||
vm.Run()
|
||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||
}
|
||||
|
||||
func TestTUCKgood(t *testing.T) {
|
||||
prog := makeProgram(TUCK)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(42)
|
||||
vm.estack.PushVal(34)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, int64(34), vm.estack.Peek(0).BigInt().Int64())
|
||||
assert.Equal(t, int64(42), vm.estack.Peek(1).BigInt().Int64())
|
||||
assert.Equal(t, int64(34), vm.estack.Peek(2).BigInt().Int64())
|
||||
}
|
||||
|
||||
func TestTUCKgood2(t *testing.T) {
|
||||
prog := makeProgram(TUCK)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(11)
|
||||
vm.estack.PushVal(42)
|
||||
vm.estack.PushVal(34)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, int64(34), vm.estack.Peek(0).BigInt().Int64())
|
||||
assert.Equal(t, int64(42), vm.estack.Peek(1).BigInt().Int64())
|
||||
assert.Equal(t, int64(34), vm.estack.Peek(2).BigInt().Int64())
|
||||
assert.Equal(t, int64(11), vm.estack.Peek(3).BigInt().Int64())
|
||||
}
|
||||
|
||||
func TestOVERbadNoitem(t *testing.T) {
|
||||
prog := makeProgram(OVER)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(1)
|
||||
vm.Run()
|
||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||
}
|
||||
|
||||
func TestOVERbadNoitems(t *testing.T) {
|
||||
prog := makeProgram(OVER)
|
||||
vm := load(prog)
|
||||
vm.Run()
|
||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||
}
|
||||
|
||||
func TestOVERgood(t *testing.T) {
|
||||
prog := makeProgram(OVER)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(42)
|
||||
vm.estack.PushVal(34)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, int64(42), vm.estack.Peek(0).BigInt().Int64())
|
||||
assert.Equal(t, int64(34), vm.estack.Peek(1).BigInt().Int64())
|
||||
assert.Equal(t, int64(42), vm.estack.Peek(2).BigInt().Int64())
|
||||
assert.Equal(t, 3, vm.estack.Len())
|
||||
}
|
||||
|
||||
func TestXDROPbadNoitem(t *testing.T) {
|
||||
prog := makeProgram(XDROP)
|
||||
vm := load(prog)
|
||||
vm.Run()
|
||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||
}
|
||||
|
||||
func TestXDROPbadNoN(t *testing.T) {
|
||||
prog := makeProgram(XDROP)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(1)
|
||||
vm.estack.PushVal(2)
|
||||
vm.Run()
|
||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||
}
|
||||
|
||||
func TestXDROPbadNegative(t *testing.T) {
|
||||
prog := makeProgram(XDROP)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(1)
|
||||
vm.estack.PushVal(-1)
|
||||
vm.Run()
|
||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||
}
|
||||
|
||||
func TestXDROPgood(t *testing.T) {
|
||||
prog := makeProgram(XDROP)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(0)
|
||||
vm.estack.PushVal(1)
|
||||
vm.estack.PushVal(2)
|
||||
vm.estack.PushVal(2)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, 2, vm.estack.Len())
|
||||
assert.Equal(t, int64(2), vm.estack.Peek(0).BigInt().Int64())
|
||||
assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64())
|
||||
}
|
||||
|
||||
func TestINVERTbadNoitem(t *testing.T) {
|
||||
prog := makeProgram(INVERT)
|
||||
vm := load(prog)
|
||||
vm.Run()
|
||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||
}
|
||||
|
||||
func TestINVERTgood1(t *testing.T) {
|
||||
prog := makeProgram(INVERT)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(0)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, int64(-1), vm.estack.Peek(0).BigInt().Int64())
|
||||
}
|
||||
|
||||
func TestINVERTgood2(t *testing.T) {
|
||||
prog := makeProgram(INVERT)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(-1)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, int64(0), vm.estack.Peek(0).BigInt().Int64())
|
||||
}
|
||||
|
||||
func TestINVERTgood3(t *testing.T) {
|
||||
prog := makeProgram(INVERT)
|
||||
vm := load(prog)
|
||||
vm.estack.PushVal(0x69)
|
||||
vm.Run()
|
||||
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||
assert.Equal(t, int64(-0x6A), vm.estack.Peek(0).BigInt().Int64())
|
||||
}
|
||||
|
||||
func makeProgram(opcodes ...Instruction) []byte {
|
||||
prog := make([]byte, len(opcodes)+1) // RET
|
||||
for i := 0; i < len(opcodes); i++ {
|
||||
|
|
Loading…
Reference in a new issue