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

@ -288,7 +288,7 @@ func runNativeTestCases(t *testing.T, ctr interop.ContractMD, name string, nativ
}) })
} }
func getMethod(t *testing.T, ctr interop.ContractMD, name string, params []string) interop.MethodAndPrice { func getMethod(t *testing.T, ctr interop.ContractMD, name string, params []string) interop.HFSpecificMethodAndPrice {
paramLen := len(params) paramLen := len(params)
switch { switch {
@ -308,8 +308,10 @@ func getMethod(t *testing.T, ctr interop.ContractMD, name string, params []strin
name = strings.TrimSuffix(name, "WithData") name = strings.TrimSuffix(name, "WithData")
} }
md, ok := ctr.GetMethod(name, paramLen) latestHF := config.LatestHardfork()
require.True(t, ok, ctr.Manifest.Name, name, paramLen) cMD := ctr.HFSpecificContractMD(&latestHF)
md, ok := cMD.GetMethod(name, paramLen)
require.True(t, ok, cMD.Manifest.Name, name, paramLen)
return md return md
} }

View file

@ -36,9 +36,30 @@ func init() {
} }
} }
// Cmp returns the result of hardforks comparison. It returns:
//
// -1 if hf < other
// 0 if hf == other
// +1 if hf > other
func (hf Hardfork) Cmp(other Hardfork) int {
switch {
case hf == other:
return 0
case hf < other:
return -1
default:
return 1
}
}
// IsHardforkValid denotes whether the provided string represents a valid // IsHardforkValid denotes whether the provided string represents a valid
// Hardfork name. // Hardfork name.
func IsHardforkValid(s string) bool { func IsHardforkValid(s string) bool {
_, ok := hardforks[s] _, ok := hardforks[s]
return ok return ok
} }
// LatestHardfork returns latest known hardfork.
func LatestHardfork() Hardfork {
return hfLast >> 1
}

View file

@ -44,7 +44,7 @@ import (
// Tuning parameters. // Tuning parameters.
const ( const (
version = "0.2.10" version = "0.2.11"
// DefaultInitialGAS is the default amount of GAS emitted to the standby validators // DefaultInitialGAS is the default amount of GAS emitted to the standby validators
// multisignature account during native GAS contract initialization. // 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 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. // SetOracle sets oracle module. It can safely be called on the running blockchain.
// To unregister Oracle service use SetOracle(nil). // To unregister Oracle service use SetOracle(nil).
func (bc *Blockchain) SetOracle(mod native.OracleService) { func (bc *Blockchain) SetOracle(mod native.OracleService) {
orc := bc.contracts.Oracle orc := bc.contracts.Oracle
currentHF := bc.getCurrentHF()
if mod != nil { if mod != nil {
md, ok := orc.GetMethod(manifest.MethodVerify, -1) orcMd := orc.HFSpecificContractMD(&currentHF)
md, ok := orcMd.GetMethod(manifest.MethodVerify, -1)
if !ok { if !ok {
panic(fmt.Errorf("%s method not found", manifest.MethodVerify)) 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) orc.Hash, md.MD.Offset)
keys, _, err := bc.GetDesignatedByRole(noderoles.Oracle) keys, _, err := bc.GetDesignatedByRole(noderoles.Oracle)
if err != nil { if err != nil {
@ -487,6 +507,7 @@ func (bc *Blockchain) init() error {
// Check autogenerated native contracts' manifests and NEFs against the stored ones. // 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 // Need to be done after native Management cache initialization to be able to get
// contract state from DAO via high-level bc API. // contract state from DAO via high-level bc API.
var current = bc.getCurrentHF()
for _, c := range bc.contracts.Contracts { for _, c := range bc.contracts.Contracts {
md := c.Metadata() md := c.Metadata()
storedCS := bc.GetContractState(md.Hash) storedCS := bc.GetContractState(md.Hash)
@ -504,8 +525,9 @@ func (bc *Blockchain) init() error {
if err != nil { if err != nil {
return fmt.Errorf("failed to check native %s state against autogenerated one: %w", md.Name, err) return fmt.Errorf("failed to check native %s state against autogenerated one: %w", md.Name, err)
} }
hfMD := md.HFSpecificContractMD(&current)
autogenCS := &state.Contract{ 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. UpdateCounter: storedCS.UpdateCounter, // it can be restored only from the DB, so use the stored value.
} }
autogenCSBytes, err := stackitem.SerializeConvertible(autogenCS) autogenCSBytes, err := stackitem.SerializeConvertible(autogenCS)
@ -2269,8 +2291,16 @@ func (bc *Blockchain) GetNativeContractScriptHash(name string) (util.Uint160, er
// GetNatives returns list of native contracts. // GetNatives returns list of native contracts.
func (bc *Blockchain) GetNatives() []state.NativeContract { func (bc *Blockchain) GetNatives() []state.NativeContract {
res := make([]state.NativeContract, 0, len(bc.contracts.Contracts)) res := make([]state.NativeContract, 0, len(bc.contracts.Contracts))
current := bc.getCurrentHF()
for _, c := range bc.contracts.Contracts { 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 return res
} }

View file

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"sort" "sort"
"strings" "strings"
"sync"
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
@ -140,8 +141,14 @@ type Function struct {
// Method is a signature for a native method. // Method is a signature for a native method.
type Method = func(ic *Context, args []stackitem.Item) stackitem.Item type Method = func(ic *Context, args []stackitem.Item) stackitem.Item
// MethodAndPrice is a native-contract method descriptor. // MethodAndPrice is a generic hardfork-independent native contract method descriptor.
type MethodAndPrice struct { type MethodAndPrice struct {
HFSpecificMethodAndPrice
ActiveFrom *config.Hardfork
}
// HFSpecificMethodAndPrice is a hardfork-specific native contract method descriptor.
type HFSpecificMethodAndPrice struct {
Func Method Func Method
MD *manifest.Method MD *manifest.Method
CPUFee int64 CPUFee int64
@ -150,10 +157,23 @@ type MethodAndPrice struct {
RequiredFlags callflag.CallFlag RequiredFlags callflag.CallFlag
} }
// Event is a generic hardfork-independent native contract event descriptor.
type Event struct {
HFSpecificEvent
ActiveFrom *config.Hardfork
}
// HFSpecificEvent is a hardfork-specific native contract event descriptor.
type HFSpecificEvent struct {
MD *manifest.Event
}
// Contract is an interface for all native contracts. // Contract is an interface for all native contracts.
type Contract interface { type Contract interface {
Initialize(*Context) error // Initialize performs native contract initialization on contract deploy or update.
// ActiveIn returns the hardfork native contract is active from or nil in case // Active hardfork is passed as the second argument.
Initialize(*Context, *config.Hardfork) error
// ActiveIn returns the hardfork native contract is active starting from or nil in case
// it's always active. // it's always active.
ActiveIn() *config.Hardfork ActiveIn() *config.Hardfork
// InitializeCache aimed to initialize contract's cache when the contract has // InitializeCache aimed to initialize contract's cache when the contract has
@ -161,53 +181,158 @@ type Contract interface {
// It should be called each time after node restart iff the contract was // It should be called each time after node restart iff the contract was
// deployed and no Initialize method was called. // deployed and no Initialize method was called.
InitializeCache(blockHeight uint32, d *dao.Simple) error InitializeCache(blockHeight uint32, d *dao.Simple) error
// Metadata returns generic native contract metadata.
Metadata() *ContractMD Metadata() *ContractMD
OnPersist(*Context) error OnPersist(*Context) error
PostPersist(*Context) error PostPersist(*Context) error
} }
// ContractMD represents a native contract instance. // ContractMD represents a generic hardfork-independent native contract instance.
type ContractMD struct { type ContractMD struct {
state.NativeContract ID int32
Name string Hash util.Uint160
Name string
// Methods is a generic set of contract methods with activation hardforks. Any HF-dependent part of included methods
// (offsets, in particular) must not be used, there's a mdCache field for that.
Methods []MethodAndPrice Methods []MethodAndPrice
// Events is a generic set of contract events with activation hardforks. Any HF-dependent part of events must not be
// used, there's a mdCache field for that.
Events []Event
// ActiveHFs is a map of hardforks that contract should react to. Contract update should be called for active
// hardforks. Note, that unlike the C# implementation, this map doesn't include contract's activation hardfork.
// This map is being initialized on contract creation and used as a read-only, hence, not protected
// by mutex.
ActiveHFs map[config.Hardfork]struct{}
// mdCache contains hardfork-specific ready-to-use contract descriptors. This cache is lazy and thus, protected by
// mdCacheLock.
mdCacheLock sync.RWMutex
mdCache map[config.Hardfork]*HFSpecificContractMD
// onManifestConstruction is a callback for manifest finalization.
onManifestConstruction func(*manifest.Manifest)
} }
// NewContractMD returns Contract with the specified list of methods. // HFSpecificContractMD is a hardfork-specific native contract descriptor.
func NewContractMD(name string, id int32) *ContractMD { type HFSpecificContractMD struct {
state.ContractBase
Methods []HFSpecificMethodAndPrice
Events []HFSpecificEvent
}
// NewContractMD returns Contract with the specified fields set. onManifestConstruction callback every time
// after hardfork-specific manifest creation and aimed to finalize the manifest.
func NewContractMD(name string, id int32, onManifestConstruction ...func(*manifest.Manifest)) *ContractMD {
c := &ContractMD{Name: name} c := &ContractMD{Name: name}
if len(onManifestConstruction) != 0 {
c.onManifestConstruction = onManifestConstruction[0]
}
c.ID = id c.ID = id
// NEF is now stored in the contract state and affects state dump.
// Therefore, values are taken from C# node.
c.NEF.Header.Compiler = "neo-core-v3.0"
c.NEF.Header.Magic = nef.Magic
c.NEF.Tokens = []nef.MethodToken{} // avoid `nil` result during JSON marshalling
c.Hash = state.CreateNativeContractHash(c.Name) c.Hash = state.CreateNativeContractHash(c.Name)
c.Manifest = *manifest.DefaultManifest(name) c.ActiveHFs = make(map[config.Hardfork]struct{})
c.mdCache = make(map[config.Hardfork]*HFSpecificContractMD)
return c return c
} }
// UpdateHash creates a native contract script and updates hash. // HFSpecificContractMD returns hardfork-specific native contract metadata, i.e. with methods, events and script
func (c *ContractMD) UpdateHash() { // corresponding to the specified hardfork. If hardfork is not specified, then default metadata will be returned
// (methods, events and script that are always active).
func (c *ContractMD) HFSpecificContractMD(hf *config.Hardfork) *HFSpecificContractMD {
var key config.Hardfork
if hf != nil {
key = *hf
}
c.mdCacheLock.RLock()
if md, ok := c.mdCache[key]; ok {
c.mdCacheLock.RUnlock()
return md
}
c.mdCacheLock.RUnlock()
md := c.buildHFSpecificMD(hf)
return md
}
// buildHFSpecificMD builds hardfork-specific contract descriptor that includes methods and events active starting from
// the specified hardfork or older.
func (c *ContractMD) buildHFSpecificMD(hf *config.Hardfork) *HFSpecificContractMD {
var (
abiMethods = make([]manifest.Method, 0, len(c.Methods))
methods = make([]HFSpecificMethodAndPrice, 0, len(c.Methods))
abiEvents = make([]manifest.Event, 0, len(c.Events))
events = make([]HFSpecificEvent, 0, len(c.Events))
)
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
for i := range c.Methods { for i := range c.Methods {
offset := w.Len() m := c.Methods[i]
c.Methods[i].MD.Offset = offset if !(m.ActiveFrom == nil || (hf != nil && (*m.ActiveFrom).Cmp(*hf) >= 0)) {
c.Manifest.ABI.Methods[i].Offset = offset continue
}
// Perform method descriptor copy to support independent HF-based offset update.
md := *m.MD
m.MD = &md
m.MD.Offset = w.Len()
emit.Int(w.BinWriter, 0) emit.Int(w.BinWriter, 0)
c.Methods[i].SyscallOffset = w.Len() m.SyscallOffset = w.Len()
emit.Syscall(w.BinWriter, interopnames.SystemContractCallNative) emit.Syscall(w.BinWriter, interopnames.SystemContractCallNative)
emit.Opcodes(w.BinWriter, opcode.RET) emit.Opcodes(w.BinWriter, opcode.RET)
abiMethods = append(abiMethods, *m.MD)
methods = append(methods, m.HFSpecificMethodAndPrice)
} }
if w.Err != nil { if w.Err != nil {
panic(fmt.Errorf("can't create native contract script: %w", w.Err)) panic(fmt.Errorf("can't create native contract script: %w", w.Err))
} }
for i := range c.Events {
e := c.Events[i]
if !(e.ActiveFrom == nil || (hf != nil && (*e.ActiveFrom).Cmp(*hf) >= 0)) {
continue
}
c.NEF.Script = w.Bytes() abiEvents = append(abiEvents, *e.MD)
c.NEF.Checksum = c.NEF.CalculateChecksum() events = append(events, e.HFSpecificEvent)
}
// NEF is now stored in the contract state and affects state dump.
// Therefore, values are taken from C# node.
nf := nef.File{
Header: nef.Header{
Magic: nef.Magic,
Compiler: "neo-core-v3.0",
},
Tokens: []nef.MethodToken{}, // avoid `nil` result during JSON marshalling,
Script: w.Bytes(),
}
nf.Checksum = nf.CalculateChecksum()
m := manifest.DefaultManifest(c.Name)
m.ABI.Methods = abiMethods
m.ABI.Events = abiEvents
if c.onManifestConstruction != nil {
c.onManifestConstruction(m)
}
var key config.Hardfork
if hf != nil {
key = *hf
}
md := &HFSpecificContractMD{
ContractBase: state.ContractBase{
ID: c.ID,
Hash: c.Hash,
NEF: nf,
Manifest: *m,
},
Methods: methods,
Events: events,
}
c.mdCacheLock.Lock()
c.mdCache[key] = md
c.mdCacheLock.Unlock()
return md
} }
// AddMethod adds a new method to a native contract. // AddMethod adds a new method to a native contract.
@ -215,36 +340,35 @@ func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method) {
md.MD = desc md.MD = desc
desc.Safe = md.RequiredFlags&(callflag.All^callflag.ReadOnly) == 0 desc.Safe = md.RequiredFlags&(callflag.All^callflag.ReadOnly) == 0
index := sort.Search(len(c.Manifest.ABI.Methods), func(i int) bool { index := sort.Search(len(c.Methods), func(i int) bool {
md := c.Manifest.ABI.Methods[i] md := c.Methods[i].MD
if md.Name != desc.Name { if md.Name != desc.Name {
return md.Name >= desc.Name return md.Name >= desc.Name
} }
return len(md.Parameters) > len(desc.Parameters) return len(md.Parameters) > len(desc.Parameters)
}) })
c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, manifest.Method{})
copy(c.Manifest.ABI.Methods[index+1:], c.Manifest.ABI.Methods[index:])
c.Manifest.ABI.Methods[index] = *desc
// Cache follows the same order.
c.Methods = append(c.Methods, MethodAndPrice{}) c.Methods = append(c.Methods, MethodAndPrice{})
copy(c.Methods[index+1:], c.Methods[index:]) copy(c.Methods[index+1:], c.Methods[index:])
c.Methods[index] = *md c.Methods[index] = *md
if md.ActiveFrom != nil {
c.ActiveHFs[*md.ActiveFrom] = struct{}{}
}
} }
// GetMethodByOffset returns method with the provided offset. // GetMethodByOffset returns method with the provided offset.
// Offset is offset of `System.Contract.CallNative` syscall. // Offset is offset of `System.Contract.CallNative` syscall.
func (c *ContractMD) GetMethodByOffset(offset int) (MethodAndPrice, bool) { func (c *HFSpecificContractMD) GetMethodByOffset(offset int) (HFSpecificMethodAndPrice, bool) {
for k := range c.Methods { for k := range c.Methods {
if c.Methods[k].SyscallOffset == offset { if c.Methods[k].SyscallOffset == offset {
return c.Methods[k], true return c.Methods[k], true
} }
} }
return MethodAndPrice{}, false return HFSpecificMethodAndPrice{}, false
} }
// GetMethod returns method `name` with the specified number of parameters. // GetMethod returns method `name` with the specified number of parameters.
func (c *ContractMD) GetMethod(name string, paramCount int) (MethodAndPrice, bool) { func (c *HFSpecificContractMD) GetMethod(name string, paramCount int) (HFSpecificMethodAndPrice, bool) {
index := sort.Search(len(c.Methods), func(i int) bool { index := sort.Search(len(c.Methods), func(i int) bool {
md := c.Methods[i] md := c.Methods[i]
res := strings.Compare(name, md.MD.Name) res := strings.Compare(name, md.MD.Name)
@ -261,15 +385,16 @@ func (c *ContractMD) GetMethod(name string, paramCount int) (MethodAndPrice, boo
return md, true return md, true
} }
} }
return MethodAndPrice{}, false return HFSpecificMethodAndPrice{}, false
} }
// AddEvent adds a new event to the native contract. // AddEvent adds a new event to the native contract.
func (c *ContractMD) AddEvent(name string, ps ...manifest.Parameter) { func (c *ContractMD) AddEvent(md Event) {
c.Manifest.ABI.Events = append(c.Manifest.ABI.Events, manifest.Event{ c.Events = append(c.Events, md)
Name: name,
Parameters: ps, if md.ActiveFrom != nil {
}) c.ActiveHFs[*md.ActiveFrom] = struct{}{}
}
} }
// Sort sorts interop functions by id. // Sort sorts interop functions by id.

View file

@ -12,12 +12,14 @@ import (
func TestNamesASCII(t *testing.T) { func TestNamesASCII(t *testing.T) {
cfg := config.ProtocolConfiguration{P2PSigExtensions: true} cfg := config.ProtocolConfiguration{P2PSigExtensions: true}
cs := NewContracts(cfg) cs := NewContracts(cfg)
latestHF := config.LatestHardfork()
for _, c := range cs.Contracts { for _, c := range cs.Contracts {
require.True(t, isASCII(c.Metadata().Name)) require.True(t, isASCII(c.Metadata().Name))
for _, m := range c.Metadata().Methods { hfMD := c.Metadata().HFSpecificContractMD(&latestHF)
for _, m := range hfMD.Methods {
require.True(t, isASCII(m.MD.Name)) require.True(t, isASCII(m.MD.Name))
} }
for _, e := range c.Metadata().Manifest.ABI.Events { for _, e := range hfMD.Manifest.ABI.Events {
require.True(t, isASCII(e.Name)) require.True(t, isASCII(e.Name))
} }
} }

View file

@ -12,10 +12,12 @@ import (
func TestNativeGetMethod(t *testing.T) { func TestNativeGetMethod(t *testing.T) {
cfg := config.ProtocolConfiguration{P2PSigExtensions: true} cfg := config.ProtocolConfiguration{P2PSigExtensions: true}
cs := NewContracts(cfg) cs := NewContracts(cfg)
latestHF := config.LatestHardfork()
for _, c := range cs.Contracts { for _, c := range cs.Contracts {
hfMD := c.Metadata().HFSpecificContractMD(&latestHF)
t.Run(c.Metadata().Name, func(t *testing.T) { t.Run(c.Metadata().Name, func(t *testing.T) {
for _, m := range c.Metadata().Methods { for _, m := range hfMD.Methods {
_, ok := c.Metadata().GetMethod(m.MD.Name, len(m.MD.Parameters)) _, ok := hfMD.GetMethod(m.MD.Name, len(m.MD.Parameters))
require.True(t, ok) require.True(t, ok)
} }
}) })

View file

@ -41,7 +41,6 @@ const cryptoContractID = -3
func newCrypto() *Crypto { func newCrypto() *Crypto {
c := &Crypto{ContractMD: *interop.NewContractMD(nativenames.CryptoLib, cryptoContractID)} c := &Crypto{ContractMD: *interop.NewContractMD(nativenames.CryptoLib, cryptoContractID)}
defer c.UpdateHash()
desc := newDescriptor("sha256", smartcontract.ByteArrayType, desc := newDescriptor("sha256", smartcontract.ByteArrayType,
manifest.NewParameter("data", smartcontract.ByteArrayType)) manifest.NewParameter("data", smartcontract.ByteArrayType))
@ -310,7 +309,7 @@ func (c *Crypto) Metadata() *interop.ContractMD {
} }
// Initialize implements the Contract interface. // Initialize implements the Contract interface.
func (c *Crypto) Initialize(ic *interop.Context) error { func (c *Crypto) Initialize(ic *interop.Context, hf *config.Hardfork) error {
return nil return nil
} }

View file

@ -106,7 +106,6 @@ func newDesignate(p2pSigExtensionsEnabled bool, initialNodeRoles map[noderoles.R
s := &Designate{ContractMD: *interop.NewContractMD(nativenames.Designation, designateContractID)} s := &Designate{ContractMD: *interop.NewContractMD(nativenames.Designation, designateContractID)}
s.p2pSigExtensionsEnabled = p2pSigExtensionsEnabled s.p2pSigExtensionsEnabled = p2pSigExtensionsEnabled
s.initialNodeRoles = initialNodeRoles s.initialNodeRoles = initialNodeRoles
defer s.UpdateHash()
desc := newDescriptor("getDesignatedByRole", smartcontract.ArrayType, desc := newDescriptor("getDesignatedByRole", smartcontract.ArrayType,
manifest.NewParameter("role", smartcontract.IntegerType), manifest.NewParameter("role", smartcontract.IntegerType),
@ -120,9 +119,11 @@ func newDesignate(p2pSigExtensionsEnabled bool, initialNodeRoles map[noderoles.R
md = newMethodAndPrice(s.designateAsRole, 1<<15, callflag.States|callflag.AllowNotify) md = newMethodAndPrice(s.designateAsRole, 1<<15, callflag.States|callflag.AllowNotify)
s.AddMethod(md, desc) s.AddMethod(md, desc)
s.AddEvent(DesignationEventName, eDesc := newEventDescriptor(DesignationEventName,
manifest.NewParameter("Role", smartcontract.IntegerType), manifest.NewParameter("Role", smartcontract.IntegerType),
manifest.NewParameter("BlockIndex", smartcontract.IntegerType)) manifest.NewParameter("BlockIndex", smartcontract.IntegerType))
eMD := newEvent(eDesc)
s.AddEvent(eMD)
return s return s
} }
@ -130,7 +131,11 @@ func newDesignate(p2pSigExtensionsEnabled bool, initialNodeRoles map[noderoles.R
// Initialize initializes Designation contract. It is called once at native Management's OnPersist // Initialize initializes Designation contract. It is called once at native Management's OnPersist
// at the genesis block, and we can't properly fill the cache at this point, as there are no roles // at the genesis block, and we can't properly fill the cache at this point, as there are no roles
// data in the storage. // data in the storage.
func (s *Designate) Initialize(ic *interop.Context) error { func (s *Designate) Initialize(ic *interop.Context, hf *config.Hardfork) error {
if hf != s.ActiveIn() {
return nil
}
cache := &DesignationCache{} cache := &DesignationCache{}
ic.DAO.SetCache(s.ID, cache) ic.DAO.SetCache(s.ID, cache)

View file

@ -32,17 +32,25 @@ func Call(ic *interop.Context) error {
return fmt.Errorf("native contract %s (version %d) not found", curr.StringLE(), version) return fmt.Errorf("native contract %s (version %d) not found", curr.StringLE(), version)
} }
var ( var (
meta = c.Metadata() genericMeta = c.Metadata()
activeIn = c.ActiveIn() activeIn = c.ActiveIn()
) )
if activeIn != nil { if activeIn != nil {
height, ok := ic.Hardforks[activeIn.String()] height, ok := ic.Hardforks[activeIn.String()]
// Persisting block must not be taken into account, native contract can be called // Persisting block must not be taken into account, native contract can be called
// only AFTER its initialization block persist, thus, can't use ic.IsHardforkEnabled. // only AFTER its initialization block persist, thus, can't use ic.IsHardforkEnabled.
if !ok || ic.BlockHeight() < height { if !ok || ic.BlockHeight() < height {
return fmt.Errorf("native contract %s is active after hardfork %s", meta.Name, activeIn.String()) return fmt.Errorf("native contract %s is active after hardfork %s", genericMeta.Name, activeIn.String())
} }
} }
var current config.Hardfork
for _, hf := range config.Hardforks {
if !ic.IsHardforkEnabled(hf) {
break
}
current = hf
}
meta := genericMeta.HFSpecificContractMD(&current)
m, ok := meta.GetMethodByOffset(ic.VM.Context().IP()) m, ok := meta.GetMethodByOffset(ic.VM.Context().IP())
if !ok { if !ok {
return fmt.Errorf("method not found") return fmt.Errorf("method not found")

View file

@ -31,7 +31,6 @@ func newLedger() *Ledger {
var l = &Ledger{ var l = &Ledger{
ContractMD: *interop.NewContractMD(nativenames.Ledger, ledgerContractID), ContractMD: *interop.NewContractMD(nativenames.Ledger, ledgerContractID),
} }
defer l.UpdateHash()
desc := newDescriptor("currentHash", smartcontract.Hash256Type) desc := newDescriptor("currentHash", smartcontract.Hash256Type)
md := newMethodAndPrice(l.currentHash, 1<<15, callflag.ReadStates) md := newMethodAndPrice(l.currentHash, 1<<15, callflag.ReadStates)
@ -81,7 +80,7 @@ func (l *Ledger) Metadata() *interop.ContractMD {
} }
// Initialize implements the Contract interface. // Initialize implements the Contract interface.
func (l *Ledger) Initialize(ic *interop.Context) error { func (l *Ledger) Initialize(ic *interop.Context, hf *config.Hardfork) error {
return nil return nil
} }

View file

@ -101,7 +101,6 @@ func newManagement() *Management {
var m = &Management{ var m = &Management{
ContractMD: *interop.NewContractMD(nativenames.Management, ManagementContractID), ContractMD: *interop.NewContractMD(nativenames.Management, ManagementContractID),
} }
defer m.UpdateHash()
desc := newDescriptor("getContract", smartcontract.ArrayType, desc := newDescriptor("getContract", smartcontract.ArrayType,
manifest.NewParameter("hash", smartcontract.Hash160Type)) manifest.NewParameter("hash", smartcontract.Hash160Type))
@ -164,9 +163,17 @@ func newManagement() *Management {
m.AddMethod(md, desc) m.AddMethod(md, desc)
hashParam := manifest.NewParameter("Hash", smartcontract.Hash160Type) hashParam := manifest.NewParameter("Hash", smartcontract.Hash160Type)
m.AddEvent(contractDeployNotificationName, hashParam) eDesc := newEventDescriptor(contractDeployNotificationName, hashParam)
m.AddEvent(contractUpdateNotificationName, hashParam) eMD := newEvent(eDesc)
m.AddEvent(contractDestroyNotificationName, hashParam) m.AddEvent(eMD)
eDesc = newEventDescriptor(contractUpdateNotificationName, hashParam)
eMD = newEvent(eDesc)
m.AddEvent(eMD)
eDesc = newEventDescriptor(contractDestroyNotificationName, hashParam)
eMD = newEvent(eDesc)
m.AddEvent(eMD)
return m return m
} }
@ -220,6 +227,11 @@ func (m *Management) getContractByID(ic *interop.Context, args []stackitem.Item)
// GetContract returns a contract with the given hash from the given DAO. // GetContract returns a contract with the given hash from the given DAO.
func GetContract(d *dao.Simple, hash util.Uint160) (*state.Contract, error) { func GetContract(d *dao.Simple, hash util.Uint160) (*state.Contract, error) {
cache := d.GetROCache(ManagementContractID).(*ManagementCache) cache := d.GetROCache(ManagementContractID).(*ManagementCache)
return getContract(cache, hash)
}
// getContract returns a contract with the given hash from provided RO or RW cache.
func getContract(cache *ManagementCache, hash util.Uint160) (*state.Contract, error) {
cs, ok := cache.contracts[hash] cs, ok := cache.contracts[hash]
if !ok { if !ok {
return nil, storage.ErrKeyNotFound return nil, storage.ErrKeyNotFound
@ -583,22 +595,65 @@ func updateContractCache(cache *ManagementCache, cs *state.Contract) {
func (m *Management) OnPersist(ic *interop.Context) error { func (m *Management) OnPersist(ic *interop.Context) error {
var cache *ManagementCache var cache *ManagementCache
for _, native := range ic.Natives { for _, native := range ic.Natives {
activeIn := native.ActiveIn() var (
if !(activeIn == nil && ic.Block.Index == 0 || activeIn = native.ActiveIn()
activeIn != nil && ic.IsHardforkActivation(*activeIn)) { isDeploy bool
isUpdate bool
latestHF config.Hardfork
)
activeHFs := native.Metadata().ActiveHFs
isDeploy = activeIn == nil && ic.Block.Index == 0 ||
activeIn != nil && ic.IsHardforkActivation(*activeIn)
if !isDeploy {
for _, hf := range config.Hardforks {
if _, ok := activeHFs[hf]; ok && ic.IsHardforkActivation(hf) {
isUpdate = true
activation := hf // avoid loop variable pointer exporting.
activeIn = &activation // reuse ActiveIn variable for the initialization hardfork.
// Break immediately since native Initialize should be called only for the first hardfork in a raw
// (if there are multiple hardforks with the same enabling height).
break
}
}
}
// Search for the latest active hardfork to properly construct manifest.
for _, hf := range config.Hardforks {
if _, ok := activeHFs[hf]; ok && ic.IsHardforkActivation(hf) {
latestHF = hf
}
}
if !(isDeploy || isUpdate) {
continue continue
} }
md := native.Metadata() md := native.Metadata()
cs := &state.Contract{ base := md.HFSpecificContractMD(&latestHF).ContractBase
ContractBase: md.ContractBase, var cs *state.Contract
} switch {
if err := native.Initialize(ic); err != nil { case isDeploy:
return fmt.Errorf("initializing %s native contract: %w", md.Name, err) cs = &state.Contract{
ContractBase: base,
}
case isUpdate:
if cache == nil {
cache = ic.DAO.GetRWCache(m.ID).(*ManagementCache)
}
oldcontract, err := getContract(cache, md.Hash)
if err != nil {
return fmt.Errorf("failed to retrieve native %s from cache: %w", md.Name, err)
}
contract := *oldcontract // Make a copy, don't ruin cached contract and cache.
contract.NEF = base.NEF
contract.Manifest = base.Manifest
contract.UpdateCounter++
cs = &contract
} }
err := putContractState(ic.DAO, cs, false) // Perform cache update manually. err := putContractState(ic.DAO, cs, false) // Perform cache update manually.
if err != nil { if err != nil {
return err return fmt.Errorf("failed to put contract state: %w", err)
}
if err := native.Initialize(ic, activeIn); err != nil {
return fmt.Errorf("initializing %s native contract at HF %d: %w", md.Name, activeIn, err)
} }
if cache == nil { if cache == nil {
cache = ic.DAO.GetRWCache(m.ID).(*ManagementCache) cache = ic.DAO.GetRWCache(m.ID).(*ManagementCache)
@ -666,7 +721,11 @@ func (m *Management) GetNEP17Contracts(d *dao.Simple) []util.Uint160 {
} }
// Initialize implements the Contract interface. // Initialize implements the Contract interface.
func (m *Management) Initialize(ic *interop.Context) error { func (m *Management) Initialize(ic *interop.Context, hf *config.Hardfork) error {
if hf != m.ActiveIn() {
return nil
}
setIntWithKey(m.ID, ic.DAO, keyMinimumDeploymentFee, defaultMinimumDeploymentFee) setIntWithKey(m.ID, ic.DAO, keyMinimumDeploymentFee, defaultMinimumDeploymentFee)
setIntWithKey(m.ID, ic.DAO, keyNextAvailableID, 1) setIntWithKey(m.ID, ic.DAO, keyNextAvailableID, 1)

View file

@ -20,9 +20,9 @@ func TestDeployGetUpdateDestroyContract(t *testing.T) {
mgmt.Policy = newPolicy(false) mgmt.Policy = newPolicy(false)
d := dao.NewSimple(storage.NewMemoryStore(), false) d := dao.NewSimple(storage.NewMemoryStore(), false)
ic := &interop.Context{DAO: d} ic := &interop.Context{DAO: d}
err := mgmt.Initialize(ic) err := mgmt.Initialize(ic, nil)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d})) require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}, nil))
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)
@ -97,9 +97,9 @@ func TestManagement_GetNEP17Contracts(t *testing.T) {
mgmt := newManagement() mgmt := newManagement()
mgmt.Policy = newPolicy(false) mgmt.Policy = newPolicy(false)
d := dao.NewSimple(storage.NewMemoryStore(), false) d := dao.NewSimple(storage.NewMemoryStore(), false)
err := mgmt.Initialize(&interop.Context{DAO: d}) err := mgmt.Initialize(&interop.Context{DAO: d}, nil)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d})) require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}, nil))
err = mgmt.InitializeCache(0, d) err = mgmt.InitializeCache(0, d)
require.NoError(t, err) require.NoError(t, err)

View file

@ -37,7 +37,6 @@ func newGAS(init int64, p2pSigExtensionsEnabled bool) *GAS {
initialSupply: init, initialSupply: init,
p2pSigExtensionsEnabled: p2pSigExtensionsEnabled, p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
} }
defer g.UpdateHash()
nep17 := newNEP17Native(nativenames.Gas, gasContractID) nep17 := newNEP17Native(nativenames.Gas, gasContractID)
nep17.symbol = "GAS" nep17.symbol = "GAS"
@ -83,7 +82,11 @@ func (g *GAS) balanceFromBytes(si *state.StorageItem) (*big.Int, error) {
} }
// Initialize initializes a GAS contract. // Initialize initializes a GAS contract.
func (g *GAS) Initialize(ic *interop.Context) error { func (g *GAS) Initialize(ic *interop.Context, hf *config.Hardfork) error {
if hf != g.ActiveIn() {
return nil
}
if err := g.nep17TokenNative.Initialize(ic); err != nil { if err := g.nep17TokenNative.Initialize(ic); err != nil {
return err return err
} }

View file

@ -172,7 +172,6 @@ func makeValidatorKey(key *keys.PublicKey) []byte {
// newNEO returns NEO native contract. // newNEO returns NEO native contract.
func newNEO(cfg config.ProtocolConfiguration) *NEO { func newNEO(cfg config.ProtocolConfiguration) *NEO {
n := &NEO{} n := &NEO{}
defer n.UpdateHash()
nep17 := newNEP17Native(nativenames.Neo, neoContractID) nep17 := newNEP17Native(nativenames.Neo, neoContractID)
nep17.symbol = "NEO" nep17.symbol = "NEO"
@ -258,27 +257,39 @@ func newNEO(cfg config.ProtocolConfiguration) *NEO {
md = newMethodAndPrice(n.setRegisterPrice, 1<<15, callflag.States) md = newMethodAndPrice(n.setRegisterPrice, 1<<15, callflag.States)
n.AddMethod(md, desc) n.AddMethod(md, desc)
n.AddEvent("CandidateStateChanged", eDesc := newEventDescriptor("CandidateStateChanged",
manifest.NewParameter("pubkey", smartcontract.PublicKeyType), manifest.NewParameter("pubkey", smartcontract.PublicKeyType),
manifest.NewParameter("registered", smartcontract.BoolType), manifest.NewParameter("registered", smartcontract.BoolType),
manifest.NewParameter("votes", smartcontract.IntegerType), manifest.NewParameter("votes", smartcontract.IntegerType),
) )
n.AddEvent("Vote", eMD := newEvent(eDesc)
n.AddEvent(eMD)
eDesc = newEventDescriptor("Vote",
manifest.NewParameter("account", smartcontract.Hash160Type), manifest.NewParameter("account", smartcontract.Hash160Type),
manifest.NewParameter("from", smartcontract.PublicKeyType), manifest.NewParameter("from", smartcontract.PublicKeyType),
manifest.NewParameter("to", smartcontract.PublicKeyType), manifest.NewParameter("to", smartcontract.PublicKeyType),
manifest.NewParameter("amount", smartcontract.IntegerType), manifest.NewParameter("amount", smartcontract.IntegerType),
) )
n.AddEvent("CommitteeChanged", eMD = newEvent(eDesc)
n.AddEvent(eMD)
eDesc = newEventDescriptor("CommitteeChanged",
manifest.NewParameter("old", smartcontract.ArrayType), manifest.NewParameter("old", smartcontract.ArrayType),
manifest.NewParameter("new", smartcontract.ArrayType), manifest.NewParameter("new", smartcontract.ArrayType),
) )
eMD = newEvent(eDesc)
n.AddEvent(eMD)
return n return n
} }
// Initialize initializes a NEO contract. // Initialize initializes a NEO contract.
func (n *NEO) Initialize(ic *interop.Context) error { func (n *NEO) Initialize(ic *interop.Context, hf *config.Hardfork) error {
if hf != n.ActiveIn() {
return nil
}
if err := n.nep17TokenNative.Initialize(ic); err != nil { if err := n.nep17TokenNative.Initialize(ic); err != nil {
return err return err
} }

View file

@ -6,6 +6,7 @@ import (
"math" "math"
"math/big" "math/big"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/dao"
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract" "github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
@ -45,8 +46,9 @@ func (c *nep17TokenNative) Metadata() *interop.ContractMD {
} }
func newNEP17Native(name string, id int32) *nep17TokenNative { func newNEP17Native(name string, id int32) *nep17TokenNative {
n := &nep17TokenNative{ContractMD: *interop.NewContractMD(name, id)} n := &nep17TokenNative{ContractMD: *interop.NewContractMD(name, id, func(m *manifest.Manifest) {
n.Manifest.SupportedStandards = []string{manifest.NEP17StandardName} m.SupportedStandards = []string{manifest.NEP17StandardName}
})}
desc := newDescriptor("symbol", smartcontract.StringType) desc := newDescriptor("symbol", smartcontract.StringType)
md := newMethodAndPrice(n.Symbol, 0, callflag.NoneFlag) md := newMethodAndPrice(n.Symbol, 0, callflag.NoneFlag)
@ -77,7 +79,9 @@ func newNEP17Native(name string, id int32) *nep17TokenNative {
md.StorageFee = 50 md.StorageFee = 50
n.AddMethod(md, desc) n.AddMethod(md, desc)
n.AddEvent("Transfer", transferParams...) eDesc := newEventDescriptor("Transfer", transferParams...)
eMD := newEvent(eDesc)
n.AddEvent(eMD)
return n return n
} }
@ -319,12 +323,40 @@ func newDescriptor(name string, ret smartcontract.ParamType, ps ...manifest.Para
} }
} }
func newMethodAndPrice(f interop.Method, cpuFee int64, flags callflag.CallFlag) *interop.MethodAndPrice { func newMethodAndPrice(f interop.Method, cpuFee int64, flags callflag.CallFlag, activeFrom ...config.Hardfork) *interop.MethodAndPrice {
return &interop.MethodAndPrice{ md := &interop.MethodAndPrice{
Func: f, HFSpecificMethodAndPrice: interop.HFSpecificMethodAndPrice{
CPUFee: cpuFee, Func: f,
RequiredFlags: flags, CPUFee: cpuFee,
RequiredFlags: flags,
},
} }
if len(activeFrom) != 0 {
md.ActiveFrom = &activeFrom[0]
}
return md
}
func newEventDescriptor(name string, ps ...manifest.Parameter) *manifest.Event {
if len(ps) == 0 {
ps = []manifest.Parameter{}
}
return &manifest.Event{
Name: name,
Parameters: ps,
}
}
func newEvent(desc *manifest.Event, activeFrom ...config.Hardfork) interop.Event {
md := interop.Event{
HFSpecificEvent: interop.HFSpecificEvent{
MD: desc,
},
}
if len(activeFrom) != 0 {
md.ActiveFrom = &activeFrom[0]
}
return md
} }
func toBigInt(s stackitem.Item) *big.Int { func toBigInt(s stackitem.Item) *big.Int {

View file

@ -73,7 +73,6 @@ func copyNotaryCache(src, dst *NotaryCache) {
// newNotary returns Notary native contract. // newNotary returns Notary native contract.
func newNotary() *Notary { func newNotary() *Notary {
n := &Notary{ContractMD: *interop.NewContractMD(nativenames.Notary, notaryContractID)} n := &Notary{ContractMD: *interop.NewContractMD(nativenames.Notary, notaryContractID)}
defer n.UpdateHash()
desc := newDescriptor("onNEP17Payment", smartcontract.VoidType, desc := newDescriptor("onNEP17Payment", smartcontract.VoidType,
manifest.NewParameter("from", smartcontract.Hash160Type), manifest.NewParameter("from", smartcontract.Hash160Type),
@ -127,7 +126,11 @@ func (n *Notary) Metadata() *interop.ContractMD {
} }
// Initialize initializes Notary native contract and implements the Contract interface. // Initialize initializes Notary native contract and implements the Contract interface.
func (n *Notary) Initialize(ic *interop.Context) error { func (n *Notary) Initialize(ic *interop.Context, hf *config.Hardfork) error {
if hf != n.ActiveIn() {
return nil
}
setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, defaultMaxNotValidBeforeDelta) setIntWithKey(n.ID, ic.DAO, maxNotValidBeforeDeltaKey, defaultMaxNotValidBeforeDelta)
cache := &NotaryCache{ cache := &NotaryCache{

View file

@ -118,7 +118,6 @@ func newOracle() *Oracle {
ContractMD: *interop.NewContractMD(nativenames.Oracle, oracleContractID), ContractMD: *interop.NewContractMD(nativenames.Oracle, oracleContractID),
newRequests: make(map[uint64]*state.OracleRequest), newRequests: make(map[uint64]*state.OracleRequest),
} }
defer o.UpdateHash()
o.oracleScript = CreateOracleResponseScript(o.Hash) o.oracleScript = CreateOracleResponseScript(o.Hash)
@ -139,12 +138,17 @@ func newOracle() *Oracle {
md = newMethodAndPrice(o.verify, 1<<15, callflag.NoneFlag) md = newMethodAndPrice(o.verify, 1<<15, callflag.NoneFlag)
o.AddMethod(md, desc) o.AddMethod(md, desc)
o.AddEvent("OracleRequest", manifest.NewParameter("Id", smartcontract.IntegerType), eDesc := newEventDescriptor("OracleRequest", manifest.NewParameter("Id", smartcontract.IntegerType),
manifest.NewParameter("RequestContract", smartcontract.Hash160Type), manifest.NewParameter("RequestContract", smartcontract.Hash160Type),
manifest.NewParameter("Url", smartcontract.StringType), manifest.NewParameter("Url", smartcontract.StringType),
manifest.NewParameter("Filter", smartcontract.StringType)) manifest.NewParameter("Filter", smartcontract.StringType))
o.AddEvent("OracleResponse", manifest.NewParameter("Id", smartcontract.IntegerType), eMD := newEvent(eDesc)
o.AddEvent(eMD)
eDesc = newEventDescriptor("OracleResponse", manifest.NewParameter("Id", smartcontract.IntegerType),
manifest.NewParameter("OriginalTx", smartcontract.Hash256Type)) manifest.NewParameter("OriginalTx", smartcontract.Hash256Type))
eMD = newEvent(eDesc)
o.AddEvent(eMD)
desc = newDescriptor("getPrice", smartcontract.IntegerType) desc = newDescriptor("getPrice", smartcontract.IntegerType)
md = newMethodAndPrice(o.getPrice, 1<<15, callflag.ReadStates) md = newMethodAndPrice(o.getPrice, 1<<15, callflag.ReadStates)
@ -241,7 +245,11 @@ func (o *Oracle) Metadata() *interop.ContractMD {
} }
// Initialize initializes an Oracle contract. // Initialize initializes an Oracle contract.
func (o *Oracle) Initialize(ic *interop.Context) error { func (o *Oracle) Initialize(ic *interop.Context, hf *config.Hardfork) error {
if hf != o.ActiveIn() {
return nil
}
setIntWithKey(o.ID, ic.DAO, prefixRequestID, 0) setIntWithKey(o.ID, ic.DAO, prefixRequestID, 0)
setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, DefaultOracleRequestPrice) setIntWithKey(o.ID, ic.DAO, prefixRequestPrice, DefaultOracleRequestPrice)

View file

@ -105,7 +105,6 @@ func newPolicy(p2pSigExtensionsEnabled bool) *Policy {
ContractMD: *interop.NewContractMD(nativenames.Policy, policyContractID), ContractMD: *interop.NewContractMD(nativenames.Policy, policyContractID),
p2pSigExtensionsEnabled: p2pSigExtensionsEnabled, p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
} }
defer p.UpdateHash()
desc := newDescriptor("getFeePerByte", smartcontract.IntegerType) desc := newDescriptor("getFeePerByte", smartcontract.IntegerType)
md := newMethodAndPrice(p.getFeePerByte, 1<<15, callflag.ReadStates) md := newMethodAndPrice(p.getFeePerByte, 1<<15, callflag.ReadStates)
@ -169,7 +168,11 @@ func (p *Policy) Metadata() *interop.ContractMD {
} }
// Initialize initializes Policy native contract and implements the Contract interface. // Initialize initializes Policy native contract and implements the Contract interface.
func (p *Policy) Initialize(ic *interop.Context) error { func (p *Policy) Initialize(ic *interop.Context, hf *config.Hardfork) error {
if hf != p.ActiveIn() {
return nil
}
setIntWithKey(p.ID, ic.DAO, feePerByteKey, defaultFeePerByte) setIntWithKey(p.ID, ic.DAO, feePerByteKey, defaultFeePerByte)
setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, defaultExecFeeFactor) setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, defaultExecFeeFactor)
setIntWithKey(p.ID, ic.DAO, storagePriceKey, DefaultStoragePrice) setIntWithKey(p.ID, ic.DAO, storagePriceKey, DefaultStoragePrice)

View file

@ -46,7 +46,6 @@ var (
func newStd() *Std { func newStd() *Std {
s := &Std{ContractMD: *interop.NewContractMD(nativenames.StdLib, stdContractID)} s := &Std{ContractMD: *interop.NewContractMD(nativenames.StdLib, stdContractID)}
defer s.UpdateHash()
desc := newDescriptor("serialize", smartcontract.ByteArrayType, desc := newDescriptor("serialize", smartcontract.ByteArrayType,
manifest.NewParameter("item", smartcontract.AnyType)) manifest.NewParameter("item", smartcontract.AnyType))
@ -439,7 +438,7 @@ func (s *Std) Metadata() *interop.ContractMD {
} }
// Initialize implements the Contract interface. // Initialize implements the Contract interface.
func (s *Std) Initialize(ic *interop.Context) error { func (s *Std) Initialize(ic *interop.Context, hf *config.Hardfork) error {
return nil return nil
} }