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(¤t) 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 }