smartcontract: add checks for parameter names
It can be annoying to use parameter names as specified by standard, so a separate `CheckABI` is supported.
This commit is contained in:
parent
9d4ccf0fcc
commit
d89f055697
5 changed files with 79 additions and 31 deletions
|
@ -227,7 +227,7 @@ func CompileAndSave(src string, o *Options) ([]byte, error) {
|
|||
return b, fmt.Errorf("failed to convert debug info to manifest: %w", err)
|
||||
}
|
||||
if !o.NoStandardCheck {
|
||||
if err := standard.Check(m, o.ContractSupportedStandards...); err != nil {
|
||||
if err := standard.CheckABI(m, o.ContractSupportedStandards...); err != nil {
|
||||
return b, err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ var (
|
|||
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")
|
||||
)
|
||||
|
@ -25,12 +26,21 @@ var checks = map[string][]*Standard{
|
|||
// Check checks if 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, ss[i]); err == nil {
|
||||
if err = comply(m, checkNames, ss[i]); err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -45,13 +55,22 @@ func Check(m *manifest.Manifest, standards ...string) error {
|
|||
// Comply if m has all methods and event from st manifest and they have the same signature.
|
||||
// Parameter names are ignored.
|
||||
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, st.Base); err != nil {
|
||||
if err := comply(m, checkNames, st.Base); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, stm := range st.ABI.Methods {
|
||||
if err := checkMethod(m, &stm, false); err != nil {
|
||||
if err := checkMethod(m, &stm, false, checkNames); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +84,10 @@ func Comply(m *manifest.Manifest, st *Standard) error {
|
|||
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)
|
||||
|
@ -72,14 +95,15 @@ func Comply(m *manifest.Manifest, st *Standard) error {
|
|||
}
|
||||
}
|
||||
for _, stm := range st.Optional {
|
||||
if err := checkMethod(m, &stm, true); err != nil {
|
||||
if err := checkMethod(m, &stm, true, checkNames); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkMethod(m *manifest.Manifest, expected *manifest.Method, allowMissing bool) error {
|
||||
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 {
|
||||
|
@ -93,6 +117,10 @@ func checkMethod(m *manifest.Manifest, expected *manifest.Method, allowMissing b
|
|||
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)
|
||||
|
|
|
@ -81,6 +81,25 @@ func TestComplyParameterType(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestComplyParameterName(t *testing.T) {
|
||||
t.Run("Method", func(t *testing.T) {
|
||||
m := fooMethodBarEvent()
|
||||
m.ABI.GetMethod("foo", -1).Parameters[0].Name = "hehe"
|
||||
s := &Standard{Manifest: *fooMethodBarEvent()}
|
||||
err := Comply(m, s)
|
||||
require.True(t, errors.Is(err, ErrInvalidParameterName))
|
||||
require.NoError(t, ComplyABI(m, s))
|
||||
})
|
||||
t.Run("Event", func(t *testing.T) {
|
||||
m := fooMethodBarEvent()
|
||||
m.ABI.GetEvent("bar").Parameters[0].Name = "hehe"
|
||||
s := &Standard{Manifest: *fooMethodBarEvent()}
|
||||
err := Comply(m, s)
|
||||
require.True(t, errors.Is(err, ErrInvalidParameterName))
|
||||
require.NoError(t, ComplyABI(m, s))
|
||||
})
|
||||
}
|
||||
|
||||
func TestMissingEvent(t *testing.T) {
|
||||
m := fooMethodBarEvent()
|
||||
m.ABI.GetEvent("bar").Name = "notabar"
|
||||
|
@ -120,6 +139,7 @@ func TestCheck(t *testing.T) {
|
|||
m.ABI.Methods = append(m.ABI.Methods, nep17.ABI.Methods...)
|
||||
m.ABI.Events = append(m.ABI.Events, nep17.ABI.Events...)
|
||||
require.NoError(t, Check(m, manifest.NEP17StandardName))
|
||||
require.NoError(t, CheckABI(m, manifest.NEP17StandardName))
|
||||
}
|
||||
|
||||
func TestOptional(t *testing.T) {
|
||||
|
|
|
@ -13,7 +13,7 @@ var nep11Base = &Standard{
|
|||
{
|
||||
Name: "balanceOf",
|
||||
Parameters: []manifest.Parameter{
|
||||
{Type: smartcontract.Hash160Type},
|
||||
{Name: "owner", Type: smartcontract.Hash160Type},
|
||||
},
|
||||
ReturnType: smartcontract.IntegerType,
|
||||
Safe: true,
|
||||
|
@ -22,15 +22,15 @@ var nep11Base = &Standard{
|
|||
Name: "tokensOf",
|
||||
ReturnType: smartcontract.AnyType, // Iterator
|
||||
Parameters: []manifest.Parameter{
|
||||
{Type: smartcontract.Hash160Type},
|
||||
{Name: "owner", Type: smartcontract.Hash160Type},
|
||||
},
|
||||
Safe: true,
|
||||
},
|
||||
{
|
||||
Name: "transfer",
|
||||
Parameters: []manifest.Parameter{
|
||||
{Type: smartcontract.Hash160Type},
|
||||
{Type: smartcontract.ByteArrayType},
|
||||
{Name: "to", Type: smartcontract.Hash160Type},
|
||||
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||
},
|
||||
ReturnType: smartcontract.BoolType,
|
||||
},
|
||||
|
@ -39,10 +39,10 @@ var nep11Base = &Standard{
|
|||
{
|
||||
Name: "Transfer",
|
||||
Parameters: []manifest.Parameter{
|
||||
{Type: smartcontract.Hash160Type},
|
||||
{Type: smartcontract.Hash160Type},
|
||||
{Type: smartcontract.IntegerType},
|
||||
{Type: smartcontract.ByteArrayType},
|
||||
{Name: "from", Type: smartcontract.Hash160Type},
|
||||
{Name: "to", Type: smartcontract.Hash160Type},
|
||||
{Name: "amount", Type: smartcontract.IntegerType},
|
||||
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -52,7 +52,7 @@ var nep11Base = &Standard{
|
|||
{
|
||||
Name: "properties",
|
||||
Parameters: []manifest.Parameter{
|
||||
{Type: smartcontract.ByteArrayType},
|
||||
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||
},
|
||||
ReturnType: smartcontract.MapType,
|
||||
Safe: true,
|
||||
|
@ -73,7 +73,7 @@ var nep11NonDivisible = &Standard{
|
|||
{
|
||||
Name: "ownerOf",
|
||||
Parameters: []manifest.Parameter{
|
||||
{Type: smartcontract.ByteArrayType},
|
||||
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||
},
|
||||
ReturnType: smartcontract.Hash160Type,
|
||||
Safe: true,
|
||||
|
@ -91,8 +91,8 @@ var nep11Divisible = &Standard{
|
|||
{
|
||||
Name: "balanceOf",
|
||||
Parameters: []manifest.Parameter{
|
||||
{Type: smartcontract.Hash160Type},
|
||||
{Type: smartcontract.ByteArrayType},
|
||||
{Name: "owner", Type: smartcontract.Hash160Type},
|
||||
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||
},
|
||||
ReturnType: smartcontract.IntegerType,
|
||||
Safe: true,
|
||||
|
@ -100,7 +100,7 @@ var nep11Divisible = &Standard{
|
|||
{
|
||||
Name: "ownerOf",
|
||||
Parameters: []manifest.Parameter{
|
||||
{Type: smartcontract.ByteArrayType},
|
||||
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||
},
|
||||
ReturnType: smartcontract.AnyType,
|
||||
Safe: true,
|
||||
|
@ -108,10 +108,10 @@ var nep11Divisible = &Standard{
|
|||
{
|
||||
Name: "transfer",
|
||||
Parameters: []manifest.Parameter{
|
||||
{Type: smartcontract.Hash160Type},
|
||||
{Type: smartcontract.Hash160Type},
|
||||
{Type: smartcontract.IntegerType},
|
||||
{Type: smartcontract.ByteArrayType},
|
||||
{Name: "from", Type: smartcontract.Hash160Type},
|
||||
{Name: "to", Type: smartcontract.Hash160Type},
|
||||
{Name: "amount", Type: smartcontract.IntegerType},
|
||||
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||
},
|
||||
ReturnType: smartcontract.BoolType,
|
||||
},
|
||||
|
|
|
@ -13,7 +13,7 @@ var nep17 = &Standard{
|
|||
{
|
||||
Name: "balanceOf",
|
||||
Parameters: []manifest.Parameter{
|
||||
{Type: smartcontract.Hash160Type},
|
||||
{Name: "account", Type: smartcontract.Hash160Type},
|
||||
},
|
||||
ReturnType: smartcontract.IntegerType,
|
||||
Safe: true,
|
||||
|
@ -21,10 +21,10 @@ var nep17 = &Standard{
|
|||
{
|
||||
Name: "transfer",
|
||||
Parameters: []manifest.Parameter{
|
||||
{Type: smartcontract.Hash160Type},
|
||||
{Type: smartcontract.Hash160Type},
|
||||
{Type: smartcontract.IntegerType},
|
||||
{Type: smartcontract.AnyType},
|
||||
{Name: "from", Type: smartcontract.Hash160Type},
|
||||
{Name: "to", Type: smartcontract.Hash160Type},
|
||||
{Name: "amount", Type: smartcontract.IntegerType},
|
||||
{Name: "data", Type: smartcontract.AnyType},
|
||||
},
|
||||
ReturnType: smartcontract.BoolType,
|
||||
},
|
||||
|
@ -33,9 +33,9 @@ var nep17 = &Standard{
|
|||
{
|
||||
Name: "Transfer",
|
||||
Parameters: []manifest.Parameter{
|
||||
{Type: smartcontract.Hash160Type},
|
||||
{Type: smartcontract.Hash160Type},
|
||||
{Type: smartcontract.IntegerType},
|
||||
{Name: "from", Type: smartcontract.Hash160Type},
|
||||
{Name: "to", Type: smartcontract.Hash160Type},
|
||||
{Name: "amount", Type: smartcontract.IntegerType},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue