From b1324db8478a814279c9d48d65d576136300af47 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 16 Dec 2020 16:41:55 +0300 Subject: [PATCH] core: add notifications to ManagmentContract --- pkg/core/native/management.go | 34 ++++++++++++++++++++++++------ pkg/core/native_management_test.go | 27 ++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/pkg/core/native/management.go b/pkg/core/native/management.go index 0462d43a1..b28357537 100644 --- a/pkg/core/native/management.go +++ b/pkg/core/native/management.go @@ -38,7 +38,10 @@ const StoragePrice = 100000 const ( prefixContract = 8 - defaultMinimumDeploymentFee = 10_00000000 + defaultMinimumDeploymentFee = 10_00000000 + contractDeployNotificationName = "Deploy" + contractUpdateNotificationName = "Update" + contractDestroyNotificationName = "Destroy" ) var ( @@ -68,17 +71,17 @@ func newManagement() *Management { desc = newDescriptor("deploy", smartcontract.ArrayType, manifest.NewParameter("script", smartcontract.ByteArrayType), manifest.NewParameter("manifest", smartcontract.ByteArrayType)) - md = newMethodAndPrice(m.deploy, 0, smartcontract.WriteStates) + md = newMethodAndPrice(m.deploy, 0, smartcontract.WriteStates|smartcontract.AllowNotify) m.AddMethod(md, desc) desc = newDescriptor("update", smartcontract.VoidType, manifest.NewParameter("script", smartcontract.ByteArrayType), manifest.NewParameter("manifest", smartcontract.ByteArrayType)) - md = newMethodAndPrice(m.update, 0, smartcontract.WriteStates) + md = newMethodAndPrice(m.update, 0, smartcontract.WriteStates|smartcontract.AllowNotify) m.AddMethod(md, desc) desc = newDescriptor("destroy", smartcontract.VoidType) - md = newMethodAndPrice(m.destroy, 1000000, smartcontract.WriteStates) + md = newMethodAndPrice(m.destroy, 1000000, smartcontract.WriteStates|smartcontract.AllowNotify) m.AddMethod(md, desc) desc = newDescriptor("getMinimumDeploymentFee", smartcontract.IntegerType) @@ -89,6 +92,11 @@ func newManagement() *Management { manifest.NewParameter("value", smartcontract.IntegerType)) md = newMethodAndPrice(m.setMinimumDeploymentFee, 300_0000, smartcontract.WriteStates) m.AddMethod(md, desc) + + hashParam := manifest.NewParameter("Hash", smartcontract.Hash160Type) + m.AddEvent(contractDeployNotificationName, hashParam) + m.AddEvent(contractUpdateNotificationName, hashParam) + m.AddEvent(contractDestroyNotificationName, hashParam) return m } @@ -214,6 +222,7 @@ func (m *Management) deploy(ic *interop.Context, args []stackitem.Item) stackite panic(err) } callDeploy(ic, newcontract, false) + m.emitNotification(ic, contractDeployNotificationName, newcontract.Hash) return contractToStack(newcontract) } @@ -225,7 +234,7 @@ func (m *Management) markUpdated(h util.Uint160) { } // Deploy creates contract's hash/ID and saves new contract into the given DAO. -// It doesn't run _deploy method. +// It doesn't run _deploy method and doesn't emit notification. func (m *Management) Deploy(d dao.DAO, sender util.Uint160, neff *nef.File, manif *manifest.Manifest) (*state.Contract, error) { h := state.CreateContractHash(sender, neff.Script) key := makeContractKey(h) @@ -269,11 +278,12 @@ func (m *Management) update(ic *interop.Context, args []stackitem.Item) stackite panic(err) } callDeploy(ic, contract, true) + m.emitNotification(ic, contractUpdateNotificationName, contract.Hash) return stackitem.Null{} } // Update updates contract's script and/or manifest in the given DAO. -// It doesn't run _deploy method. +// It doesn't run _deploy method and doesn't emit notification. func (m *Management) Update(d dao.DAO, hash util.Uint160, neff *nef.File, manif *manifest.Manifest) (*state.Contract, error) { contract, err := m.GetContract(d, hash) if err != nil { @@ -308,10 +318,11 @@ func (m *Management) destroy(ic *interop.Context, sis []stackitem.Item) stackite if err != nil { panic(err) } + m.emitNotification(ic, contractDestroyNotificationName, hash) return stackitem.Null{} } -// Destroy drops given contract from DAO along with its storage. +// Destroy drops given contract from DAO along with its storage. It doesn't emit notification. func (m *Management) Destroy(d dao.DAO, hash util.Uint160) error { contract, err := m.GetContract(d, hash) if err != nil { @@ -502,3 +513,12 @@ func (m *Management) getNextContractID(d dao.DAO) (int32, error) { si.Value = bigint.ToPreallocatedBytes(id, si.Value) return ret, d.PutStorageItem(m.ContractID, keyNextAvailableID, si) } + +func (m *Management) emitNotification(ic *interop.Context, name string, hash util.Uint160) { + ne := state.NotificationEvent{ + ScriptHash: m.Hash, + Name: name, + Item: stackitem.NewArray([]stackitem.Item{addrToStackItem(&hash)}), + } + ic.Notifications = append(ic.Notifications, ne) +} diff --git a/pkg/core/native_management_test.go b/pkg/core/native_management_test.go index 41e0e0883..43a0553f0 100644 --- a/pkg/core/native_management_test.go +++ b/pkg/core/native_management_test.go @@ -159,7 +159,11 @@ func TestContractDeploy(t *testing.T) { require.Equal(t, 1, len(res.Stack)) compareContractStates(t, cs1, res.Stack[0]) } - + require.Equal(t, aers[0].Events, []state.NotificationEvent{{ + ScriptHash: mgmtHash, + Name: "Deploy", + Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}), + }}) t.Run("_deploy called", func(t *testing.T) { res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue") require.NoError(t, err) @@ -337,7 +341,11 @@ func TestContractUpdate(t *testing.T) { require.Equal(t, vm.HaltState, aers[1].VMState) require.Equal(t, 1, len(aers[1].Stack)) compareContractStates(t, cs1, aers[1].Stack[0]) - + require.Equal(t, aers[0].Events, []state.NotificationEvent{{ + ScriptHash: mgmtHash, + Name: "Update", + Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}), + }}) t.Run("_deploy called", func(t *testing.T) { res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue") require.NoError(t, err) @@ -358,6 +366,11 @@ func TestContractUpdate(t *testing.T) { res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nil, manif1) require.NoError(t, err) require.Equal(t, vm.HaltState, res.VMState) + require.Equal(t, res.Events, []state.NotificationEvent{{ + ScriptHash: mgmtHash, + Name: "Update", + Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}), + }}) t.Run("check contract", func(t *testing.T) { checkContractState(t, bc, cs1.Hash, cs1) }) @@ -377,6 +390,11 @@ func TestContractUpdate(t *testing.T) { res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nef1b, manif1) require.NoError(t, err) require.Equal(t, vm.HaltState, res.VMState) + require.Equal(t, res.Events, []state.NotificationEvent{{ + ScriptHash: mgmtHash, + Name: "Update", + Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}), + }}) t.Run("check contract", func(t *testing.T) { checkContractState(t, bc, cs1.Hash, cs1) }) @@ -432,6 +450,11 @@ func TestContractDestroy(t *testing.T) { res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "destroy") require.NoError(t, err) require.Equal(t, vm.HaltState, res.VMState) + require.Equal(t, res.Events, []state.NotificationEvent{{ + ScriptHash: mgmtHash, + Name: "Destroy", + Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(cs1.Hash.BytesBE())}), + }}) t.Run("check contract", func(t *testing.T) { res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "getContract", cs1.Hash.BytesBE()) require.NoError(t, err)