native: implement HF-based update
A part of #3213. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
This commit is contained in:
parent
83fdcc8568
commit
d62fad1268
19 changed files with 416 additions and 105 deletions
|
@ -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)
|
||||
|
||||
switch {
|
||||
|
@ -308,8 +308,10 @@ func getMethod(t *testing.T, ctr interop.ContractMD, name string, params []strin
|
|||
name = strings.TrimSuffix(name, "WithData")
|
||||
}
|
||||
|
||||
md, ok := ctr.GetMethod(name, paramLen)
|
||||
require.True(t, ok, ctr.Manifest.Name, name, paramLen)
|
||||
latestHF := config.LatestHardfork()
|
||||
cMD := ctr.HFSpecificContractMD(&latestHF)
|
||||
md, ok := cMD.GetMethod(name, paramLen)
|
||||
require.True(t, ok, cMD.Manifest.Name, name, paramLen)
|
||||
return md
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
// Hardfork name.
|
||||
func IsHardforkValid(s string) bool {
|
||||
_, ok := hardforks[s]
|
||||
return ok
|
||||
}
|
||||
|
||||
// LatestHardfork returns latest known hardfork.
|
||||
func LatestHardfork() Hardfork {
|
||||
return hfLast >> 1
|
||||
}
|
||||
|
|
|
@ -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(¤tHF)
|
||||
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(¤t)
|
||||
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(¤t)
|
||||
res = append(res, state.NativeContract{
|
||||
ContractBase: md.ContractBase,
|
||||
})
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
|
@ -140,8 +141,14 @@ type Function struct {
|
|||
// Method is a signature for a native method.
|
||||
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 {
|
||||
HFSpecificMethodAndPrice
|
||||
ActiveFrom *config.Hardfork
|
||||
}
|
||||
|
||||
// HFSpecificMethodAndPrice is a hardfork-specific native contract method descriptor.
|
||||
type HFSpecificMethodAndPrice struct {
|
||||
Func Method
|
||||
MD *manifest.Method
|
||||
CPUFee int64
|
||||
|
@ -150,10 +157,23 @@ type MethodAndPrice struct {
|
|||
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.
|
||||
type Contract interface {
|
||||
Initialize(*Context) error
|
||||
// ActiveIn returns the hardfork native contract is active from or nil in case
|
||||
// Initialize performs native contract initialization on contract deploy or update.
|
||||
// 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.
|
||||
ActiveIn() *config.Hardfork
|
||||
// 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
|
||||
// deployed and no Initialize method was called.
|
||||
InitializeCache(blockHeight uint32, d *dao.Simple) error
|
||||
// Metadata returns generic native contract metadata.
|
||||
Metadata() *ContractMD
|
||||
OnPersist(*Context) error
|
||||
PostPersist(*Context) error
|
||||
}
|
||||
|
||||
// ContractMD represents a native contract instance.
|
||||
// ContractMD represents a generic hardfork-independent native contract instance.
|
||||
type ContractMD struct {
|
||||
state.NativeContract
|
||||
ID int32
|
||||
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
|
||||
// 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.
|
||||
func NewContractMD(name string, id int32) *ContractMD {
|
||||
// HFSpecificContractMD is a hardfork-specific native contract descriptor.
|
||||
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}
|
||||
if len(onManifestConstruction) != 0 {
|
||||
c.onManifestConstruction = onManifestConstruction[0]
|
||||
}
|
||||
|
||||
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.Manifest = *manifest.DefaultManifest(name)
|
||||
c.ActiveHFs = make(map[config.Hardfork]struct{})
|
||||
c.mdCache = make(map[config.Hardfork]*HFSpecificContractMD)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// UpdateHash creates a native contract script and updates hash.
|
||||
func (c *ContractMD) UpdateHash() {
|
||||
// HFSpecificContractMD returns hardfork-specific native contract metadata, i.e. with methods, events and script
|
||||
// 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()
|
||||
for i := range c.Methods {
|
||||
offset := w.Len()
|
||||
c.Methods[i].MD.Offset = offset
|
||||
c.Manifest.ABI.Methods[i].Offset = offset
|
||||
m := c.Methods[i]
|
||||
if !(m.ActiveFrom == nil || (hf != nil && (*m.ActiveFrom).Cmp(*hf) >= 0)) {
|
||||
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)
|
||||
c.Methods[i].SyscallOffset = w.Len()
|
||||
m.SyscallOffset = w.Len()
|
||||
emit.Syscall(w.BinWriter, interopnames.SystemContractCallNative)
|
||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
||||
|
||||
abiMethods = append(abiMethods, *m.MD)
|
||||
methods = append(methods, m.HFSpecificMethodAndPrice)
|
||||
}
|
||||
if w.Err != nil {
|
||||
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()
|
||||
c.NEF.Checksum = c.NEF.CalculateChecksum()
|
||||
abiEvents = append(abiEvents, *e.MD)
|
||||
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.
|
||||
|
@ -215,36 +340,35 @@ func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method) {
|
|||
md.MD = desc
|
||||
desc.Safe = md.RequiredFlags&(callflag.All^callflag.ReadOnly) == 0
|
||||
|
||||
index := sort.Search(len(c.Manifest.ABI.Methods), func(i int) bool {
|
||||
md := c.Manifest.ABI.Methods[i]
|
||||
index := sort.Search(len(c.Methods), func(i int) bool {
|
||||
md := c.Methods[i].MD
|
||||
if md.Name != desc.Name {
|
||||
return md.Name >= desc.Name
|
||||
}
|
||||
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{})
|
||||
copy(c.Methods[index+1:], c.Methods[index:])
|
||||
c.Methods[index] = *md
|
||||
|
||||
if md.ActiveFrom != nil {
|
||||
c.ActiveHFs[*md.ActiveFrom] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// GetMethodByOffset returns method with the provided offset.
|
||||
// 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 {
|
||||
if c.Methods[k].SyscallOffset == offset {
|
||||
return c.Methods[k], true
|
||||
}
|
||||
}
|
||||
return MethodAndPrice{}, false
|
||||
return HFSpecificMethodAndPrice{}, false
|
||||
}
|
||||
|
||||
// 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 {
|
||||
md := c.Methods[i]
|
||||
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 MethodAndPrice{}, false
|
||||
return HFSpecificMethodAndPrice{}, false
|
||||
}
|
||||
|
||||
// AddEvent adds a new event to the native contract.
|
||||
func (c *ContractMD) AddEvent(name string, ps ...manifest.Parameter) {
|
||||
c.Manifest.ABI.Events = append(c.Manifest.ABI.Events, manifest.Event{
|
||||
Name: name,
|
||||
Parameters: ps,
|
||||
})
|
||||
func (c *ContractMD) AddEvent(md Event) {
|
||||
c.Events = append(c.Events, md)
|
||||
|
||||
if md.ActiveFrom != nil {
|
||||
c.ActiveHFs[*md.ActiveFrom] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort sorts interop functions by id.
|
||||
|
|
|
@ -12,12 +12,14 @@ import (
|
|||
func TestNamesASCII(t *testing.T) {
|
||||
cfg := config.ProtocolConfiguration{P2PSigExtensions: true}
|
||||
cs := NewContracts(cfg)
|
||||
latestHF := config.LatestHardfork()
|
||||
for _, c := range cs.Contracts {
|
||||
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))
|
||||
}
|
||||
for _, e := range c.Metadata().Manifest.ABI.Events {
|
||||
for _, e := range hfMD.Manifest.ABI.Events {
|
||||
require.True(t, isASCII(e.Name))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,10 +12,12 @@ import (
|
|||
func TestNativeGetMethod(t *testing.T) {
|
||||
cfg := config.ProtocolConfiguration{P2PSigExtensions: true}
|
||||
cs := NewContracts(cfg)
|
||||
latestHF := config.LatestHardfork()
|
||||
for _, c := range cs.Contracts {
|
||||
hfMD := c.Metadata().HFSpecificContractMD(&latestHF)
|
||||
t.Run(c.Metadata().Name, func(t *testing.T) {
|
||||
for _, m := range c.Metadata().Methods {
|
||||
_, ok := c.Metadata().GetMethod(m.MD.Name, len(m.MD.Parameters))
|
||||
for _, m := range hfMD.Methods {
|
||||
_, ok := hfMD.GetMethod(m.MD.Name, len(m.MD.Parameters))
|
||||
require.True(t, ok)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -41,7 +41,6 @@ const cryptoContractID = -3
|
|||
|
||||
func newCrypto() *Crypto {
|
||||
c := &Crypto{ContractMD: *interop.NewContractMD(nativenames.CryptoLib, cryptoContractID)}
|
||||
defer c.UpdateHash()
|
||||
|
||||
desc := newDescriptor("sha256", smartcontract.ByteArrayType,
|
||||
manifest.NewParameter("data", smartcontract.ByteArrayType))
|
||||
|
@ -310,7 +309,7 @@ func (c *Crypto) Metadata() *interop.ContractMD {
|
|||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
|
@ -106,7 +106,6 @@ func newDesignate(p2pSigExtensionsEnabled bool, initialNodeRoles map[noderoles.R
|
|||
s := &Designate{ContractMD: *interop.NewContractMD(nativenames.Designation, designateContractID)}
|
||||
s.p2pSigExtensionsEnabled = p2pSigExtensionsEnabled
|
||||
s.initialNodeRoles = initialNodeRoles
|
||||
defer s.UpdateHash()
|
||||
|
||||
desc := newDescriptor("getDesignatedByRole", smartcontract.ArrayType,
|
||||
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)
|
||||
s.AddMethod(md, desc)
|
||||
|
||||
s.AddEvent(DesignationEventName,
|
||||
eDesc := newEventDescriptor(DesignationEventName,
|
||||
manifest.NewParameter("Role", smartcontract.IntegerType),
|
||||
manifest.NewParameter("BlockIndex", smartcontract.IntegerType))
|
||||
eMD := newEvent(eDesc)
|
||||
s.AddEvent(eMD)
|
||||
|
||||
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
|
||||
// at the genesis block, and we can't properly fill the cache at this point, as there are no roles
|
||||
// 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{}
|
||||
ic.DAO.SetCache(s.ID, cache)
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ func Call(ic *interop.Context) error {
|
|||
return fmt.Errorf("native contract %s (version %d) not found", curr.StringLE(), version)
|
||||
}
|
||||
var (
|
||||
meta = c.Metadata()
|
||||
genericMeta = c.Metadata()
|
||||
activeIn = c.ActiveIn()
|
||||
)
|
||||
if activeIn != nil {
|
||||
|
@ -40,9 +40,17 @@ func Call(ic *interop.Context) error {
|
|||
// 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.
|
||||
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(¤t)
|
||||
m, ok := meta.GetMethodByOffset(ic.VM.Context().IP())
|
||||
if !ok {
|
||||
return fmt.Errorf("method not found")
|
||||
|
|
|
@ -31,7 +31,6 @@ func newLedger() *Ledger {
|
|||
var l = &Ledger{
|
||||
ContractMD: *interop.NewContractMD(nativenames.Ledger, ledgerContractID),
|
||||
}
|
||||
defer l.UpdateHash()
|
||||
|
||||
desc := newDescriptor("currentHash", smartcontract.Hash256Type)
|
||||
md := newMethodAndPrice(l.currentHash, 1<<15, callflag.ReadStates)
|
||||
|
@ -81,7 +80,7 @@ func (l *Ledger) Metadata() *interop.ContractMD {
|
|||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,6 @@ func newManagement() *Management {
|
|||
var m = &Management{
|
||||
ContractMD: *interop.NewContractMD(nativenames.Management, ManagementContractID),
|
||||
}
|
||||
defer m.UpdateHash()
|
||||
|
||||
desc := newDescriptor("getContract", smartcontract.ArrayType,
|
||||
manifest.NewParameter("hash", smartcontract.Hash160Type))
|
||||
|
@ -164,9 +163,17 @@ func newManagement() *Management {
|
|||
m.AddMethod(md, desc)
|
||||
|
||||
hashParam := manifest.NewParameter("Hash", smartcontract.Hash160Type)
|
||||
m.AddEvent(contractDeployNotificationName, hashParam)
|
||||
m.AddEvent(contractUpdateNotificationName, hashParam)
|
||||
m.AddEvent(contractDestroyNotificationName, hashParam)
|
||||
eDesc := newEventDescriptor(contractDeployNotificationName, hashParam)
|
||||
eMD := newEvent(eDesc)
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
func GetContract(d *dao.Simple, hash util.Uint160) (*state.Contract, error) {
|
||||
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]
|
||||
if !ok {
|
||||
return nil, storage.ErrKeyNotFound
|
||||
|
@ -583,22 +595,65 @@ func updateContractCache(cache *ManagementCache, cs *state.Contract) {
|
|||
func (m *Management) OnPersist(ic *interop.Context) error {
|
||||
var cache *ManagementCache
|
||||
for _, native := range ic.Natives {
|
||||
activeIn := native.ActiveIn()
|
||||
if !(activeIn == nil && ic.Block.Index == 0 ||
|
||||
activeIn != nil && ic.IsHardforkActivation(*activeIn)) {
|
||||
var (
|
||||
activeIn = native.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
|
||||
}
|
||||
|
||||
md := native.Metadata()
|
||||
cs := &state.Contract{
|
||||
ContractBase: md.ContractBase,
|
||||
base := md.HFSpecificContractMD(&latestHF).ContractBase
|
||||
var cs *state.Contract
|
||||
switch {
|
||||
case isDeploy:
|
||||
cs = &state.Contract{
|
||||
ContractBase: base,
|
||||
}
|
||||
if err := native.Initialize(ic); err != nil {
|
||||
return fmt.Errorf("initializing %s native contract: %w", md.Name, err)
|
||||
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.
|
||||
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 {
|
||||
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.
|
||||
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, keyNextAvailableID, 1)
|
||||
|
||||
|
|
|
@ -20,9 +20,9 @@ func TestDeployGetUpdateDestroyContract(t *testing.T) {
|
|||
mgmt.Policy = newPolicy(false)
|
||||
d := dao.NewSimple(storage.NewMemoryStore(), false)
|
||||
ic := &interop.Context{DAO: d}
|
||||
err := mgmt.Initialize(ic)
|
||||
err := mgmt.Initialize(ic, nil)
|
||||
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)}
|
||||
sender := util.Uint160{1, 2, 3}
|
||||
ne, err := nef.NewFile(script)
|
||||
|
@ -97,9 +97,9 @@ func TestManagement_GetNEP17Contracts(t *testing.T) {
|
|||
mgmt := newManagement()
|
||||
mgmt.Policy = newPolicy(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, mgmt.Policy.Initialize(&interop.Context{DAO: d}))
|
||||
require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}, nil))
|
||||
err = mgmt.InitializeCache(0, d)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@ func newGAS(init int64, p2pSigExtensionsEnabled bool) *GAS {
|
|||
initialSupply: init,
|
||||
p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
|
||||
}
|
||||
defer g.UpdateHash()
|
||||
|
||||
nep17 := newNEP17Native(nativenames.Gas, gasContractID)
|
||||
nep17.symbol = "GAS"
|
||||
|
@ -83,7 +82,11 @@ func (g *GAS) balanceFromBytes(si *state.StorageItem) (*big.Int, error) {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -172,7 +172,6 @@ func makeValidatorKey(key *keys.PublicKey) []byte {
|
|||
// newNEO returns NEO native contract.
|
||||
func newNEO(cfg config.ProtocolConfiguration) *NEO {
|
||||
n := &NEO{}
|
||||
defer n.UpdateHash()
|
||||
|
||||
nep17 := newNEP17Native(nativenames.Neo, neoContractID)
|
||||
nep17.symbol = "NEO"
|
||||
|
@ -258,27 +257,39 @@ func newNEO(cfg config.ProtocolConfiguration) *NEO {
|
|||
md = newMethodAndPrice(n.setRegisterPrice, 1<<15, callflag.States)
|
||||
n.AddMethod(md, desc)
|
||||
|
||||
n.AddEvent("CandidateStateChanged",
|
||||
eDesc := newEventDescriptor("CandidateStateChanged",
|
||||
manifest.NewParameter("pubkey", smartcontract.PublicKeyType),
|
||||
manifest.NewParameter("registered", smartcontract.BoolType),
|
||||
manifest.NewParameter("votes", smartcontract.IntegerType),
|
||||
)
|
||||
n.AddEvent("Vote",
|
||||
eMD := newEvent(eDesc)
|
||||
n.AddEvent(eMD)
|
||||
|
||||
eDesc = newEventDescriptor("Vote",
|
||||
manifest.NewParameter("account", smartcontract.Hash160Type),
|
||||
manifest.NewParameter("from", smartcontract.PublicKeyType),
|
||||
manifest.NewParameter("to", smartcontract.PublicKeyType),
|
||||
manifest.NewParameter("amount", smartcontract.IntegerType),
|
||||
)
|
||||
n.AddEvent("CommitteeChanged",
|
||||
eMD = newEvent(eDesc)
|
||||
n.AddEvent(eMD)
|
||||
|
||||
eDesc = newEventDescriptor("CommitteeChanged",
|
||||
manifest.NewParameter("old", smartcontract.ArrayType),
|
||||
manifest.NewParameter("new", smartcontract.ArrayType),
|
||||
)
|
||||
eMD = newEvent(eDesc)
|
||||
n.AddEvent(eMD)
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"math"
|
||||
"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/interop"
|
||||
"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 {
|
||||
n := &nep17TokenNative{ContractMD: *interop.NewContractMD(name, id)}
|
||||
n.Manifest.SupportedStandards = []string{manifest.NEP17StandardName}
|
||||
n := &nep17TokenNative{ContractMD: *interop.NewContractMD(name, id, func(m *manifest.Manifest) {
|
||||
m.SupportedStandards = []string{manifest.NEP17StandardName}
|
||||
})}
|
||||
|
||||
desc := newDescriptor("symbol", smartcontract.StringType)
|
||||
md := newMethodAndPrice(n.Symbol, 0, callflag.NoneFlag)
|
||||
|
@ -77,7 +79,9 @@ func newNEP17Native(name string, id int32) *nep17TokenNative {
|
|||
md.StorageFee = 50
|
||||
n.AddMethod(md, desc)
|
||||
|
||||
n.AddEvent("Transfer", transferParams...)
|
||||
eDesc := newEventDescriptor("Transfer", transferParams...)
|
||||
eMD := newEvent(eDesc)
|
||||
n.AddEvent(eMD)
|
||||
|
||||
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 {
|
||||
return &interop.MethodAndPrice{
|
||||
func newMethodAndPrice(f interop.Method, cpuFee int64, flags callflag.CallFlag, activeFrom ...config.Hardfork) *interop.MethodAndPrice {
|
||||
md := &interop.MethodAndPrice{
|
||||
HFSpecificMethodAndPrice: interop.HFSpecificMethodAndPrice{
|
||||
Func: f,
|
||||
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 {
|
||||
|
|
|
@ -73,7 +73,6 @@ func copyNotaryCache(src, dst *NotaryCache) {
|
|||
// newNotary returns Notary native contract.
|
||||
func newNotary() *Notary {
|
||||
n := &Notary{ContractMD: *interop.NewContractMD(nativenames.Notary, notaryContractID)}
|
||||
defer n.UpdateHash()
|
||||
|
||||
desc := newDescriptor("onNEP17Payment", smartcontract.VoidType,
|
||||
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.
|
||||
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)
|
||||
|
||||
cache := &NotaryCache{
|
||||
|
|
|
@ -118,7 +118,6 @@ func newOracle() *Oracle {
|
|||
ContractMD: *interop.NewContractMD(nativenames.Oracle, oracleContractID),
|
||||
newRequests: make(map[uint64]*state.OracleRequest),
|
||||
}
|
||||
defer o.UpdateHash()
|
||||
|
||||
o.oracleScript = CreateOracleResponseScript(o.Hash)
|
||||
|
||||
|
@ -139,12 +138,17 @@ func newOracle() *Oracle {
|
|||
md = newMethodAndPrice(o.verify, 1<<15, callflag.NoneFlag)
|
||||
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("Url", 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))
|
||||
eMD = newEvent(eDesc)
|
||||
o.AddEvent(eMD)
|
||||
|
||||
desc = newDescriptor("getPrice", smartcontract.IntegerType)
|
||||
md = newMethodAndPrice(o.getPrice, 1<<15, callflag.ReadStates)
|
||||
|
@ -241,7 +245,11 @@ func (o *Oracle) Metadata() *interop.ContractMD {
|
|||
}
|
||||
|
||||
// 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, prefixRequestPrice, DefaultOracleRequestPrice)
|
||||
|
||||
|
|
|
@ -105,7 +105,6 @@ func newPolicy(p2pSigExtensionsEnabled bool) *Policy {
|
|||
ContractMD: *interop.NewContractMD(nativenames.Policy, policyContractID),
|
||||
p2pSigExtensionsEnabled: p2pSigExtensionsEnabled,
|
||||
}
|
||||
defer p.UpdateHash()
|
||||
|
||||
desc := newDescriptor("getFeePerByte", smartcontract.IntegerType)
|
||||
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.
|
||||
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, execFeeFactorKey, defaultExecFeeFactor)
|
||||
setIntWithKey(p.ID, ic.DAO, storagePriceKey, DefaultStoragePrice)
|
||||
|
|
|
@ -46,7 +46,6 @@ var (
|
|||
|
||||
func newStd() *Std {
|
||||
s := &Std{ContractMD: *interop.NewContractMD(nativenames.StdLib, stdContractID)}
|
||||
defer s.UpdateHash()
|
||||
|
||||
desc := newDescriptor("serialize", smartcontract.ByteArrayType,
|
||||
manifest.NewParameter("item", smartcontract.AnyType))
|
||||
|
@ -439,7 +438,7 @@ func (s *Std) Metadata() *interop.ContractMD {
|
|||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue