forked from TrueCloudLab/neoneo-go
smartcontract: add checks for onNEP*Payable methods
When they are present in contract, we want them to have correct signature.
This commit is contained in:
parent
d89f055697
commit
b38443fe20
5 changed files with 127 additions and 29 deletions
|
@ -222,36 +222,9 @@ func CompileAndSave(src string, o *Options) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.ManifestFile != "" {
|
if o.ManifestFile != "" {
|
||||||
m, err := di.ConvertToManifest(o)
|
m, err := CreateManifest(di, o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return b, fmt.Errorf("failed to convert debug info to manifest: %w", err)
|
return b, err
|
||||||
}
|
|
||||||
if !o.NoStandardCheck {
|
|
||||||
if err := standard.CheckABI(m, o.ContractSupportedStandards...); err != nil {
|
|
||||||
return b, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !o.NoEventsCheck {
|
|
||||||
for name := range di.EmittedEvents {
|
|
||||||
ev := m.ABI.GetEvent(name)
|
|
||||||
if ev == nil {
|
|
||||||
return nil, fmt.Errorf("event '%s' is emitted but not specified in manifest", name)
|
|
||||||
}
|
|
||||||
argsList := di.EmittedEvents[name]
|
|
||||||
for i := range argsList {
|
|
||||||
if len(argsList[i]) != len(ev.Parameters) {
|
|
||||||
return nil, fmt.Errorf("event '%s' should have %d parameters but has %d",
|
|
||||||
name, len(ev.Parameters), len(argsList[i]))
|
|
||||||
}
|
|
||||||
for j := range ev.Parameters {
|
|
||||||
expected := ev.Parameters[j].Type.String()
|
|
||||||
if argsList[i][j] != expected {
|
|
||||||
return nil, fmt.Errorf("event '%s' should have '%s' as type of %d parameter, "+
|
|
||||||
"got: %s", name, expected, j+1, argsList[i][j])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mData, err := json.Marshal(m)
|
mData, err := json.Marshal(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -262,3 +235,49 @@ func CompileAndSave(src string, o *Options) ([]byte, error) {
|
||||||
|
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateManifest creates manifest and checks that is is valid.
|
||||||
|
func CreateManifest(di *DebugInfo, o *Options) (*manifest.Manifest, error) {
|
||||||
|
m, err := di.ConvertToManifest(o)
|
||||||
|
if err != nil {
|
||||||
|
return m, fmt.Errorf("failed to convert debug info to manifest: %w", err)
|
||||||
|
}
|
||||||
|
if !o.NoStandardCheck {
|
||||||
|
if err := standard.CheckABI(m, o.ContractSupportedStandards...); err != nil {
|
||||||
|
return m, err
|
||||||
|
}
|
||||||
|
if m.ABI.GetMethod(manifest.MethodOnNEP11Payment, -1) != nil {
|
||||||
|
if err := standard.CheckABI(m, manifest.NEP11Payable); err != nil {
|
||||||
|
return m, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m.ABI.GetMethod(manifest.MethodOnNEP17Payment, -1) != nil {
|
||||||
|
if err := standard.CheckABI(m, manifest.NEP17Payable); err != nil {
|
||||||
|
return m, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !o.NoEventsCheck {
|
||||||
|
for name := range di.EmittedEvents {
|
||||||
|
ev := m.ABI.GetEvent(name)
|
||||||
|
if ev == nil {
|
||||||
|
return nil, fmt.Errorf("event '%s' is emitted but not specified in manifest", name)
|
||||||
|
}
|
||||||
|
argsList := di.EmittedEvents[name]
|
||||||
|
for i := range argsList {
|
||||||
|
if len(argsList[i]) != len(ev.Parameters) {
|
||||||
|
return nil, fmt.Errorf("event '%s' should have %d parameters but has %d",
|
||||||
|
name, len(ev.Parameters), len(argsList[i]))
|
||||||
|
}
|
||||||
|
for j := range ev.Parameters {
|
||||||
|
expected := ev.Parameters[j].Type.String()
|
||||||
|
if argsList[i][j] != expected {
|
||||||
|
return nil, fmt.Errorf("event '%s' should have '%s' as type of %d parameter, "+
|
||||||
|
"got: %s", name, expected, j+1, argsList[i][j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||||
|
@ -90,3 +91,37 @@ func compileFile(src string) error {
|
||||||
_, err := compiler.Compile(src, nil)
|
_, err := compiler.Compile(src, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOnPayableChecks(t *testing.T) {
|
||||||
|
compileAndCheck := func(t *testing.T, src string) error {
|
||||||
|
_, di, err := compiler.CompileWithDebugInfo("payable", strings.NewReader(src))
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = compiler.CreateManifest(di, &compiler.Options{})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("NEP-11, good", func(t *testing.T) {
|
||||||
|
src := `package payable
|
||||||
|
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||||
|
func OnNEP11Payment(from interop.Hash160, amount int, tokenID []byte) {}`
|
||||||
|
require.NoError(t, compileAndCheck(t, src))
|
||||||
|
})
|
||||||
|
t.Run("NEP-11, bad", func(t *testing.T) {
|
||||||
|
src := `package payable
|
||||||
|
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||||
|
func OnNEP11Payment(from interop.Hash160, amount int, oldParam string, tokenID []byte) {}`
|
||||||
|
require.Error(t, compileAndCheck(t, src))
|
||||||
|
})
|
||||||
|
t.Run("NEP-17, good", func(t *testing.T) {
|
||||||
|
src := `package payable
|
||||||
|
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||||
|
func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) {}`
|
||||||
|
require.NoError(t, compileAndCheck(t, src))
|
||||||
|
})
|
||||||
|
t.Run("NEP-17, bad", func(t *testing.T) {
|
||||||
|
src := `package payable
|
||||||
|
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||||
|
func OnNEP17Payment(from interop.Hash160, amount int, data interface{}, extra int) {}`
|
||||||
|
require.Error(t, compileAndCheck(t, src))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,10 @@ const (
|
||||||
NEP11StandardName = "NEP-11"
|
NEP11StandardName = "NEP-11"
|
||||||
// NEP17StandardName represents the name of NEP17 smartcontract standard.
|
// NEP17StandardName represents the name of NEP17 smartcontract standard.
|
||||||
NEP17StandardName = "NEP-17"
|
NEP17StandardName = "NEP-17"
|
||||||
|
// NEP11Payable represents the name of contract interface which can receive NEP-11 tokens.
|
||||||
|
NEP11Payable = "NEP-11-Payable"
|
||||||
|
// NEP17Payable represents the name of contract interface which can receive NEP-17 tokens.
|
||||||
|
NEP17Payable = "NEP-17-Payable"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Manifest represens contract metadata.
|
// Manifest represens contract metadata.
|
||||||
|
|
|
@ -21,6 +21,8 @@ var (
|
||||||
var checks = map[string][]*Standard{
|
var checks = map[string][]*Standard{
|
||||||
manifest.NEP11StandardName: {nep11NonDivisible, nep11Divisible},
|
manifest.NEP11StandardName: {nep11NonDivisible, nep11Divisible},
|
||||||
manifest.NEP17StandardName: {nep17},
|
manifest.NEP17StandardName: {nep17},
|
||||||
|
manifest.NEP11Payable: {nep11payable},
|
||||||
|
manifest.NEP17Payable: {nep17payable},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check checks if manifest complies with all provided standards.
|
// Check checks if manifest complies with all provided standards.
|
||||||
|
|
38
pkg/smartcontract/manifest/standard/payable.go
Normal file
38
pkg/smartcontract/manifest/standard/payable.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package standard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
|
)
|
||||||
|
|
||||||
|
var nep11payable = &Standard{
|
||||||
|
Manifest: manifest.Manifest{
|
||||||
|
ABI: manifest.ABI{
|
||||||
|
Methods: []manifest.Method{{
|
||||||
|
Name: manifest.MethodOnNEP11Payment,
|
||||||
|
Parameters: []manifest.Parameter{
|
||||||
|
{Name: "from", Type: smartcontract.Hash160Type},
|
||||||
|
{Name: "amount", Type: smartcontract.IntegerType},
|
||||||
|
{Name: "tokenid", Type: smartcontract.ByteArrayType},
|
||||||
|
},
|
||||||
|
ReturnType: smartcontract.VoidType,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var nep17payable = &Standard{
|
||||||
|
Manifest: manifest.Manifest{
|
||||||
|
ABI: manifest.ABI{
|
||||||
|
Methods: []manifest.Method{{
|
||||||
|
Name: manifest.MethodOnNEP17Payment,
|
||||||
|
Parameters: []manifest.Parameter{
|
||||||
|
{Name: "from", Type: smartcontract.Hash160Type},
|
||||||
|
{Name: "amount", Type: smartcontract.IntegerType},
|
||||||
|
{Name: "data", Type: smartcontract.AnyType},
|
||||||
|
},
|
||||||
|
ReturnType: smartcontract.VoidType,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
Loading…
Reference in a new issue