forked from TrueCloudLab/neoneo-go
bdb2d24a5a
VM always has istack and it doesn't even change, so doing this microallocation makes no sense. Notice that estack is a bit harder to change we do replace it in some cases and we compare pointers to it as well.
452 lines
10 KiB
Go
452 lines
10 KiB
Go
package vm
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
)
|
|
|
|
// Stack implementation for the neo-go virtual machine. The stack implements
|
|
// a double linked list where its semantics are first in first out.
|
|
// To simplify the implementation, internally a Stack s is implemented as a
|
|
// ring, such that &s.top is both the next element of the last element s.Back()
|
|
// and the previous element of the first element s.Top().
|
|
//
|
|
// s.Push(0)
|
|
// s.Push(1)
|
|
// s.Push(2)
|
|
//
|
|
// [ 2 ] > top
|
|
// [ 1 ]
|
|
// [ 0 ] > back
|
|
//
|
|
// s.Pop() > 2
|
|
//
|
|
// [ 1 ]
|
|
// [ 0 ]
|
|
|
|
// Element represents an element in the double linked list (the stack),
|
|
// which will hold the underlying stackitem.Item.
|
|
type Element struct {
|
|
value stackitem.Item
|
|
next, prev *Element
|
|
stack *Stack
|
|
}
|
|
|
|
// NewElement returns a new Element object, with its underlying value inferred
|
|
// to the corresponding type.
|
|
func NewElement(v interface{}) *Element {
|
|
return &Element{
|
|
value: stackitem.Make(v),
|
|
}
|
|
}
|
|
|
|
// Next returns the next element in the stack.
|
|
func (e *Element) Next() *Element {
|
|
if elem := e.next; e.stack != nil && elem != &e.stack.top {
|
|
return elem
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Prev returns the previous element in the stack.
|
|
func (e *Element) Prev() *Element {
|
|
if elem := e.prev; e.stack != nil && elem != &e.stack.top {
|
|
return elem
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Item returns Item contained in the element.
|
|
func (e *Element) Item() stackitem.Item {
|
|
return e.value
|
|
}
|
|
|
|
// Value returns value of the Item contained in the element.
|
|
func (e *Element) Value() interface{} {
|
|
return e.value.Value()
|
|
}
|
|
|
|
// BigInt attempts to get the underlying value of the element as a big integer.
|
|
// Will panic if the assertion failed which will be caught by the VM.
|
|
func (e *Element) BigInt() *big.Int {
|
|
val, err := e.value.TryInteger()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return val
|
|
}
|
|
|
|
// Bool converts an underlying value of the element to a boolean if it's
|
|
// possible to do so, it will panic otherwise.
|
|
func (e *Element) Bool() bool {
|
|
b, err := e.value.TryBool()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return b
|
|
}
|
|
|
|
// Bytes attempts to get the underlying value of the element as a byte array.
|
|
// Will panic if the assertion failed which will be caught by the VM.
|
|
func (e *Element) Bytes() []byte {
|
|
bs, err := e.value.TryBytes()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return bs
|
|
}
|
|
|
|
// BytesOrNil attempts to get the underlying value of the element as a byte array or nil.
|
|
// Will panic if the assertion failed which will be caught by the VM.
|
|
func (e *Element) BytesOrNil() []byte {
|
|
if _, ok := e.value.(stackitem.Null); ok {
|
|
return nil
|
|
}
|
|
bs, err := e.value.TryBytes()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return bs
|
|
}
|
|
|
|
// String attempts to get string from the element value.
|
|
// It is assumed to be use in interops and panics if string is not a valid UTF-8 byte sequence.
|
|
func (e *Element) String() string {
|
|
s, err := stackitem.ToString(e.value)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return s
|
|
}
|
|
|
|
// Array attempts to get the underlying value of the element as an array of
|
|
// other items. Will panic if the item type is different which will be caught
|
|
// by the VM.
|
|
func (e *Element) Array() []stackitem.Item {
|
|
switch t := e.value.(type) {
|
|
case *stackitem.Array:
|
|
return t.Value().([]stackitem.Item)
|
|
case *stackitem.Struct:
|
|
return t.Value().([]stackitem.Item)
|
|
default:
|
|
panic("element is not an array")
|
|
}
|
|
}
|
|
|
|
// Interop attempts to get the underlying value of the element
|
|
// as an interop item.
|
|
func (e *Element) Interop() *stackitem.Interop {
|
|
switch t := e.value.(type) {
|
|
case *stackitem.Interop:
|
|
return t
|
|
default:
|
|
panic("element is not an interop")
|
|
}
|
|
}
|
|
|
|
// Stack represents a Stack backed by a double linked list.
|
|
type Stack struct {
|
|
top Element
|
|
name string
|
|
len int
|
|
refs *refCounter
|
|
}
|
|
|
|
// NewStack returns a new stack name by the given name.
|
|
func NewStack(n string) *Stack {
|
|
return newStack(n, newRefCounter())
|
|
}
|
|
|
|
func newStack(n string, refc *refCounter) *Stack {
|
|
s := new(Stack)
|
|
initStack(s, n, refc)
|
|
return s
|
|
}
|
|
func initStack(s *Stack, n string, refc *refCounter) {
|
|
s.name = n
|
|
s.refs = refc
|
|
s.top.next = &s.top
|
|
s.top.prev = &s.top
|
|
}
|
|
|
|
// Clear clears all elements on the stack and set its length to 0.
|
|
func (s *Stack) Clear() {
|
|
s.top.next = &s.top
|
|
s.top.prev = &s.top
|
|
s.len = 0
|
|
}
|
|
|
|
// Len returns the number of elements that are on the stack.
|
|
func (s *Stack) Len() int {
|
|
return s.len
|
|
}
|
|
|
|
// insert inserts the element after element (at) on the stack.
|
|
func (s *Stack) insert(e, at *Element) *Element {
|
|
// If we insert an element that is already popped from this stack,
|
|
// we need to clean it up, there are still pointers referencing to it.
|
|
if e.stack == s {
|
|
e = NewElement(e.value)
|
|
}
|
|
|
|
n := at.next
|
|
at.next = e
|
|
e.prev = at
|
|
e.next = n
|
|
n.prev = e
|
|
e.stack = s
|
|
s.len++
|
|
|
|
s.refs.Add(e.value)
|
|
|
|
return e
|
|
}
|
|
|
|
// InsertAt inserts 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 - 1)
|
|
if before == nil {
|
|
return nil
|
|
}
|
|
return s.insert(e, before)
|
|
}
|
|
|
|
// Push pushes the given element on the stack.
|
|
func (s *Stack) Push(e *Element) {
|
|
s.insert(e, &s.top)
|
|
}
|
|
|
|
// PushVal pushes the given value on the stack. It will infer the
|
|
// underlying Item to its corresponding type.
|
|
func (s *Stack) PushVal(v interface{}) {
|
|
s.Push(NewElement(v))
|
|
}
|
|
|
|
// Pop removes and returns the element on top of the stack.
|
|
func (s *Stack) Pop() *Element {
|
|
return s.Remove(s.Top())
|
|
}
|
|
|
|
// Top returns the element on top of the stack. Nil if the stack
|
|
// is empty.
|
|
func (s *Stack) Top() *Element {
|
|
if s.len == 0 {
|
|
return nil
|
|
}
|
|
return s.top.next
|
|
}
|
|
|
|
// Back returns the element at the end of the stack. Nil if the stack
|
|
// is empty.
|
|
func (s *Stack) Back() *Element {
|
|
if s.len == 0 {
|
|
return nil
|
|
}
|
|
return s.top.prev
|
|
}
|
|
|
|
// Peek returns the element (n) far in the stack beginning from
|
|
// the top of the stack.
|
|
// n = 0 => will return the element on top of the stack.
|
|
func (s *Stack) Peek(n int) *Element {
|
|
i := 0
|
|
for e := s.Top(); e != nil; e = e.Next() {
|
|
if n == i {
|
|
return e
|
|
}
|
|
i++
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RemoveAt removes the element (n) deep on the stack beginning
|
|
// from the top of the stack.
|
|
func (s *Stack) RemoveAt(n int) *Element {
|
|
return s.Remove(s.Peek(n))
|
|
}
|
|
|
|
// Remove removes and returns the given element from the stack.
|
|
func (s *Stack) Remove(e *Element) *Element {
|
|
if e == nil {
|
|
return nil
|
|
}
|
|
e.prev.next = e.next
|
|
e.next.prev = e.prev
|
|
e.next = nil // avoid memory leaks.
|
|
e.prev = nil // avoid memory leaks.
|
|
e.stack = nil
|
|
s.len--
|
|
|
|
s.refs.Remove(e.value)
|
|
|
|
return e
|
|
}
|
|
|
|
// Dup duplicates and returns the element at position n.
|
|
// Dup is used for copying elements on to the top of its own stack.
|
|
// s.Push(s.Peek(0)) // will result in unexpected behaviour.
|
|
// s.Push(s.Dup(0)) // is the correct approach.
|
|
func (s *Stack) Dup(n int) *Element {
|
|
e := s.Peek(n)
|
|
if e == nil {
|
|
return nil
|
|
}
|
|
|
|
return &Element{
|
|
value: e.value.Dup(),
|
|
}
|
|
}
|
|
|
|
// Iter iterates over all the elements int the stack, starting from the top
|
|
// of the stack.
|
|
// s.Iter(func(elem *Element) {
|
|
// // do something with the element.
|
|
// })
|
|
func (s *Stack) Iter(f func(*Element)) {
|
|
for e := s.Top(); e != nil; e = e.Next() {
|
|
f(e)
|
|
}
|
|
}
|
|
|
|
// IterBack iterates over all the elements of the stack, starting from the bottom
|
|
// of the stack.
|
|
// s.IterBack(func(elem *Element) {
|
|
// // do something with the element.
|
|
// })
|
|
func (s *Stack) IterBack(f func(*Element)) {
|
|
for e := s.Back(); e != nil; e = e.Prev() {
|
|
f(e)
|
|
}
|
|
}
|
|
|
|
// Swap swaps two elements on the stack without popping and pushing them.
|
|
func (s *Stack) Swap(n1, n2 int) error {
|
|
if n1 < 0 || n2 < 0 {
|
|
return errors.New("negative index")
|
|
}
|
|
if n1 >= s.len || n2 >= s.len {
|
|
return errors.New("too big index")
|
|
}
|
|
if n1 == n2 {
|
|
return nil
|
|
}
|
|
s.swap(n1, n2)
|
|
return nil
|
|
}
|
|
|
|
func (s *Stack) swap(n1, n2 int) {
|
|
a := s.Peek(n1)
|
|
b := s.Peek(n2)
|
|
a.value, b.value = b.value, a.value
|
|
}
|
|
|
|
// ReverseTop reverses top n items of the stack.
|
|
func (s *Stack) ReverseTop(n int) error {
|
|
if n < 0 {
|
|
return errors.New("negative index")
|
|
} else if n > s.len {
|
|
return errors.New("too big index")
|
|
} else if n <= 1 {
|
|
return nil
|
|
}
|
|
|
|
a, b := s.Peek(0), s.Peek(n-1)
|
|
for i := 0; i < n/2; i++ {
|
|
a.value, b.value = b.value, a.value
|
|
a = a.Next()
|
|
b = b.Prev()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Roll brings an item with the given index to the top of the stack, moving all
|
|
// the other elements down accordingly. It does all of that without popping and
|
|
// pushing elements.
|
|
func (s *Stack) Roll(n int) error {
|
|
if n < 0 {
|
|
return errors.New("negative index")
|
|
}
|
|
if n >= s.len {
|
|
return errors.New("too big index")
|
|
}
|
|
if n == 0 {
|
|
return nil
|
|
}
|
|
top := s.Peek(0)
|
|
e := s.Peek(n)
|
|
|
|
e.prev.next = e.next
|
|
e.next.prev = e.prev
|
|
|
|
top.prev = e
|
|
e.next = top
|
|
|
|
e.prev = &s.top
|
|
s.top.next = e
|
|
|
|
return nil
|
|
}
|
|
|
|
// PopSigElements pops keys or signatures from the stack as needed for
|
|
// CHECKMULTISIG.
|
|
func (s *Stack) PopSigElements() ([][]byte, error) {
|
|
var num int
|
|
var elems [][]byte
|
|
item := s.Pop()
|
|
if item == nil {
|
|
return nil, fmt.Errorf("nothing on the stack")
|
|
}
|
|
switch item.value.(type) {
|
|
case *stackitem.Array:
|
|
num = len(item.Array())
|
|
if num < 1 {
|
|
return nil, fmt.Errorf("less than one element in the array")
|
|
}
|
|
elems = make([][]byte, num)
|
|
for k, v := range item.Array() {
|
|
b, ok := v.Value().([]byte)
|
|
if !ok {
|
|
return nil, fmt.Errorf("bad element %s", v.String())
|
|
}
|
|
elems[k] = b
|
|
}
|
|
default:
|
|
num = int(item.BigInt().Int64())
|
|
if num < 1 || num > s.Len() {
|
|
return nil, fmt.Errorf("wrong number of elements: %d", num)
|
|
}
|
|
elems = make([][]byte, num)
|
|
for i := 0; i < num; i++ {
|
|
elems[i] = s.Pop().Bytes()
|
|
}
|
|
}
|
|
return elems, nil
|
|
}
|
|
|
|
// ToArray converts stack to an array of stackitems with top item being the last.
|
|
func (s *Stack) ToArray() []stackitem.Item {
|
|
items := make([]stackitem.Item, 0, s.len)
|
|
s.IterBack(func(e *Element) {
|
|
items = append(items, e.Item())
|
|
})
|
|
return items
|
|
}
|
|
|
|
// MarshalJSON implements JSON marshalling interface.
|
|
func (s *Stack) MarshalJSON() ([]byte, error) {
|
|
items := s.ToArray()
|
|
arr := make([]json.RawMessage, len(items))
|
|
for i := range items {
|
|
data, err := stackitem.ToJSONWithTypes(items[i])
|
|
if err == nil {
|
|
arr[i] = data
|
|
}
|
|
}
|
|
return json.Marshal(arr)
|
|
}
|