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