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

View file

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

View file

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

View file

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