native: implement HF-based update

A part of #3213.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
This commit is contained in:
Anna Shaleva 2024-03-27 20:48:14 +03:00
parent 83fdcc8568
commit d62fad1268
19 changed files with 416 additions and 105 deletions

View file

@ -44,7 +44,7 @@ import (
// Tuning parameters.
const (
version = "0.2.10"
version = "0.2.11"
// DefaultInitialGAS is the default amount of GAS emitted to the standby validators
// multisignature account during native GAS contract initialization.
@ -341,16 +341,36 @@ func (bc *Blockchain) GetDesignatedByRole(r noderoles.Role) (keys.PublicKeys, ui
return res, h, err
}
// getCurrentHF returns the latest currently enabled hardfork. In case if no hardforks are enabled, the
// default config.Hardfork(0) value is returned.
func (bc *Blockchain) getCurrentHF() config.Hardfork {
var (
height = bc.BlockHeight()
current config.Hardfork
)
// Rely on the fact that hardforks list is continuous.
for _, hf := range config.Hardforks {
enableHeight, ok := bc.config.Hardforks[hf.String()]
if !ok || height < enableHeight {
break
}
current = hf
}
return current
}
// SetOracle sets oracle module. It can safely be called on the running blockchain.
// To unregister Oracle service use SetOracle(nil).
func (bc *Blockchain) SetOracle(mod native.OracleService) {
orc := bc.contracts.Oracle
currentHF := bc.getCurrentHF()
if mod != nil {
md, ok := orc.GetMethod(manifest.MethodVerify, -1)
orcMd := orc.HFSpecificContractMD(&currentHF)
md, ok := orcMd.GetMethod(manifest.MethodVerify, -1)
if !ok {
panic(fmt.Errorf("%s method not found", manifest.MethodVerify))
}
mod.UpdateNativeContract(orc.NEF.Script, orc.GetOracleResponseScript(),
mod.UpdateNativeContract(orcMd.NEF.Script, orc.GetOracleResponseScript(),
orc.Hash, md.MD.Offset)
keys, _, err := bc.GetDesignatedByRole(noderoles.Oracle)
if err != nil {
@ -487,6 +507,7 @@ func (bc *Blockchain) init() error {
// Check autogenerated native contracts' manifests and NEFs against the stored ones.
// Need to be done after native Management cache initialization to be able to get
// contract state from DAO via high-level bc API.
var current = bc.getCurrentHF()
for _, c := range bc.contracts.Contracts {
md := c.Metadata()
storedCS := bc.GetContractState(md.Hash)
@ -504,8 +525,9 @@ func (bc *Blockchain) init() error {
if err != nil {
return fmt.Errorf("failed to check native %s state against autogenerated one: %w", md.Name, err)
}
hfMD := md.HFSpecificContractMD(&current)
autogenCS := &state.Contract{
ContractBase: md.ContractBase,
ContractBase: hfMD.ContractBase,
UpdateCounter: storedCS.UpdateCounter, // it can be restored only from the DB, so use the stored value.
}
autogenCSBytes, err := stackitem.SerializeConvertible(autogenCS)
@ -2269,8 +2291,16 @@ func (bc *Blockchain) GetNativeContractScriptHash(name string) (util.Uint160, er
// GetNatives returns list of native contracts.
func (bc *Blockchain) GetNatives() []state.NativeContract {
res := make([]state.NativeContract, 0, len(bc.contracts.Contracts))
current := bc.getCurrentHF()
for _, c := range bc.contracts.Contracts {
res = append(res, c.Metadata().NativeContract)
activeIn := c.ActiveIn()
if !(activeIn == nil || activeIn.Cmp(current) <= 0) {
continue
}
md := c.Metadata().HFSpecificContractMD(&current)
res = append(res, state.NativeContract{
ContractBase: md.ContractBase,
})
}
return res
}