forked from TrueCloudLab/neoneo-go
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)
|
return b, fmt.Errorf("failed to convert debug info to manifest: %w", err)
|
||||||
}
|
}
|
||||||
if !o.NoStandardCheck {
|
if !o.NoStandardCheck {
|
||||||
if err := standard.Check(m, o.ContractSupportedStandards...); err != nil {
|
if err := standard.CheckABI(m, o.ContractSupportedStandards...); err != nil {
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ var (
|
||||||
ErrEventMissing = errors.New("event missing")
|
ErrEventMissing = errors.New("event missing")
|
||||||
ErrInvalidReturnType = errors.New("invalid return type")
|
ErrInvalidReturnType = errors.New("invalid return type")
|
||||||
ErrInvalidParameterCount = errors.New("invalid parameter count")
|
ErrInvalidParameterCount = errors.New("invalid parameter count")
|
||||||
|
ErrInvalidParameterName = errors.New("invalid parameter name")
|
||||||
ErrInvalidParameterType = errors.New("invalid parameter type")
|
ErrInvalidParameterType = errors.New("invalid parameter type")
|
||||||
ErrSafeMethodMismatch = errors.New("method has wrong safe flag")
|
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.
|
// Check checks if manifest complies with all provided standards.
|
||||||
// Currently only NEP-17 is supported.
|
// Currently only NEP-17 is supported.
|
||||||
func Check(m *manifest.Manifest, standards ...string) error {
|
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 {
|
for i := range standards {
|
||||||
ss, ok := checks[standards[i]]
|
ss, ok := checks[standards[i]]
|
||||||
if ok {
|
if ok {
|
||||||
var err error
|
var err error
|
||||||
for i := range ss {
|
for i := range ss {
|
||||||
if err = Comply(m, ss[i]); err == nil {
|
if err = comply(m, checkNames, ss[i]); err == nil {
|
||||||
break
|
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.
|
// Comply if m has all methods and event from st manifest and they have the same signature.
|
||||||
// Parameter names are ignored.
|
// Parameter names are ignored.
|
||||||
func Comply(m *manifest.Manifest, st *Standard) error {
|
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 st.Base != nil {
|
||||||
if err := Comply(m, st.Base); err != nil {
|
if err := comply(m, checkNames, st.Base); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, stm := range st.ABI.Methods {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +84,10 @@ func Comply(m *manifest.Manifest, st *Standard) error {
|
||||||
name, len(ste.Parameters), len(ed.Parameters))
|
name, len(ste.Parameters), len(ed.Parameters))
|
||||||
}
|
}
|
||||||
for i := range ste.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 {
|
if ste.Parameters[i].Type != ed.Parameters[i].Type {
|
||||||
return fmt.Errorf("%w: event '%s' (expected %s, got %s)", ErrInvalidParameterType,
|
return fmt.Errorf("%w: event '%s' (expected %s, got %s)", ErrInvalidParameterType,
|
||||||
name, ste.Parameters[i].Type, ed.Parameters[i].Type)
|
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 {
|
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 err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
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))
|
actual := m.ABI.GetMethod(expected.Name, len(expected.Parameters))
|
||||||
if actual == nil {
|
if actual == nil {
|
||||||
if allowMissing {
|
if allowMissing {
|
||||||
|
@ -93,6 +117,10 @@ func checkMethod(m *manifest.Manifest, expected *manifest.Method, allowMissing b
|
||||||
expected.Name, expected.ReturnType, actual.ReturnType)
|
expected.Name, expected.ReturnType, actual.ReturnType)
|
||||||
}
|
}
|
||||||
for i := range expected.Parameters {
|
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 {
|
if expected.Parameters[i].Type != actual.Parameters[i].Type {
|
||||||
return fmt.Errorf("%w: '%s'[%d] (expected %s, got %s)", ErrInvalidParameterType,
|
return fmt.Errorf("%w: '%s'[%d] (expected %s, got %s)", ErrInvalidParameterType,
|
||||||
expected.Name, i, expected.Parameters[i].Type, actual.Parameters[i].Type)
|
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) {
|
func TestMissingEvent(t *testing.T) {
|
||||||
m := fooMethodBarEvent()
|
m := fooMethodBarEvent()
|
||||||
m.ABI.GetEvent("bar").Name = "notabar"
|
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.Methods = append(m.ABI.Methods, nep17.ABI.Methods...)
|
||||||
m.ABI.Events = append(m.ABI.Events, nep17.ABI.Events...)
|
m.ABI.Events = append(m.ABI.Events, nep17.ABI.Events...)
|
||||||
require.NoError(t, Check(m, manifest.NEP17StandardName))
|
require.NoError(t, Check(m, manifest.NEP17StandardName))
|
||||||
|
require.NoError(t, CheckABI(m, manifest.NEP17StandardName))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOptional(t *testing.T) {
|
func TestOptional(t *testing.T) {
|
||||||
|
|
|
@ -13,7 +13,7 @@ var nep11Base = &Standard{
|
||||||
{
|
{
|
||||||
Name: "balanceOf",
|
Name: "balanceOf",
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
{Type: smartcontract.Hash160Type},
|
{Name: "owner", Type: smartcontract.Hash160Type},
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.IntegerType,
|
ReturnType: smartcontract.IntegerType,
|
||||||
Safe: true,
|
Safe: true,
|
||||||
|
@ -22,15 +22,15 @@ var nep11Base = &Standard{
|
||||||
Name: "tokensOf",
|
Name: "tokensOf",
|
||||||
ReturnType: smartcontract.AnyType, // Iterator
|
ReturnType: smartcontract.AnyType, // Iterator
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
{Type: smartcontract.Hash160Type},
|
{Name: "owner", Type: smartcontract.Hash160Type},
|
||||||
},
|
},
|
||||||
Safe: true,
|
Safe: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "transfer",
|
Name: "transfer",
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
{Type: smartcontract.Hash160Type},
|
{Name: "to", Type: smartcontract.Hash160Type},
|
||||||
{Type: smartcontract.ByteArrayType},
|
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.BoolType,
|
ReturnType: smartcontract.BoolType,
|
||||||
},
|
},
|
||||||
|
@ -39,10 +39,10 @@ var nep11Base = &Standard{
|
||||||
{
|
{
|
||||||
Name: "Transfer",
|
Name: "Transfer",
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
{Type: smartcontract.Hash160Type},
|
{Name: "from", Type: smartcontract.Hash160Type},
|
||||||
{Type: smartcontract.Hash160Type},
|
{Name: "to", Type: smartcontract.Hash160Type},
|
||||||
{Type: smartcontract.IntegerType},
|
{Name: "amount", Type: smartcontract.IntegerType},
|
||||||
{Type: smartcontract.ByteArrayType},
|
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -52,7 +52,7 @@ var nep11Base = &Standard{
|
||||||
{
|
{
|
||||||
Name: "properties",
|
Name: "properties",
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
{Type: smartcontract.ByteArrayType},
|
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.MapType,
|
ReturnType: smartcontract.MapType,
|
||||||
Safe: true,
|
Safe: true,
|
||||||
|
@ -73,7 +73,7 @@ var nep11NonDivisible = &Standard{
|
||||||
{
|
{
|
||||||
Name: "ownerOf",
|
Name: "ownerOf",
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
{Type: smartcontract.ByteArrayType},
|
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.Hash160Type,
|
ReturnType: smartcontract.Hash160Type,
|
||||||
Safe: true,
|
Safe: true,
|
||||||
|
@ -91,8 +91,8 @@ var nep11Divisible = &Standard{
|
||||||
{
|
{
|
||||||
Name: "balanceOf",
|
Name: "balanceOf",
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
{Type: smartcontract.Hash160Type},
|
{Name: "owner", Type: smartcontract.Hash160Type},
|
||||||
{Type: smartcontract.ByteArrayType},
|
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.IntegerType,
|
ReturnType: smartcontract.IntegerType,
|
||||||
Safe: true,
|
Safe: true,
|
||||||
|
@ -100,7 +100,7 @@ var nep11Divisible = &Standard{
|
||||||
{
|
{
|
||||||
Name: "ownerOf",
|
Name: "ownerOf",
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
{Type: smartcontract.ByteArrayType},
|
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.AnyType,
|
ReturnType: smartcontract.AnyType,
|
||||||
Safe: true,
|
Safe: true,
|
||||||
|
@ -108,10 +108,10 @@ var nep11Divisible = &Standard{
|
||||||
{
|
{
|
||||||
Name: "transfer",
|
Name: "transfer",
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
{Type: smartcontract.Hash160Type},
|
{Name: "from", Type: smartcontract.Hash160Type},
|
||||||
{Type: smartcontract.Hash160Type},
|
{Name: "to", Type: smartcontract.Hash160Type},
|
||||||
{Type: smartcontract.IntegerType},
|
{Name: "amount", Type: smartcontract.IntegerType},
|
||||||
{Type: smartcontract.ByteArrayType},
|
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.BoolType,
|
ReturnType: smartcontract.BoolType,
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,7 +13,7 @@ var nep17 = &Standard{
|
||||||
{
|
{
|
||||||
Name: "balanceOf",
|
Name: "balanceOf",
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
{Type: smartcontract.Hash160Type},
|
{Name: "account", Type: smartcontract.Hash160Type},
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.IntegerType,
|
ReturnType: smartcontract.IntegerType,
|
||||||
Safe: true,
|
Safe: true,
|
||||||
|
@ -21,10 +21,10 @@ var nep17 = &Standard{
|
||||||
{
|
{
|
||||||
Name: "transfer",
|
Name: "transfer",
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
{Type: smartcontract.Hash160Type},
|
{Name: "from", Type: smartcontract.Hash160Type},
|
||||||
{Type: smartcontract.Hash160Type},
|
{Name: "to", Type: smartcontract.Hash160Type},
|
||||||
{Type: smartcontract.IntegerType},
|
{Name: "amount", Type: smartcontract.IntegerType},
|
||||||
{Type: smartcontract.AnyType},
|
{Name: "data", Type: smartcontract.AnyType},
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.BoolType,
|
ReturnType: smartcontract.BoolType,
|
||||||
},
|
},
|
||||||
|
@ -33,9 +33,9 @@ var nep17 = &Standard{
|
||||||
{
|
{
|
||||||
Name: "Transfer",
|
Name: "Transfer",
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
{Type: smartcontract.Hash160Type},
|
{Name: "from", Type: smartcontract.Hash160Type},
|
||||||
{Type: smartcontract.Hash160Type},
|
{Name: "to", Type: smartcontract.Hash160Type},
|
||||||
{Type: smartcontract.IntegerType},
|
{Name: "amount", Type: smartcontract.IntegerType},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue