Merge pull request #2462 from nspcc-dev/block-destroyed

core: block destroyed contracts and restrict maximum contract updates
This commit is contained in:
Roman Khimov 2022-05-04 14:43:51 +03:00 committed by GitHub
commit 4adb2b95c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 24 additions and 6 deletions

View file

@ -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

View file

@ -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")
@ -349,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
@ -404,6 +411,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
}

View file

@ -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)

View file

@ -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)
})
}

View file

@ -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