github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/smartcontract/manifest/standard/comply.go (about) 1 package standard 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" 8 ) 9 10 // Various validation errors. 11 var ( 12 ErrMethodMissing = errors.New("method missing") 13 ErrEventMissing = errors.New("event missing") 14 ErrInvalidReturnType = errors.New("invalid return type") 15 ErrInvalidParameterCount = errors.New("invalid parameter count") 16 ErrInvalidParameterName = errors.New("invalid parameter name") 17 ErrInvalidParameterType = errors.New("invalid parameter type") 18 ErrSafeMethodMismatch = errors.New("method has wrong safe flag") 19 ) 20 21 var checks = map[string][]*Standard{ 22 manifest.NEP11StandardName: {Nep11NonDivisible, Nep11Divisible}, 23 manifest.NEP17StandardName: {Nep17}, 24 manifest.NEP11Payable: {Nep11Payable}, 25 manifest.NEP17Payable: {Nep17Payable}, 26 } 27 28 // Check checks if the manifest complies with all provided standards. 29 // Currently, only NEP-17 is supported. 30 func Check(m *manifest.Manifest, standards ...string) error { 31 return check(m, true, standards...) 32 } 33 34 // CheckABI is similar to Check but doesn't check parameter names. 35 func CheckABI(m *manifest.Manifest, standards ...string) error { 36 return check(m, false, standards...) 37 } 38 39 func check(m *manifest.Manifest, checkNames bool, standards ...string) error { 40 for i := range standards { 41 ss, ok := checks[standards[i]] 42 if ok { 43 var err error 44 for i := range ss { 45 if err = comply(m, checkNames, ss[i]); err == nil { 46 break 47 } 48 } 49 if err != nil { 50 return fmt.Errorf("manifest is not compliant with '%s': %w", standards[i], err) 51 } 52 } 53 } 54 return nil 55 } 56 57 // Comply if m has all methods and event from st manifest and they have the same signature. 58 // Parameter names are checked to exactly match the ones in the given standard. 59 func Comply(m *manifest.Manifest, st *Standard) error { 60 return comply(m, true, st) 61 } 62 63 // ComplyABI is similar to Comply but doesn't check parameter names. 64 func ComplyABI(m *manifest.Manifest, st *Standard) error { 65 return comply(m, false, st) 66 } 67 68 func comply(m *manifest.Manifest, checkNames bool, st *Standard) error { 69 if st.Base != nil { 70 if err := comply(m, checkNames, st.Base); err != nil { 71 return err 72 } 73 } 74 for _, stm := range st.ABI.Methods { 75 if err := checkMethod(m, &stm, false, checkNames); err != nil { 76 return err 77 } 78 } 79 for _, ste := range st.ABI.Events { 80 name := ste.Name 81 ed := m.ABI.GetEvent(name) 82 if ed == nil { 83 return fmt.Errorf("%w: event '%s'", ErrEventMissing, name) 84 } else if len(ste.Parameters) != len(ed.Parameters) { 85 return fmt.Errorf("%w: event '%s' (expected %d, got %d)", ErrInvalidParameterCount, 86 name, len(ste.Parameters), len(ed.Parameters)) 87 } 88 for i := range ste.Parameters { 89 if checkNames && ste.Parameters[i].Name != ed.Parameters[i].Name { 90 return fmt.Errorf("%w: event '%s'[%d] (expected %s, got %s)", ErrInvalidParameterName, 91 name, i, ste.Parameters[i].Name, ed.Parameters[i].Name) 92 } 93 if ste.Parameters[i].Type != ed.Parameters[i].Type { 94 return fmt.Errorf("%w: event '%s' (expected %s, got %s)", ErrInvalidParameterType, 95 name, ste.Parameters[i].Type, ed.Parameters[i].Type) 96 } 97 } 98 } 99 for _, stm := range st.Optional { 100 if err := checkMethod(m, &stm, true, checkNames); err != nil { 101 return err 102 } 103 } 104 return nil 105 } 106 107 func checkMethod(m *manifest.Manifest, expected *manifest.Method, 108 allowMissing bool, checkNames bool) error { 109 actual := m.ABI.GetMethod(expected.Name, len(expected.Parameters)) 110 if actual == nil { 111 if allowMissing { 112 return nil 113 } 114 return fmt.Errorf("%w: '%s' with %d parameters", ErrMethodMissing, 115 expected.Name, len(expected.Parameters)) 116 } 117 if expected.ReturnType != actual.ReturnType { 118 return fmt.Errorf("%w: '%s' (expected %s, got %s)", ErrInvalidReturnType, 119 expected.Name, expected.ReturnType, actual.ReturnType) 120 } 121 for i := range expected.Parameters { 122 if checkNames && expected.Parameters[i].Name != actual.Parameters[i].Name { 123 return fmt.Errorf("%w: '%s'[%d] (expected %s, got %s)", ErrInvalidParameterName, 124 expected.Name, i, expected.Parameters[i].Name, actual.Parameters[i].Name) 125 } 126 if expected.Parameters[i].Type != actual.Parameters[i].Type { 127 return fmt.Errorf("%w: '%s'[%d] (expected %s, got %s)", ErrInvalidParameterType, 128 expected.Name, i, expected.Parameters[i].Type, actual.Parameters[i].Type) 129 } 130 } 131 if expected.Safe != actual.Safe { 132 return fmt.Errorf("'%s' %w: expected %t", expected.Name, ErrSafeMethodMismatch, expected.Safe) 133 } 134 return nil 135 }