From 1ff0caf40e69239be962263d25888975fe5a9144 Mon Sep 17 00:00:00 2001 From: BlockChainDev Date: Fri, 15 Mar 2019 22:32:08 +0000 Subject: [PATCH] Add Context stack Item --- pkg/vm/stack/context.go | 137 +++++++++++++++++++++++++++++++++++ pkg/vm/stack/context_test.go | 20 +++++ pkg/vm/stack/stackitem.go | 7 ++ 3 files changed, 164 insertions(+) create mode 100644 pkg/vm/stack/context.go create mode 100644 pkg/vm/stack/context_test.go diff --git a/pkg/vm/stack/context.go b/pkg/vm/stack/context.go new file mode 100644 index 000000000..37e342739 --- /dev/null +++ b/pkg/vm/stack/context.go @@ -0,0 +1,137 @@ +package stack + +import ( + "encoding/binary" +) + +// Context represent the current execution context of the VM. +// context will be treated as stack item +// and placed onto the invocation stack +type Context struct { + *abstractItem + + // Instruction pointer. + ip int + + // The raw program script. + prog []byte + + // Breakpoints + breakPoints []int + + // Evaluation Stack + Estack RandomAccess +} + +// NewContext return a new Context object. +func NewContext(b []byte) *Context { + return &Context{ + abstractItem: &abstractItem{}, + ip: -1, + prog: b, + breakPoints: []int{}, + } +} + +// Context overrides the default implementation +// to return a context item +func (c *Context) Context() (*Context, error) { + return c, nil +} + +// Next return the next instruction to execute. +func (c *Context) Next() Instruction { + c.ip++ + if c.ip >= len(c.prog) { + return RET + } + return Instruction(c.prog[c.ip]) +} + +// IP returns the absolute instruction without taking 0 into account. +// If that program starts the ip = 0 but IP() will return 1, cause its +// the first instruction. +func (c *Context) IP() int { + return c.ip + 1 +} + +// LenInstr returns the number of instructions loaded. +func (c *Context) LenInstr() int { + return len(c.prog) +} + +// CurrInstr returns the current instruction and opcode. +func (c *Context) CurrInstr() (int, Instruction) { + if c.ip < 0 { + return c.ip, Instruction(0x00) + } + return c.ip, Instruction(c.prog[c.ip]) +} + +// Copy returns an new exact copy of c. +func (c *Context) Copy() *Context { + return &Context{ + ip: c.ip, + prog: c.prog, + breakPoints: c.breakPoints, + } +} + +// Program returns the loaded program. +func (c *Context) Program() []byte { + return c.prog +} + +func (c *Context) atBreakPoint() bool { + for _, n := range c.breakPoints { + if n == c.ip { + return true + } + } + return false +} + +func (c *Context) String() string { + return "execution context" +} + +func (c *Context) readUint32() uint32 { + start, end := c.IP(), c.IP()+4 + if end > len(c.prog) { + return 0 + } + val := binary.LittleEndian.Uint32(c.prog[start:end]) + c.ip += 4 + return val +} + +func (c *Context) readUint16() uint16 { + start, end := c.IP(), c.IP()+2 + if end > len(c.prog) { + return 0 + } + val := binary.LittleEndian.Uint16(c.prog[start:end]) + c.ip += 2 + return val +} + +func (c *Context) readByte() byte { + return c.readBytes(1)[0] +} + +func (c *Context) readBytes(n int) []byte { + start, end := c.IP(), c.IP()+n + if end > len(c.prog) { + return nil + } + + out := make([]byte, n) + copy(out, c.prog[start:end]) + c.ip += n + return out +} + +func (c *Context) readVarBytes() []byte { + n := c.readByte() + return c.readBytes(int(n)) +} diff --git a/pkg/vm/stack/context_test.go b/pkg/vm/stack/context_test.go new file mode 100644 index 000000000..6dbe36bb6 --- /dev/null +++ b/pkg/vm/stack/context_test.go @@ -0,0 +1,20 @@ +package stack + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNextInstruction(t *testing.T) { + // PUSHBYTES1 2 + builder := NewBuilder() + builder.EmitBytes([]byte{0x02}) //[]byte{0x01, 0x02} + + ctx := NewContext(builder.Bytes()) + op := ctx.Next() + byt := ctx.readByte() + + assert.Equal(t, PUSHBYTES1, op) + assert.Equal(t, byte(2), byt) +} diff --git a/pkg/vm/stack/stackitem.go b/pkg/vm/stack/stackitem.go index 9f29f9d3b..beed13363 100644 --- a/pkg/vm/stack/stackitem.go +++ b/pkg/vm/stack/stackitem.go @@ -10,6 +10,7 @@ type Item interface { Boolean() (*Boolean, error) ByteArray() (*ByteArray, error) Array() (*Array, error) + Context() (*Context, error) } // Represents an `abstract` stack item @@ -40,3 +41,9 @@ func (a *abstractItem) ByteArray() (*ByteArray, error) { func (a *abstractItem) Array() (*Array, error) { return nil, errors.New("This stack item is not an array") } + +// Context is the default implementation for a stackItem +// Implements Item interface +func (a *abstractItem) Context() (*Context, error) { + return nil, errors.New("This stack item is not of type context") +}