From 9b4fd99fbc0dd040c03eeef4cb7fcad01ee47b9a Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 3 Mar 2020 12:52:56 +0300 Subject: [PATCH] vm: break circular references when recursing into ToContractParameters Reference types can have circular pointers to each other, thus we need to control recursion. --- pkg/rpc/response/result/application_log.go | 4 +- pkg/vm/context.go | 2 +- pkg/vm/stack_item.go | 51 +++++++++++++--------- pkg/vm/stack_item_test.go | 3 +- 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/pkg/rpc/response/result/application_log.go b/pkg/rpc/response/result/application_log.go index 1788d87cd..9e4f7fcc8 100644 --- a/pkg/rpc/response/result/application_log.go +++ b/pkg/rpc/response/result/application_log.go @@ -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, diff --git a/pkg/vm/context.go b/pkg/vm/context.go index bfabd0a34..462531711 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -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") } diff --git a/pkg/vm/stack_item.go b/pkg/vm/stack_item.go index 7c19e0b33..d7184abdf 100644 --- a/pkg/vm/stack_item.go +++ b/pkg/vm/stack_item.go @@ -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, diff --git a/pkg/vm/stack_item_test.go b/pkg/vm/stack_item_test.go index af54b4110..24ca5f2f9 100644 --- a/pkg/vm/stack_item_test.go +++ b/pkg/vm/stack_item_test.go @@ -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) } }