neoneo-go/pkg/core/native/interop.go
Anna Shaleva d62fad1268 native: implement HF-based update
A part of #3213.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-04-25 13:16:42 +03:00

121 lines
3.3 KiB
Go

package native
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// Call calls the specified native contract method.
func Call(ic *interop.Context) error {
version := ic.VM.Estack().Pop().BigInt().Int64()
if version != 0 {
return fmt.Errorf("native contract of version %d is not active", version)
}
var (
c interop.Contract
curr = ic.VM.GetCurrentScriptHash()
)
for _, ctr := range ic.Natives {
if ctr.Metadata().Hash == curr {
c = ctr
break
}
}
if c == nil {
return fmt.Errorf("native contract %s (version %d) not found", curr.StringLE(), version)
}
var (
genericMeta = c.Metadata()
activeIn = c.ActiveIn()
)
if activeIn != nil {
height, ok := ic.Hardforks[activeIn.String()]
// 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", 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())
if !ok {
return fmt.Errorf("method not found")
}
reqFlags := m.RequiredFlags
if !ic.IsHardforkEnabled(config.HFAspidochelone) && meta.ID == ManagementContractID &&
(m.MD.Name == "deploy" || m.MD.Name == "update") {
reqFlags &= callflag.States | callflag.AllowNotify
}
if !ic.VM.Context().GetCallFlags().Has(reqFlags) {
return fmt.Errorf("missing call flags for native %d `%s` operation call: %05b vs %05b",
version, m.MD.Name, ic.VM.Context().GetCallFlags(), reqFlags)
}
invokeFee := m.CPUFee*ic.BaseExecFee() +
m.StorageFee*ic.BaseStorageFee()
if !ic.VM.AddGas(invokeFee) {
return errors.New("gas limit exceeded")
}
ctx := ic.VM.Context()
args := make([]stackitem.Item, len(m.MD.Parameters))
for i := range args {
args[i] = ic.VM.Estack().Peek(i).Item()
}
result := m.Func(ic, args)
for range m.MD.Parameters {
ic.VM.Estack().Pop()
}
if m.MD.ReturnType != smartcontract.VoidType {
ctx.Estack().PushItem(result)
}
return nil
}
// OnPersist calls OnPersist methods for all native contracts.
func OnPersist(ic *interop.Context) error {
if ic.Trigger != trigger.OnPersist {
return errors.New("onPersist must be trigered by system")
}
for _, c := range ic.Natives {
activeIn := c.ActiveIn()
if !(activeIn == nil || ic.IsHardforkEnabled(*activeIn)) {
continue
}
err := c.OnPersist(ic)
if err != nil {
return err
}
}
return nil
}
// PostPersist calls PostPersist methods for all native contracts.
func PostPersist(ic *interop.Context) error {
if ic.Trigger != trigger.PostPersist {
return errors.New("postPersist must be trigered by system")
}
for _, c := range ic.Natives {
activeIn := c.ActiveIn()
if !(activeIn == nil || ic.IsHardforkEnabled(*activeIn)) {
continue
}
err := c.PostPersist(ic)
if err != nil {
return err
}
}
return nil
}