forked from TrueCloudLab/neoneo-go
core: maintain a set of NEP17-compliant contracts
This commit is contained in:
parent
ad35db66b5
commit
c0a2c74e0c
5 changed files with 112 additions and 0 deletions
|
@ -33,6 +33,8 @@ type Management struct {
|
||||||
|
|
||||||
mtx sync.RWMutex
|
mtx sync.RWMutex
|
||||||
contracts map[util.Uint160]*state.Contract
|
contracts map[util.Uint160]*state.Contract
|
||||||
|
// nep17 is a map of NEP17-compliant contracts which is updated with every PostPersist.
|
||||||
|
nep17 map[util.Uint160]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -63,6 +65,7 @@ func newManagement() *Management {
|
||||||
var m = &Management{
|
var m = &Management{
|
||||||
ContractMD: *interop.NewContractMD(nativenames.Management, managementContractID),
|
ContractMD: *interop.NewContractMD(nativenames.Management, managementContractID),
|
||||||
contracts: make(map[util.Uint160]*state.Contract),
|
contracts: make(map[util.Uint160]*state.Contract),
|
||||||
|
nep17: make(map[util.Uint160]struct{}),
|
||||||
}
|
}
|
||||||
defer m.UpdateHash()
|
defer m.UpdateHash()
|
||||||
|
|
||||||
|
@ -471,6 +474,9 @@ func (m *Management) OnPersist(ic *interop.Context) error {
|
||||||
}
|
}
|
||||||
m.mtx.Lock()
|
m.mtx.Lock()
|
||||||
m.contracts[md.Hash] = cs
|
m.contracts[md.Hash] = cs
|
||||||
|
if md.Manifest.IsStandardSupported(manifest.NEP17StandardName) {
|
||||||
|
m.nep17[md.Hash] = struct{}{}
|
||||||
|
}
|
||||||
m.mtx.Unlock()
|
m.mtx.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,6 +498,9 @@ func (m *Management) InitializeCache(d dao.DAO) error {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
m.contracts[cs.Hash] = cs
|
m.contracts[cs.Hash] = cs
|
||||||
|
if cs.Manifest.IsStandardSupported(manifest.NEP17StandardName) {
|
||||||
|
m.nep17[cs.Hash] = struct{}{}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return initErr
|
return initErr
|
||||||
}
|
}
|
||||||
|
@ -507,14 +516,33 @@ func (m *Management) PostPersist(ic *interop.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Contract was destroyed.
|
// Contract was destroyed.
|
||||||
delete(m.contracts, h)
|
delete(m.contracts, h)
|
||||||
|
delete(m.nep17, h)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
m.contracts[h] = newCs
|
m.contracts[h] = newCs
|
||||||
|
if newCs.Manifest.IsStandardSupported(manifest.NEP17StandardName) {
|
||||||
|
m.nep17[h] = struct{}{}
|
||||||
|
} else {
|
||||||
|
delete(m.nep17, h)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m.mtx.Unlock()
|
m.mtx.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetNEP17Contracts returns hashes of all deployed contracts that support NEP17 standard. The list
|
||||||
|
// is updated every PostPersist, so until PostPersist is called, the result for the previous block
|
||||||
|
// is returned.
|
||||||
|
func (m *Management) GetNEP17Contracts() []util.Uint160 {
|
||||||
|
m.mtx.RLock()
|
||||||
|
result := make([]util.Uint160, 0, len(m.nep17))
|
||||||
|
for h := range m.nep17 {
|
||||||
|
result = append(result, h)
|
||||||
|
}
|
||||||
|
m.mtx.RUnlock()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize implements Contract interface.
|
// Initialize implements Contract interface.
|
||||||
func (m *Management) Initialize(ic *interop.Context) error {
|
func (m *Management) Initialize(ic *interop.Context) error {
|
||||||
if err := setIntWithKey(m.ID, ic.DAO, keyMinimumDeploymentFee, defaultMinimumDeploymentFee); err != nil {
|
if err := setIntWithKey(m.ID, ic.DAO, keyMinimumDeploymentFee, defaultMinimumDeploymentFee); err != nil {
|
||||||
|
|
|
@ -83,3 +83,50 @@ func TestManagement_Initialize(t *testing.T) {
|
||||||
require.Error(t, mgmt.InitializeCache(d))
|
require.Error(t, mgmt.InitializeCache(d))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestManagement_GetNEP17Contracts(t *testing.T) {
|
||||||
|
mgmt := newManagement()
|
||||||
|
d := dao.NewCached(dao.NewSimple(storage.NewMemoryStore(), false))
|
||||||
|
err := mgmt.Initialize(&interop.Context{DAO: d})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Empty(t, mgmt.GetNEP17Contracts())
|
||||||
|
|
||||||
|
// Deploy NEP17 contract
|
||||||
|
script := []byte{byte(opcode.RET)}
|
||||||
|
sender := util.Uint160{1, 2, 3}
|
||||||
|
ne, err := nef.NewFile(script)
|
||||||
|
require.NoError(t, err)
|
||||||
|
manif := manifest.NewManifest("Test")
|
||||||
|
manif.ABI.Methods = append(manif.ABI.Methods, manifest.Method{
|
||||||
|
Name: "dummy",
|
||||||
|
ReturnType: smartcontract.VoidType,
|
||||||
|
Parameters: []manifest.Parameter{},
|
||||||
|
})
|
||||||
|
manif.SupportedStandards = []string{manifest.NEP17StandardName}
|
||||||
|
c1, err := mgmt.Deploy(d, sender, ne, manif)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// PostPersist is not yet called, thus no NEP17 contracts are expected
|
||||||
|
require.Empty(t, mgmt.GetNEP17Contracts())
|
||||||
|
|
||||||
|
// Call PostPersist, check c1 contract hash is returned
|
||||||
|
require.NoError(t, mgmt.PostPersist(&interop.Context{DAO: d}))
|
||||||
|
require.Equal(t, []util.Uint160{c1.Hash}, mgmt.GetNEP17Contracts())
|
||||||
|
|
||||||
|
// Update contract
|
||||||
|
manif.ABI.Methods = append(manif.ABI.Methods, manifest.Method{
|
||||||
|
Name: "dummy2",
|
||||||
|
ReturnType: smartcontract.VoidType,
|
||||||
|
Parameters: []manifest.Parameter{},
|
||||||
|
})
|
||||||
|
c2, err := mgmt.Update(d, c1.Hash, ne, manif)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// No changes expected before PostPersist call.
|
||||||
|
require.Equal(t, []util.Uint160{c1.Hash}, mgmt.GetNEP17Contracts())
|
||||||
|
|
||||||
|
// Call PostPersist, check c2 contract hash is returned
|
||||||
|
require.NoError(t, mgmt.PostPersist(&interop.Context{DAO: d}))
|
||||||
|
require.Equal(t, []util.Uint160{c2.Hash}, mgmt.GetNEP17Contracts())
|
||||||
|
}
|
||||||
|
|
|
@ -607,3 +607,18 @@ func TestMinimumDeploymentFee(t *testing.T) {
|
||||||
|
|
||||||
testGetSet(t, chain, chain.contracts.Management.Hash, "MinimumDeploymentFee", 10_00000000, 0, 0)
|
testGetSet(t, chain, chain.contracts.Management.Hash, "MinimumDeploymentFee", 10_00000000, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestManagement_GetNEP17Contracts(t *testing.T) {
|
||||||
|
t.Run("empty chain", func(t *testing.T) {
|
||||||
|
chain := newTestChain(t)
|
||||||
|
require.ElementsMatch(t, []util.Uint160{chain.contracts.NEO.Hash, chain.contracts.GAS.Hash}, chain.contracts.Management.GetNEP17Contracts())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("test chain", func(t *testing.T) {
|
||||||
|
chain := newTestChain(t)
|
||||||
|
initBasicChain(t, chain)
|
||||||
|
rublesHash, err := chain.GetContractScriptHash(1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.ElementsMatch(t, []util.Uint160{chain.contracts.NEO.Hash, chain.contracts.GAS.Hash, rublesHash}, chain.contracts.Management.GetNEP17Contracts())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -119,6 +119,16 @@ func (m *Manifest) IsValid(hash util.Uint160) error {
|
||||||
return Permissions(m.Permissions).AreValid()
|
return Permissions(m.Permissions).AreValid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsStandardSupported denotes whether the specified standard supported by the contract.
|
||||||
|
func (m *Manifest) IsStandardSupported(standard string) bool {
|
||||||
|
for _, st := range m.SupportedStandards {
|
||||||
|
if st == standard {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// ToStackItem converts Manifest to stackitem.Item.
|
// ToStackItem converts Manifest to stackitem.Item.
|
||||||
func (m *Manifest) ToStackItem() (stackitem.Item, error) {
|
func (m *Manifest) ToStackItem() (stackitem.Item, error) {
|
||||||
groups := make([]stackitem.Item, len(m.Groups))
|
groups := make([]stackitem.Item, len(m.Groups))
|
||||||
|
|
|
@ -429,3 +429,15 @@ func TestExtraToStackItem(t *testing.T) {
|
||||||
require.Equal(t, tc.expected, string(actual))
|
require.Equal(t, tc.expected, string(actual))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestManifest_IsStandardSupported(t *testing.T) {
|
||||||
|
m := &Manifest{
|
||||||
|
SupportedStandards: []string{NEP17StandardName, NEP17Payable, NEP11Payable},
|
||||||
|
}
|
||||||
|
for _, st := range m.SupportedStandards {
|
||||||
|
require.True(t, m.IsStandardSupported(st))
|
||||||
|
}
|
||||||
|
require.False(t, m.IsStandardSupported(NEP11StandardName))
|
||||||
|
require.False(t, m.IsStandardSupported(""))
|
||||||
|
require.False(t, m.IsStandardSupported("unknown standard"))
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue