github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/smartcontract/manifest/abi.go (about) 1 package manifest 2 3 import ( 4 "errors" 5 "fmt" 6 "sort" 7 8 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 9 ) 10 11 const ( 12 // MethodInit is a name for default initialization method. 13 MethodInit = "_initialize" 14 15 // MethodDeploy is a name for default method called during contract deployment. 16 MethodDeploy = "_deploy" 17 18 // MethodVerify is a name for default verification method. 19 MethodVerify = "verify" 20 21 // MethodOnNEP17Payment is the name of the method which is called when contract receives NEP-17 tokens. 22 MethodOnNEP17Payment = "onNEP17Payment" 23 24 // MethodOnNEP11Payment is the name of the method which is called when contract receives NEP-11 tokens. 25 MethodOnNEP11Payment = "onNEP11Payment" 26 ) 27 28 // ABI represents a contract application binary interface. 29 type ABI struct { 30 Methods []Method `json:"methods"` 31 Events []Event `json:"events"` 32 } 33 34 // GetMethod returns methods with the specified name. 35 func (a *ABI) GetMethod(name string, paramCount int) *Method { 36 for i := range a.Methods { 37 if a.Methods[i].Name == name && (paramCount == -1 || len(a.Methods[i].Parameters) == paramCount) { 38 return &a.Methods[i] 39 } 40 } 41 return nil 42 } 43 44 // GetEvent returns the event with the specified name. 45 func (a *ABI) GetEvent(name string) *Event { 46 for i := range a.Events { 47 if a.Events[i].Name == name { 48 return &a.Events[i] 49 } 50 } 51 return nil 52 } 53 54 // IsValid checks ABI consistency and correctness. 55 func (a *ABI) IsValid() error { 56 if len(a.Methods) == 0 { 57 return errors.New("no methods") 58 } 59 for i := range a.Methods { 60 err := a.Methods[i].IsValid() 61 if err != nil { 62 return fmt.Errorf("method %q/%d: %w", a.Methods[i].Name, len(a.Methods[i].Parameters), err) 63 } 64 } 65 if len(a.Methods) > 1 { 66 methods := make([]struct { 67 name string 68 params int 69 }, len(a.Methods)) 70 for i := range methods { 71 methods[i].name = a.Methods[i].Name 72 methods[i].params = len(a.Methods[i].Parameters) 73 } 74 sort.Slice(methods, func(i, j int) bool { 75 if methods[i].name < methods[j].name { 76 return true 77 } 78 if methods[i].name == methods[j].name { 79 return methods[i].params < methods[j].params 80 } 81 return false 82 }) 83 for i := range methods { 84 if i == 0 { 85 continue 86 } 87 if methods[i].name == methods[i-1].name && 88 methods[i].params == methods[i-1].params { 89 return errors.New("duplicate method specifications") 90 } 91 } 92 } 93 for i := range a.Events { 94 err := a.Events[i].IsValid() 95 if err != nil { 96 return fmt.Errorf("event %q/%d: %w", a.Events[i].Name, len(a.Events[i].Parameters), err) 97 } 98 } 99 if len(a.Events) > 1 { 100 names := make([]string, len(a.Events)) 101 for i := range a.Events { 102 names[i] = a.Events[i].Name 103 } 104 if stringsHaveDups(names) { 105 return errors.New("duplicate event names") 106 } 107 } 108 return nil 109 } 110 111 // ToStackItem converts ABI to stackitem.Item. 112 func (a *ABI) ToStackItem() stackitem.Item { 113 methods := make([]stackitem.Item, len(a.Methods)) 114 for i := range a.Methods { 115 methods[i] = a.Methods[i].ToStackItem() 116 } 117 events := make([]stackitem.Item, len(a.Events)) 118 for i := range a.Events { 119 events[i] = a.Events[i].ToStackItem() 120 } 121 return stackitem.NewStruct([]stackitem.Item{ 122 stackitem.Make(methods), 123 stackitem.Make(events), 124 }) 125 } 126 127 // FromStackItem converts stackitem.Item to ABI. 128 func (a *ABI) FromStackItem(item stackitem.Item) error { 129 if item.Type() != stackitem.StructT { 130 return errors.New("invalid ABI stackitem type") 131 } 132 str := item.Value().([]stackitem.Item) 133 if len(str) != 2 { 134 return errors.New("invalid ABI stackitem length") 135 } 136 if str[0].Type() != stackitem.ArrayT { 137 return errors.New("invalid Methods stackitem type") 138 } 139 methods := str[0].Value().([]stackitem.Item) 140 a.Methods = make([]Method, len(methods)) 141 for i := range methods { 142 m := new(Method) 143 if err := m.FromStackItem(methods[i]); err != nil { 144 return err 145 } 146 a.Methods[i] = *m 147 } 148 if str[1].Type() != stackitem.ArrayT { 149 return errors.New("invalid Events stackitem type") 150 } 151 events := str[1].Value().([]stackitem.Item) 152 a.Events = make([]Event, len(events)) 153 for i := range events { 154 e := new(Event) 155 if err := e.FromStackItem(events[i]); err != nil { 156 return err 157 } 158 a.Events[i] = *e 159 } 160 return nil 161 }