diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index 59e6261f8..0166f3f30 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -31,6 +31,7 @@ type DAO interface { GetHeaderHashes() ([]util.Uint256, error) GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error) GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error) + GetNextContractID() (int32, error) GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) GetStorageItemsWithPrefix(hash util.Uint160, prefix []byte) (map[string]*state.StorageItem, error) @@ -45,6 +46,7 @@ type DAO interface { PutCurrentHeader(hashAndIndex []byte) error PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error + PutNextContractID(id int32) error PutStorageItem(scripthash util.Uint160, key []byte, si *state.StorageItem) error PutVersion(v string) error StoreAsBlock(block *block.Block) error @@ -169,6 +171,27 @@ func (dao *Simple) DeleteContractState(hash util.Uint160) error { return dao.Store.Delete(key) } +// GetNextContractID returns id for the next contract and increases stored id. +func (dao *Simple) GetNextContractID() (int32, error) { + key := storage.SYSContractID.Bytes() + data, err := dao.Store.Get(key) + if err != nil { + if err == storage.ErrKeyNotFound { + err = nil + } + return 0, err + } + return int32(binary.LittleEndian.Uint32(data)), nil +} + +// PutNextContractID sets next contract id to id. +func (dao *Simple) PutNextContractID(id int32) error { + key := storage.SYSContractID.Bytes() + data := make([]byte, 4) + binary.LittleEndian.PutUint32(data, uint32(id)) + return dao.Store.Put(key, data) +} + // -- end contracts. // -- start nep5 balances. diff --git a/pkg/core/dao/dao_test.go b/pkg/core/dao/dao_test.go index ce29491f1..44b10e221 100644 --- a/pkg/core/dao/dao_test.go +++ b/pkg/core/dao/dao_test.go @@ -82,6 +82,17 @@ func TestDeleteContractState(t *testing.T) { require.Nil(t, gotContractState) } +func TestSimple_GetNextContractID(t *testing.T) { + dao := NewSimple(storage.NewMemoryStore()) + id, err := dao.GetNextContractID() + require.NoError(t, err) + require.EqualValues(t, 0, id) + require.NoError(t, dao.PutNextContractID(10)) + id, err = dao.GetNextContractID() + require.NoError(t, err) + require.EqualValues(t, 10, id) +} + func TestPutGetAppExecResult(t *testing.T) { dao := NewSimple(storage.NewMemoryStore()) hash := random.Uint256() diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index b80031e28..802e734bd 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -98,7 +98,16 @@ func contractCreate(ic *interop.Context, v *vm.VM) error { contract, err := ic.DAO.GetContractState(newcontract.ScriptHash()) if contract != nil { return errors.New("contract already exists") - } else if err := ic.DAO.PutContractState(newcontract); err != nil { + } + id, err := ic.DAO.GetNextContractID() + if err != nil { + return err + } + newcontract.ID = id + if err := ic.DAO.PutNextContractID(id); err != nil { + return err + } + if err := ic.DAO.PutContractState(newcontract); err != nil { return err } v.Estack().PushVal(stackitem.NewInterop(newcontract)) diff --git a/pkg/core/storage/store.go b/pkg/core/storage/store.go index 960f9ba89..5e70334ed 100644 --- a/pkg/core/storage/store.go +++ b/pkg/core/storage/store.go @@ -18,6 +18,7 @@ const ( IXHeaderHashList KeyPrefix = 0x80 SYSCurrentBlock KeyPrefix = 0xc0 SYSCurrentHeader KeyPrefix = 0xc1 + SYSContractID KeyPrefix = 0xc2 SYSVersion KeyPrefix = 0xf0 )