github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/native/interop.go (about)

     1  package native
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/nspcc-dev/neo-go/pkg/config"
     8  	"github.com/nspcc-dev/neo-go/pkg/core/interop"
     9  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    10  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
    11  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
    12  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    13  )
    14  
    15  // Call calls the specified native contract method.
    16  func Call(ic *interop.Context) error {
    17  	version := ic.VM.Estack().Pop().BigInt().Int64()
    18  	if version != 0 {
    19  		return fmt.Errorf("native contract of version %d is not active", version)
    20  	}
    21  	var (
    22  		c    interop.Contract
    23  		curr = ic.VM.GetCurrentScriptHash()
    24  	)
    25  	for _, ctr := range ic.Natives {
    26  		if ctr.Metadata().Hash == curr {
    27  			c = ctr
    28  			break
    29  		}
    30  	}
    31  	if c == nil {
    32  		return fmt.Errorf("native contract %s (version %d) not found", curr.StringLE(), version)
    33  	}
    34  	var (
    35  		genericMeta = c.Metadata()
    36  		activeIn    = c.ActiveIn()
    37  	)
    38  	if activeIn != nil {
    39  		height, ok := ic.Hardforks[activeIn.String()]
    40  		// Persisting block must not be taken into account, native contract can be called
    41  		// only AFTER its initialization block persist, thus, can't use ic.IsHardforkEnabled.
    42  		if !ok || ic.BlockHeight() < height {
    43  			return fmt.Errorf("native contract %s is active after hardfork %s", genericMeta.Name, activeIn.String())
    44  		}
    45  	}
    46  	var current config.Hardfork
    47  	for _, hf := range config.Hardforks {
    48  		if !ic.IsHardforkEnabled(hf) {
    49  			break
    50  		}
    51  		current = hf
    52  	}
    53  	meta := genericMeta.HFSpecificContractMD(&current)
    54  	m, ok := meta.GetMethodByOffset(ic.VM.Context().IP())
    55  	if !ok {
    56  		return fmt.Errorf("method not found")
    57  	}
    58  	reqFlags := m.RequiredFlags
    59  	if !ic.IsHardforkEnabled(config.HFAspidochelone) && meta.ID == ManagementContractID &&
    60  		(m.MD.Name == "deploy" || m.MD.Name == "update") {
    61  		reqFlags &= callflag.States | callflag.AllowNotify
    62  	}
    63  	if !ic.VM.Context().GetCallFlags().Has(reqFlags) {
    64  		return fmt.Errorf("missing call flags for native %d `%s` operation call: %05b vs %05b",
    65  			version, m.MD.Name, ic.VM.Context().GetCallFlags(), reqFlags)
    66  	}
    67  	invokeFee := m.CPUFee*ic.BaseExecFee() +
    68  		m.StorageFee*ic.BaseStorageFee()
    69  	if !ic.VM.AddGas(invokeFee) {
    70  		return errors.New("gas limit exceeded")
    71  	}
    72  	ctx := ic.VM.Context()
    73  	args := make([]stackitem.Item, len(m.MD.Parameters))
    74  	for i := range args {
    75  		args[i] = ic.VM.Estack().Peek(i).Item()
    76  	}
    77  	result := m.Func(ic, args)
    78  	for range m.MD.Parameters {
    79  		ic.VM.Estack().Pop()
    80  	}
    81  	if m.MD.ReturnType != smartcontract.VoidType {
    82  		ctx.Estack().PushItem(result)
    83  	}
    84  	return nil
    85  }
    86  
    87  // OnPersist calls OnPersist methods for all native contracts.
    88  func OnPersist(ic *interop.Context) error {
    89  	if ic.Trigger != trigger.OnPersist {
    90  		return errors.New("onPersist must be trigered by system")
    91  	}
    92  	for _, c := range ic.Natives {
    93  		activeIn := c.ActiveIn()
    94  		if !(activeIn == nil || ic.IsHardforkEnabled(*activeIn)) {
    95  			continue
    96  		}
    97  		err := c.OnPersist(ic)
    98  		if err != nil {
    99  			return err
   100  		}
   101  	}
   102  	return nil
   103  }
   104  
   105  // PostPersist calls PostPersist methods for all native contracts.
   106  func PostPersist(ic *interop.Context) error {
   107  	if ic.Trigger != trigger.PostPersist {
   108  		return errors.New("postPersist must be trigered by system")
   109  	}
   110  	for _, c := range ic.Natives {
   111  		activeIn := c.ActiveIn()
   112  		if !(activeIn == nil || ic.IsHardforkEnabled(*activeIn)) {
   113  			continue
   114  		}
   115  		err := c.PostPersist(ic)
   116  		if err != nil {
   117  			return err
   118  		}
   119  	}
   120  	return nil
   121  }