From b3ed3d24ee69a9110916859589f80c1e700a0e35 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 6 Sep 2019 09:12:19 +0300 Subject: [PATCH] vm: implement CAT, SUBSTR, LEFT and RIGHT String instruction set. --- pkg/vm/vm.go | 21 ++++++- pkg/vm/vm_test.go | 152 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 2 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 422e8e9e6..2e19ba363 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -299,7 +299,24 @@ func (v *VM) execute(ctx *Context, op Instruction) { panic("can't TUCK with a one-element stack") } v.estack.InsertAt(a, 2) - + case CAT: + b := v.estack.Pop().Bytes() + a := v.estack.Pop().Bytes() + ab := append(a, b...) + v.estack.PushVal(ab) + case SUBSTR: + l := int(v.estack.Pop().BigInt().Int64()) + o := int(v.estack.Pop().BigInt().Int64()) + s := v.estack.Pop().Bytes() + v.estack.PushVal(s[o:o+l]) + case LEFT: + l := int(v.estack.Pop().BigInt().Int64()) + s := v.estack.Pop().Bytes() + v.estack.PushVal(s[:l]) + case RIGHT: + l := int(v.estack.Pop().BigInt().Int64()) + s := v.estack.Pop().Bytes() + v.estack.PushVal(s[len(s)-l:]) case XDROP: n := int(v.estack.Pop().BigInt().Int64()) if n < 0 { @@ -712,7 +729,7 @@ func (v *VM) execute(ctx *Context, op Instruction) { v.state = haltState } - case CAT, SUBSTR, LEFT, RIGHT, CHECKSIG, CHECKMULTISIG, + case CHECKSIG, CHECKMULTISIG, UNPACK, REVERSE, REMOVE: panic("unimplemented") diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 7abb7bc72..8109e8067 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -481,6 +481,158 @@ func TestINVERTgood3(t *testing.T) { assert.Equal(t, int64(-0x6A), vm.estack.Peek(0).BigInt().Int64()) } +func TestCATBadNoArgs(t *testing.T) { + prog := makeProgram(CAT) + vm := load(prog) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestCATBadOneArg(t *testing.T) { + prog := makeProgram(CAT) + vm := load(prog) + vm.estack.PushVal([]byte("abc")) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestCATGood(t *testing.T) { + prog := makeProgram(CAT) + vm := load(prog) + vm.estack.PushVal([]byte("abc")) + vm.estack.PushVal([]byte("def")) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, []byte("abcdef"), vm.estack.Peek(0).Bytes()) +} + +func TestSUBSTRBadNoArgs(t *testing.T) { + prog := makeProgram(SUBSTR) + vm := load(prog) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestSUBSTRBadOneArg(t *testing.T) { + prog := makeProgram(SUBSTR) + vm := load(prog) + vm.estack.PushVal(1) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestSUBSTRBadTwoArgs(t *testing.T) { + prog := makeProgram(SUBSTR) + vm := load(prog) + vm.estack.PushVal(0) + vm.estack.PushVal(2) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestSUBSTRGood(t *testing.T) { + prog := makeProgram(SUBSTR) + vm := load(prog) + vm.estack.PushVal([]byte("abcdef")) + vm.estack.PushVal(1) + vm.estack.PushVal(2) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, []byte("bc"), vm.estack.Peek(0).Bytes()) +} + +func TestSUBSTRBadOffset(t *testing.T) { + prog := makeProgram(SUBSTR) + vm := load(prog) + vm.estack.PushVal([]byte("abcdef")) + vm.estack.PushVal(6) + vm.estack.PushVal(1) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestSUBSTRBadLen(t *testing.T) { + prog := makeProgram(SUBSTR) + vm := load(prog) + vm.estack.PushVal([]byte("abcdef")) + vm.estack.PushVal(1) + vm.estack.PushVal(6) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestLEFTBadNoArgs(t *testing.T) { + prog := makeProgram(LEFT) + vm := load(prog) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestLEFTBadNoString(t *testing.T) { + prog := makeProgram(LEFT) + vm := load(prog) + vm.estack.PushVal(2) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestLEFTGood(t *testing.T) { + prog := makeProgram(LEFT) + vm := load(prog) + vm.estack.PushVal([]byte("abcdef")) + vm.estack.PushVal(2) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, []byte("ab"), vm.estack.Peek(0).Bytes()) +} + +func TestLEFTBadLen(t *testing.T) { + prog := makeProgram(LEFT) + vm := load(prog) + vm.estack.PushVal([]byte("abcdef")) + vm.estack.PushVal(8) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestRIGHTBadNoArgs(t *testing.T) { + prog := makeProgram(RIGHT) + vm := load(prog) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestRIGHTBadNoString(t *testing.T) { + prog := makeProgram(RIGHT) + vm := load(prog) + vm.estack.PushVal(2) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestRIGHTGood(t *testing.T) { + prog := makeProgram(RIGHT) + vm := load(prog) + vm.estack.PushVal([]byte("abcdef")) + vm.estack.PushVal(2) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, []byte("ef"), vm.estack.Peek(0).Bytes()) +} + +func TestRIGHTBadLen(t *testing.T) { + prog := makeProgram(RIGHT) + vm := load(prog) + vm.estack.PushVal([]byte("abcdef")) + vm.estack.PushVal(8) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + func makeProgram(opcodes ...Instruction) []byte { prog := make([]byte, len(opcodes)+1) // RET for i := 0; i < len(opcodes); i++ {