forked from TrueCloudLab/neoneo-go
Merge pull request #2462 from nspcc-dev/block-destroyed
core: block destroyed contracts and restrict maximum contract updates
This commit is contained in:
commit
4adb2b95c1
5 changed files with 24 additions and 6 deletions
|
@ -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
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
type Management struct {
|
||||
interop.ContractMD
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue