diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 96a06ea54..2c86d5b33 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -982,6 +982,11 @@ func (bc *Blockchain) GetContractState(hash util.Uint160) *state.Contract { return contract } +// GetContractScriptHash returns contract script hash by its ID. +func (bc *Blockchain) GetContractScriptHash(id int32) (util.Uint160, error) { + return bc.dao.GetContractScriptHash(id) +} + // GetAccountState returns the account state from its script hash. func (bc *Blockchain) GetAccountState(scriptHash util.Uint160) *state.Account { as, err := bc.dao.GetAccountState(scriptHash) diff --git a/pkg/core/blockchainer/blockchainer.go b/pkg/core/blockchainer/blockchainer.go index 9dcac9e33..aebd61ba3 100644 --- a/pkg/core/blockchainer/blockchainer.go +++ b/pkg/core/blockchainer/blockchainer.go @@ -26,6 +26,7 @@ type Blockchainer interface { HeaderHeight() uint32 GetBlock(hash util.Uint256) (*block.Block, error) GetContractState(hash util.Uint160) *state.Contract + GetContractScriptHash(id int32) (util.Uint160, error) GetEnrollments() ([]state.Validator, error) GetGoverningTokenBalance(acc util.Uint160) (*big.Int, uint32) GetHeaderHash(int) util.Uint256 diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index f24c7267c..a867fa1eb 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -27,6 +27,7 @@ type DAO interface { GetBatch() *storage.MemBatch GetBlock(hash util.Uint256) (*block.Block, error) GetContractState(hash util.Uint160) (*state.Contract, error) + GetContractScriptHash(id int32) (util.Uint160, error) GetCurrentBlockHeight() (uint32, error) GetCurrentHeaderHeight() (i uint32, h util.Uint256, err error) GetHeaderHashes() ([]util.Uint256, error) @@ -163,7 +164,10 @@ func (dao *Simple) GetContractState(hash util.Uint160) (*state.Contract, error) // PutContractState puts given contract state into the given store. func (dao *Simple) PutContractState(cs *state.Contract) error { key := storage.AppendPrefix(storage.STContract, cs.ScriptHash().BytesBE()) - return dao.Put(cs, key) + if err := dao.Put(cs, key); err != nil { + return err + } + return dao.putContractScriptHash(cs) } // DeleteContractState deletes given contract state in the given store. @@ -187,6 +191,29 @@ func (dao *Simple) GetAndUpdateNextContractID() (int32, error) { return id, dao.Store.Put(key, data) } +// putContractScriptHash puts given contract script hash into the given store. +// It's a private method because it should be used after PutContractState to keep +// ID-Hash pair always up-to-date. +func (dao *Simple) putContractScriptHash(cs *state.Contract) error { + key := make([]byte, 5) + key[0] = byte(storage.STContractID) + binary.LittleEndian.PutUint32(key[1:], uint32(cs.ID)) + return dao.Store.Put(key, cs.ScriptHash().BytesBE()) +} + +// GetContractScriptHash returns script hash of the contract with the specified ID. +// Contract with the script hash may be destroyed. +func (dao *Simple) GetContractScriptHash(id int32) (util.Uint160, error) { + key := make([]byte, 5) + key[0] = byte(storage.STContractID) + binary.LittleEndian.PutUint32(key[1:], uint32(id)) + data := &util.Uint160{} + if err := dao.GetAndDecode(data, key); err != nil { + return *data, err + } + return *data, nil +} + // -- end contracts. // -- start nep5 balances. diff --git a/pkg/core/storage/store.go b/pkg/core/storage/store.go index 5e70334ed..d1431fce2 100644 --- a/pkg/core/storage/store.go +++ b/pkg/core/storage/store.go @@ -12,6 +12,7 @@ const ( STAccount KeyPrefix = 0x40 STNotification KeyPrefix = 0x4d STContract KeyPrefix = 0x50 + STContractID KeyPrefix = 0x51 STStorage KeyPrefix = 0x70 STNEP5Transfers KeyPrefix = 0x72 STNEP5Balances KeyPrefix = 0x73 diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index 6004c44a4..1d125f264 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -67,6 +67,9 @@ func (chain testChain) GetBlock(hash util.Uint256) (*block.Block, error) { func (chain testChain) GetContractState(hash util.Uint160) *state.Contract { panic("TODO") } +func (chain testChain) GetContractScriptHash(id int32) (util.Uint160, error) { + panic("TODO") +} func (chain testChain) GetHeaderHash(int) util.Uint256 { return util.Uint256{} }