Merge pull request #1259 from nspcc-dev/fix/notifications

Disallow changing notifications
This commit is contained in:
Roman Khimov 2020-08-05 19:50:45 +03:00 committed by GitHub
commit f5fbef165a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 124 additions and 2 deletions

View file

@ -47,7 +47,7 @@ func GetNotifications(ic *interop.Context, v *vm.VM) error {
ev := stackitem.NewArray([]stackitem.Item{ ev := stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(notifications[i].ScriptHash.BytesBE()), stackitem.NewByteArray(notifications[i].ScriptHash.BytesBE()),
stackitem.Make(notifications[i].Name), stackitem.Make(notifications[i].Name),
notifications[i].Item, stackitem.DeepCopy(notifications[i].Item).(*stackitem.Array),
}) })
arr.Append(ev) arr.Append(ev)
} }

View file

@ -282,7 +282,7 @@ func runtimeNotify(ic *interop.Context, v *vm.VM) error {
ne := state.NotificationEvent{ ne := state.NotificationEvent{
ScriptHash: v.GetCurrentScriptHash(), ScriptHash: v.GetCurrentScriptHash(),
Name: name, Name: name,
Item: stackitem.NewArray(args), Item: stackitem.DeepCopy(stackitem.NewArray(args)).(*stackitem.Array),
} }
ic.Notifications = append(ic.Notifications, ne) ic.Notifications = append(ic.Notifications, ne)
return nil return nil

View file

@ -1019,3 +1019,66 @@ func (i *Buffer) Convert(typ Type) (Item, error) {
func (i *Buffer) Len() int { func (i *Buffer) Len() int {
return len(i.value) return len(i.value)
} }
// DeepCopy returns new deep copy of the provided item.
// Values of Interop items are not deeply copied.
// It does preserve duplicates only for non-primitive types.
func DeepCopy(item Item) Item {
seen := make(map[Item]Item)
return deepCopy(item, seen)
}
func deepCopy(item Item, seen map[Item]Item) Item {
if it := seen[item]; it != nil {
return it
}
switch it := item.(type) {
case Null:
return Null{}
case *Array:
arr := NewArray(make([]Item, len(it.value)))
seen[item] = arr
for i := range it.value {
arr.value[i] = deepCopy(it.value[i], seen)
}
return arr
case *Struct:
arr := NewStruct(make([]Item, len(it.value)))
seen[item] = arr
for i := range it.value {
arr.value[i] = deepCopy(it.value[i], seen)
}
return arr
case *Map:
m := NewMap()
seen[item] = m
for i := range it.value {
key := deepCopy(it.value[i].Key, seen)
value := deepCopy(it.value[i].Value, seen)
m.Add(key, value)
}
return m
case *BigInteger:
bi := new(big.Int).SetBytes(it.value.Bytes())
if it.value.Sign() == -1 {
bi.Neg(bi)
}
return NewBigInteger(bi)
case *ByteArray:
val := make([]byte, len(it.value))
copy(val, it.value)
return NewByteArray(val)
case *Buffer:
val := make([]byte, len(it.value))
copy(val, it.value)
return NewBuffer(val)
case *Bool:
return NewBool(it.value)
case *Pointer:
return NewPointer(it.pos, it.script)
case *Interop:
return NewInterop(it.value)
default:
return nil
}
}

View file

@ -5,6 +5,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
var makeStackItemTestCases = []struct { var makeStackItemTestCases = []struct {
@ -412,3 +413,61 @@ func TestMarshalJSON(t *testing.T) {
assert.Equal(t, testCase.result, actual) assert.Equal(t, testCase.result, actual)
} }
} }
func TestDeepCopy(t *testing.T) {
testCases := []struct {
name string
item Item
}{
{"Integer", NewBigInteger(big.NewInt(1))},
{"ByteArray", NewByteArray([]byte{1, 2, 3})},
{"Buffer", NewBuffer([]byte{1, 2, 3})},
{"Bool", NewBool(true)},
{"Pointer", NewPointer(1, []byte{1, 2, 3})},
{"Interop", NewInterop(&[]byte{1, 2})},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actual := DeepCopy(tc.item)
require.Equal(t, tc.item, actual)
require.False(t, actual == tc.item)
})
}
t.Run("Null", func(t *testing.T) {
require.Equal(t, Null{}, DeepCopy(Null{}))
})
t.Run("Array", func(t *testing.T) {
arr := NewArray(make([]Item, 2))
arr.value[0] = NewBool(true)
arr.value[1] = arr
actual := DeepCopy(arr)
require.Equal(t, arr, actual)
require.False(t, arr == actual)
require.True(t, actual == actual.(*Array).value[1])
})
t.Run("Struct", func(t *testing.T) {
arr := NewStruct(make([]Item, 2))
arr.value[0] = NewBool(true)
arr.value[1] = arr
actual := DeepCopy(arr)
require.Equal(t, arr, actual)
require.False(t, arr == actual)
require.True(t, actual == actual.(*Struct).value[1])
})
t.Run("Map", func(t *testing.T) {
m := NewMapWithValue(make([]MapElement, 2))
m.value[0] = MapElement{Key: NewBool(true), Value: m}
m.value[1] = MapElement{Key: NewBigInteger(big.NewInt(1)), Value: NewByteArray([]byte{1, 2, 3})}
actual := DeepCopy(m)
require.Equal(t, m, actual)
require.False(t, m == actual)
require.True(t, actual == actual.(*Map).value[0].Value)
})
}