vm: break circular references when recursing into ToContractParameters

Reference types can have circular pointers to each other, thus we need to
control recursion.
This commit is contained in:
Roman Khimov 2020-03-03 12:52:56 +03:00
parent 3282c6ed41
commit 9b4fd99fbc
4 changed files with 37 additions and 23 deletions

View file

@ -6,6 +6,7 @@ import (
"github.com/CityOfZion/neo-go/pkg/core/state"
"github.com/CityOfZion/neo-go/pkg/smartcontract"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/vm"
)
// ApplicationLog wrapper used for the representation of the
@ -35,7 +36,8 @@ type NotificationEvent struct {
func NewApplicationLog(appExecRes *state.AppExecResult, scriptHash util.Uint160) ApplicationLog {
events := make([]NotificationEvent, 0, len(appExecRes.Events))
for _, e := range appExecRes.Events {
item := e.Item.ToContractParameter()
seen := make(map[vm.StackItem]bool)
item := e.Item.ToContractParameter(seen)
events = append(events, NotificationEvent{
Contract: e.ScriptHash,
Item: item,

View file

@ -171,7 +171,7 @@ func (c *Context) Dup() StackItem {
}
// ToContractParameter implements StackItem interface.
func (c *Context) ToContractParameter() smartcontract.Parameter {
func (c *Context) ToContractParameter(map[StackItem]bool) smartcontract.Parameter {
panic("Not implemented")
}

View file

@ -19,7 +19,7 @@ type StackItem interface {
// Dup duplicates current StackItem.
Dup() StackItem
// ToContractParameter converts StackItem to smartcontract.Parameter
ToContractParameter() smartcontract.Parameter
ToContractParameter(map[StackItem]bool) smartcontract.Parameter
}
func makeStackItem(v interface{}) StackItem {
@ -119,11 +119,15 @@ func (i *StructItem) Dup() StackItem {
}
// ToContractParameter implements StackItem interface.
func (i *StructItem) ToContractParameter() smartcontract.Parameter {
func (i *StructItem) ToContractParameter(seen map[StackItem]bool) smartcontract.Parameter {
var value []smartcontract.Parameter
for _, stackItem := range i.value {
parameter := stackItem.ToContractParameter()
value = append(value, parameter)
if !seen[i] {
seen[i] = true
for _, stackItem := range i.value {
parameter := stackItem.ToContractParameter(seen)
value = append(value, parameter)
}
}
return smartcontract.Parameter{
Type: smartcontract.ArrayType,
@ -179,7 +183,7 @@ func (i *BigIntegerItem) Dup() StackItem {
}
// ToContractParameter implements StackItem interface.
func (i *BigIntegerItem) ToContractParameter() smartcontract.Parameter {
func (i *BigIntegerItem) ToContractParameter(map[StackItem]bool) smartcontract.Parameter {
return smartcontract.Parameter{
Type: smartcontract.IntegerType,
Value: i.value.Int64(),
@ -223,7 +227,7 @@ func (i *BoolItem) Dup() StackItem {
}
// ToContractParameter implements StackItem interface.
func (i *BoolItem) ToContractParameter() smartcontract.Parameter {
func (i *BoolItem) ToContractParameter(map[StackItem]bool) smartcontract.Parameter {
return smartcontract.Parameter{
Type: smartcontract.BoolType,
Value: i.value,
@ -264,7 +268,7 @@ func (i *ByteArrayItem) Dup() StackItem {
}
// ToContractParameter implements StackItem interface.
func (i *ByteArrayItem) ToContractParameter() smartcontract.Parameter {
func (i *ByteArrayItem) ToContractParameter(map[StackItem]bool) smartcontract.Parameter {
return smartcontract.Parameter{
Type: smartcontract.ByteArrayType,
Value: i.value,
@ -304,11 +308,15 @@ func (i *ArrayItem) Dup() StackItem {
}
// ToContractParameter implements StackItem interface.
func (i *ArrayItem) ToContractParameter() smartcontract.Parameter {
func (i *ArrayItem) ToContractParameter(seen map[StackItem]bool) smartcontract.Parameter {
var value []smartcontract.Parameter
for _, stackItem := range i.value {
parameter := stackItem.ToContractParameter()
value = append(value, parameter)
if !seen[i] {
seen[i] = true
for _, stackItem := range i.value {
parameter := stackItem.ToContractParameter(seen)
value = append(value, parameter)
}
}
return smartcontract.Parameter{
Type: smartcontract.ArrayType,
@ -350,15 +358,18 @@ func (i *MapItem) Dup() StackItem {
}
// ToContractParameter implements StackItem interface.
func (i *MapItem) ToContractParameter() smartcontract.Parameter {
func (i *MapItem) ToContractParameter(seen map[StackItem]bool) smartcontract.Parameter {
value := make(map[smartcontract.Parameter]smartcontract.Parameter)
for key, val := range i.value {
pValue := val.ToContractParameter()
pKey := fromMapKey(key).ToContractParameter()
if pKey.Type == smartcontract.ByteArrayType {
pKey.Value = string(pKey.Value.([]byte))
if !seen[i] {
seen[i] = true
for key, val := range i.value {
pValue := val.ToContractParameter(seen)
pKey := fromMapKey(key).ToContractParameter(seen)
if pKey.Type == smartcontract.ByteArrayType {
pKey.Value = string(pKey.Value.([]byte))
}
value[pKey] = pValue
}
value[pKey] = pValue
}
return smartcontract.Parameter{
Type: smartcontract.MapType,
@ -428,7 +439,7 @@ func (i *InteropItem) Dup() StackItem {
}
// ToContractParameter implements StackItem interface.
func (i *InteropItem) ToContractParameter() smartcontract.Parameter {
func (i *InteropItem) ToContractParameter(map[StackItem]bool) smartcontract.Parameter {
return smartcontract.Parameter{
Type: smartcontract.InteropInterfaceType,
Value: nil,

View file

@ -60,7 +60,8 @@ var toContractParameterTestCases = []struct {
func TestToContractParameter(t *testing.T) {
for _, tc := range toContractParameterTestCases {
res := tc.input.ToContractParameter()
seen := make(map[StackItem]bool)
res := tc.input.ToContractParameter(seen)
assert.Equal(t, res, tc.result)
}
}