From 42e4021898798b9ab6974e99c3cc4a587fe17662 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 4 May 2022 13:27:41 +0300 Subject: [PATCH 1/2] core: block destroyed contracts --- pkg/core/native/contract.go | 1 + pkg/core/native/management.go | 7 ++++++- pkg/core/native/management_test.go | 4 ++++ pkg/core/native/native_test/management_test.go | 2 ++ pkg/core/native/policy.go | 13 ++++++++----- 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/pkg/core/native/contract.go b/pkg/core/native/contract.go index d6deaa375..b1e6a0f2f 100644 --- a/pkg/core/native/contract.go +++ b/pkg/core/native/contract.go @@ -82,6 +82,7 @@ func NewContracts(cfg config.ProtocolConfiguration) *Contracts { neo.Policy = policy gas.NEO = neo mgmt.NEO = neo + mgmt.Policy = policy policy.NEO = neo cs.GAS = gas diff --git a/pkg/core/native/management.go b/pkg/core/native/management.go index 6ed98cd97..57bccf7ef 100644 --- a/pkg/core/native/management.go +++ b/pkg/core/native/management.go @@ -28,7 +28,8 @@ import ( // Management is contract-managing native contract. type Management struct { interop.ContractMD - NEO *NEO + NEO *NEO + Policy *Policy } type ManagementCache struct { @@ -286,6 +287,9 @@ func (m *Management) markUpdated(d *dao.Simple, hash util.Uint160, cs *state.Con // It doesn't run _deploy method and doesn't emit notification. func (m *Management) Deploy(d *dao.Simple, sender util.Uint160, neff *nef.File, manif *manifest.Manifest) (*state.Contract, error) { h := state.CreateContractHash(sender, neff.Checksum, manif.Name) + if m.Policy.IsBlocked(d, h) { + return nil, fmt.Errorf("the contract %s has been blocked", h.StringLE()) + } _, err := m.GetContract(d, h) if err == nil { return nil, errors.New("contract already exists") @@ -404,6 +408,7 @@ func (m *Management) Destroy(d *dao.Simple, hash util.Uint160) error { d.DeleteStorageItem(contract.ID, k) return true }) + m.Policy.blockAccountInternal(d, hash) m.markUpdated(d, hash, nil) return nil } diff --git a/pkg/core/native/management_test.go b/pkg/core/native/management_test.go index bb669a961..1f8b92b89 100644 --- a/pkg/core/native/management_test.go +++ b/pkg/core/native/management_test.go @@ -17,9 +17,11 @@ import ( func TestDeployGetUpdateDestroyContract(t *testing.T) { mgmt := newManagement() + mgmt.Policy = newPolicy() d := dao.NewSimple(storage.NewMemoryStore(), false, false) err := mgmt.Initialize(&interop.Context{DAO: d}) require.NoError(t, err) + require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d})) script := []byte{byte(opcode.RET)} sender := util.Uint160{1, 2, 3} ne, err := nef.NewFile(script) @@ -86,9 +88,11 @@ func TestManagement_Initialize(t *testing.T) { func TestManagement_GetNEP17Contracts(t *testing.T) { mgmt := newManagement() + mgmt.Policy = newPolicy() d := dao.NewSimple(storage.NewMemoryStore(), false, false) err := mgmt.Initialize(&interop.Context{DAO: d}) require.NoError(t, err) + require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d})) err = mgmt.InitializeCache(d) require.NoError(t, err) diff --git a/pkg/core/native/native_test/management_test.go b/pkg/core/native/native_test/management_test.go index 6ff49178a..e2c690c16 100644 --- a/pkg/core/native/native_test/management_test.go +++ b/pkg/core/native/native_test/management_test.go @@ -563,5 +563,7 @@ func TestManagement_ContractDestroy(t *testing.T) { t.Run("check contract", func(t *testing.T) { managementInvoker.Invoke(t, stackitem.Null{}, "getContract", cs1.Hash.BytesBE()) }) + // deploy after destroy should fail + managementInvoker.InvokeFail(t, fmt.Sprintf("the contract %s has been blocked", cs1.Hash.StringLE()), "deploy", nefBytes, manifestBytes) }) } diff --git a/pkg/core/native/policy.go b/pkg/core/native/policy.go index 9dd40876d..1f6e99a29 100644 --- a/pkg/core/native/policy.go +++ b/pkg/core/native/policy.go @@ -317,20 +317,23 @@ func (p *Policy) blockAccount(ic *interop.Context, args []stackitem.Item) stacki panic("cannot block native contract") } } - i, blocked := p.isBlockedInternal(ic.DAO, hash) + return stackitem.NewBool(p.blockAccountInternal(ic.DAO, hash)) +} +func (p *Policy) blockAccountInternal(d *dao.Simple, hash util.Uint160) bool { + i, blocked := p.isBlockedInternal(d, hash) if blocked { - return stackitem.NewBool(false) + return false } key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...) - ic.DAO.PutStorageItem(p.ID, key, state.StorageItem{}) - cache := ic.DAO.GetRWCache(p.ID).(*PolicyCache) + d.PutStorageItem(p.ID, key, state.StorageItem{}) + cache := d.GetRWCache(p.ID).(*PolicyCache) if len(cache.blockedAccounts) == i { cache.blockedAccounts = append(cache.blockedAccounts, hash) } else { cache.blockedAccounts = append(cache.blockedAccounts[:i+1], cache.blockedAccounts[i:]...) cache.blockedAccounts[i] = hash } - return stackitem.NewBool(true) + return true } // unblockAccount is Policy contract method and removes given account hash from From ffdcdf4a95cd81b252454a74660e2df0ef573723 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 4 May 2022 13:37:18 +0300 Subject: [PATCH 2/2] core: restrict the maximum number of contract updates --- pkg/core/native/management.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/core/native/management.go b/pkg/core/native/management.go index 57bccf7ef..bd9957521 100644 --- a/pkg/core/native/management.go +++ b/pkg/core/native/management.go @@ -353,6 +353,9 @@ func (m *Management) Update(d *dao.Simple, hash util.Uint160, neff *nef.File, ma if err != nil { return nil, errors.New("contract doesn't exist") } + if oldcontract.UpdateCounter == math.MaxUint16 { + return nil, errors.New("the contract reached the maximum number of updates") + } contract = *oldcontract // Make a copy, don't ruin (potentially) cached contract. // if NEF was provided, update the contract script