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
|
neo.Policy = policy
|
||||||
gas.NEO = neo
|
gas.NEO = neo
|
||||||
mgmt.NEO = neo
|
mgmt.NEO = neo
|
||||||
|
mgmt.Policy = policy
|
||||||
policy.NEO = neo
|
policy.NEO = neo
|
||||||
|
|
||||||
cs.GAS = gas
|
cs.GAS = gas
|
||||||
|
|
|
@ -28,7 +28,8 @@ import (
|
||||||
// Management is contract-managing native contract.
|
// Management is contract-managing native contract.
|
||||||
type Management struct {
|
type Management struct {
|
||||||
interop.ContractMD
|
interop.ContractMD
|
||||||
NEO *NEO
|
NEO *NEO
|
||||||
|
Policy *Policy
|
||||||
}
|
}
|
||||||
|
|
||||||
type ManagementCache struct {
|
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.
|
// 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) {
|
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)
|
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)
|
_, err := m.GetContract(d, h)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil, errors.New("contract already exists")
|
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 {
|
if err != nil {
|
||||||
return nil, errors.New("contract doesn't exist")
|
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.
|
contract = *oldcontract // Make a copy, don't ruin (potentially) cached contract.
|
||||||
// if NEF was provided, update the contract script
|
// 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)
|
d.DeleteStorageItem(contract.ID, k)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
m.Policy.blockAccountInternal(d, hash)
|
||||||
m.markUpdated(d, hash, nil)
|
m.markUpdated(d, hash, nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,11 @@ import (
|
||||||
|
|
||||||
func TestDeployGetUpdateDestroyContract(t *testing.T) {
|
func TestDeployGetUpdateDestroyContract(t *testing.T) {
|
||||||
mgmt := newManagement()
|
mgmt := newManagement()
|
||||||
|
mgmt.Policy = newPolicy()
|
||||||
d := dao.NewSimple(storage.NewMemoryStore(), false, false)
|
d := dao.NewSimple(storage.NewMemoryStore(), false, false)
|
||||||
err := mgmt.Initialize(&interop.Context{DAO: d})
|
err := mgmt.Initialize(&interop.Context{DAO: d})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}))
|
||||||
script := []byte{byte(opcode.RET)}
|
script := []byte{byte(opcode.RET)}
|
||||||
sender := util.Uint160{1, 2, 3}
|
sender := util.Uint160{1, 2, 3}
|
||||||
ne, err := nef.NewFile(script)
|
ne, err := nef.NewFile(script)
|
||||||
|
@ -86,9 +88,11 @@ func TestManagement_Initialize(t *testing.T) {
|
||||||
|
|
||||||
func TestManagement_GetNEP17Contracts(t *testing.T) {
|
func TestManagement_GetNEP17Contracts(t *testing.T) {
|
||||||
mgmt := newManagement()
|
mgmt := newManagement()
|
||||||
|
mgmt.Policy = newPolicy()
|
||||||
d := dao.NewSimple(storage.NewMemoryStore(), false, false)
|
d := dao.NewSimple(storage.NewMemoryStore(), false, false)
|
||||||
err := mgmt.Initialize(&interop.Context{DAO: d})
|
err := mgmt.Initialize(&interop.Context{DAO: d})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}))
|
||||||
err = mgmt.InitializeCache(d)
|
err = mgmt.InitializeCache(d)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -563,5 +563,7 @@ func TestManagement_ContractDestroy(t *testing.T) {
|
||||||
t.Run("check contract", func(t *testing.T) {
|
t.Run("check contract", func(t *testing.T) {
|
||||||
managementInvoker.Invoke(t, stackitem.Null{}, "getContract", cs1.Hash.BytesBE())
|
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")
|
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 {
|
if blocked {
|
||||||
return stackitem.NewBool(false)
|
return false
|
||||||
}
|
}
|
||||||
key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...)
|
key := append([]byte{blockedAccountPrefix}, hash.BytesBE()...)
|
||||||
ic.DAO.PutStorageItem(p.ID, key, state.StorageItem{})
|
d.PutStorageItem(p.ID, key, state.StorageItem{})
|
||||||
cache := ic.DAO.GetRWCache(p.ID).(*PolicyCache)
|
cache := d.GetRWCache(p.ID).(*PolicyCache)
|
||||||
if len(cache.blockedAccounts) == i {
|
if len(cache.blockedAccounts) == i {
|
||||||
cache.blockedAccounts = append(cache.blockedAccounts, hash)
|
cache.blockedAccounts = append(cache.blockedAccounts, hash)
|
||||||
} else {
|
} else {
|
||||||
cache.blockedAccounts = append(cache.blockedAccounts[:i+1], cache.blockedAccounts[i:]...)
|
cache.blockedAccounts = append(cache.blockedAccounts[:i+1], cache.blockedAccounts[i:]...)
|
||||||
cache.blockedAccounts[i] = hash
|
cache.blockedAccounts[i] = hash
|
||||||
}
|
}
|
||||||
return stackitem.NewBool(true)
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// unblockAccount is Policy contract method and removes given account hash from
|
// unblockAccount is Policy contract method and removes given account hash from
|
||||||
|
|
Loading…
Reference in a new issue