rpcbinding: add GAS testcase, fix methodless wrappers

* strip NEP-XX methods before going into generator to avoid unused imports
 * nepXX.Invoker types already include Call
 * always import util, it's used for Hash
This commit is contained in:
Roman Khimov 2022-11-08 15:43:32 +03:00
parent aeb61fb61d
commit df29008a50
6 changed files with 122 additions and 66 deletions

View file

@ -294,6 +294,7 @@ package myspacecontract
import ( import (
"github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"math/big" "math/big"
) )
@ -328,34 +329,34 @@ func TestGenerateRPCBindings(t *testing.T) {
app := cli.NewApp() app := cli.NewApp()
app.Commands = []cli.Command{generateWrapperCmd, generateRPCWrapperCmd} app.Commands = []cli.Command{generateWrapperCmd, generateRPCWrapperCmd}
outFile := filepath.Join(tmpDir, "out.go") var checkBinding = func(manifest string, hash string, good string) {
require.NoError(t, app.Run([]string{"", "generate-rpcwrapper", t.Run(manifest, func(t *testing.T) {
"--manifest", filepath.Join("testdata", "nex", "nex.manifest.json"), outFile := filepath.Join(tmpDir, "out.go")
"--out", outFile, require.NoError(t, app.Run([]string{"", "generate-rpcwrapper",
"--hash", "0xa2a67f09e8cf22c6bfd5cea24adc0f4bf0a11aa8", "--manifest", manifest,
})) "--out", outFile,
"--hash", hash,
}))
data, err := os.ReadFile(outFile) data, err := os.ReadFile(outFile)
require.NoError(t, err) require.NoError(t, err)
data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows. data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows.
expected, err := os.ReadFile(filepath.Join("testdata", "nex", "nex.go")) expected, err := os.ReadFile(good)
require.NoError(t, err) require.NoError(t, err)
expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows. expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows.
require.Equal(t, string(expected), string(data)) require.Equal(t, string(expected), string(data))
})
}
require.NoError(t, app.Run([]string{"", "generate-rpcwrapper", checkBinding(filepath.Join("testdata", "nex", "nex.manifest.json"),
"--manifest", filepath.Join("testdata", "nameservice", "nns.manifest.json"), "0xa2a67f09e8cf22c6bfd5cea24adc0f4bf0a11aa8",
"--out", outFile, filepath.Join("testdata", "nex", "nex.go"))
"--hash", "0x50ac1c37690cc2cfc594472833cf57505d5f46de", checkBinding(filepath.Join("testdata", "nameservice", "nns.manifest.json"),
})) "0x50ac1c37690cc2cfc594472833cf57505d5f46de",
filepath.Join("testdata", "nameservice", "nns.go"))
data, err = os.ReadFile(outFile) checkBinding(filepath.Join("testdata", "gas", "gas.manifest.json"),
require.NoError(t, err) "0xd2a4cff31913016155e38e474a2c06d08be276cf",
data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows. filepath.Join("testdata", "gas", "gas.go"))
expected, err = os.ReadFile(filepath.Join("testdata", "nameservice", "nns.go"))
require.NoError(t, err)
expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows.
require.Equal(t, string(expected), string(data))
} }
func TestGenerate_Errors(t *testing.T) { func TestGenerate_Errors(t *testing.T) {

46
cli/smartcontract/testdata/gas/gas.go vendored Normal file
View file

@ -0,0 +1,46 @@
// Package gastoken contains RPC wrappers for GasToken contract.
package gastoken
import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// Hash contains contract hash.
var Hash = util.Uint160{0xcf, 0x76, 0xe2, 0x8b, 0xd0, 0x6, 0x2c, 0x4a, 0x47, 0x8e, 0xe3, 0x55, 0x61, 0x1, 0x13, 0x19, 0xf3, 0xcf, 0xa4, 0xd2}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep17.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep17.Actor
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep17.TokenReader
invoker Invoker
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep17.TokenWriter
actor Actor
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader(invoker Invoker) *ContractReader {
return &ContractReader{*nep17.NewReader(invoker, Hash), invoker}
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var nep17t = nep17.New(actor, Hash)
return &Contract{ContractReader{nep17t.TokenReader, actor},nep17t.TokenWriter, actor}
}

View file

@ -0,0 +1 @@
{"name":"GasToken","groups":[],"features":{},"supportedstandards":["NEP-17"],"abi":{"methods":[{"name":"balanceOf","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","offset":0,"safe":true},{"name":"decimals","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"symbol","parameters":[],"returntype":"String","offset":14,"safe":true},{"name":"totalSupply","parameters":[],"returntype":"Integer","offset":21,"safe":true},{"name":"transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","offset":28,"safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}

View file

@ -3,7 +3,6 @@ package nameservice
import ( import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
@ -18,13 +17,13 @@ var Hash = util.Uint160{0xde, 0x46, 0x5f, 0x5d, 0x50, 0x57, 0xcf, 0x33, 0x28, 0x
// Invoker is used by ContractReader to call various safe methods. // Invoker is used by ContractReader to call various safe methods.
type Invoker interface { type Invoker interface {
nep11.Invoker nep11.Invoker
Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error)
} }
// Actor is used by Contract to call state-changing methods. // Actor is used by Contract to call state-changing methods.
type Actor interface { type Actor interface {
Invoker Invoker
nep11.Actor nep11.Actor
MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error) MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error)

View file

@ -4,7 +4,6 @@ package nextoken
import ( import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -17,7 +16,6 @@ var Hash = util.Uint160{0xa8, 0x1a, 0xa1, 0xf0, 0x4b, 0xf, 0xdc, 0x4a, 0xa2, 0xc
// Invoker is used by ContractReader to call various safe methods. // Invoker is used by ContractReader to call various safe methods.
type Invoker interface { type Invoker interface {
nep17.Invoker nep17.Invoker
Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error)
} }
// Actor is used by Contract to call state-changing methods. // Actor is used by Contract to call state-changing methods.

View file

@ -92,20 +92,18 @@ var Hash = {{ .Hash }}
// Invoker is used by ContractReader to call various safe methods. // Invoker is used by ContractReader to call various safe methods.
type Invoker interface { type Invoker interface {
{{if or .IsNep11D .IsNep11ND}}nep11.Invoker {{if or .IsNep11D .IsNep11ND}} nep11.Invoker
{{end -}} {{else if .IsNep17}} nep17.Invoker
{{if .IsNep17}}nep17.Invoker {{else if len .SafeMethods}} Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error)
{{end -}} {{end -}}
Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error)
} }
{{if .HasWriter}}// Actor is used by Contract to call state-changing methods. {{if .HasWriter}}// Actor is used by Contract to call state-changing methods.
type Actor interface { type Actor interface {
Invoker Invoker
{{if or .IsNep11D .IsNep11ND}}nep11.Actor{{end -}} {{if or .IsNep11D .IsNep11ND}} nep11.Actor
{{if .IsNep17}}nep17.Actor{{end -}} {{else if .IsNep17}} nep17.Actor{{end}}
{{if len .Methods}} {{if len .Methods}} MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error)
MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error) MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
@ -198,19 +196,50 @@ func NewConfig() binding.Config {
// Generate writes Go file containing smartcontract bindings to the `cfg.Output`. // Generate writes Go file containing smartcontract bindings to the `cfg.Output`.
func Generate(cfg binding.Config) error { func Generate(cfg binding.Config) error {
// Avoid changing *cfg.Manifest.
mfst := *cfg.Manifest
mfst.ABI.Methods = make([]manifest.Method, len(mfst.ABI.Methods))
copy(mfst.ABI.Methods, cfg.Manifest.ABI.Methods)
cfg.Manifest = &mfst
var imports = make(map[string]struct{})
var ctr ContractTmpl
// Strip standard methods from NEP-XX packages.
for _, std := range cfg.Manifest.SupportedStandards {
if std == manifest.NEP11StandardName {
imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"] = struct{}{}
if standard.ComplyABI(cfg.Manifest, standard.Nep11Divisible) == nil {
mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep11Divisible)
ctr.IsNep11D = true
} else if standard.ComplyABI(cfg.Manifest, standard.Nep11NonDivisible) == nil {
mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep11NonDivisible)
ctr.IsNep11ND = true
}
break // Can't be NEP-17 at the same time.
}
if std == manifest.NEP17StandardName && standard.ComplyABI(cfg.Manifest, standard.Nep17) == nil {
mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep17)
imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"] = struct{}{}
ctr.IsNep17 = true
break // Can't be NEP-11 at the same time.
}
}
bctr, err := binding.TemplateFromManifest(cfg, scTypeToGo) bctr, err := binding.TemplateFromManifest(cfg, scTypeToGo)
if err != nil { if err != nil {
return err return err
} }
ctr := scTemplateToRPC(cfg, bctr) ctr.ContractTmpl = bctr
ctr = scTemplateToRPC(cfg, ctr, imports)
return srcTemplate.Execute(cfg.Output, ctr) return srcTemplate.Execute(cfg.Output, ctr)
} }
func dropManifestMethods(meths []binding.MethodTmpl, manifested []manifest.Method) []binding.MethodTmpl { func dropManifestMethods(meths []manifest.Method, manifested []manifest.Method) []manifest.Method {
for _, m := range manifested { for _, m := range manifested {
for i := 0; i < len(meths); i++ { for i := 0; i < len(meths); i++ {
if meths[i].NameABI == m.Name && len(meths[i].Arguments) == len(m.Parameters) { if meths[i].Name == m.Name && len(meths[i].Parameters) == len(m.Parameters) {
meths = append(meths[:i], meths[i+1:]...) meths = append(meths[:i], meths[i+1:]...)
i-- i--
} }
@ -219,7 +248,7 @@ func dropManifestMethods(meths []binding.MethodTmpl, manifested []manifest.Metho
return meths return meths
} }
func dropStdMethods(meths []binding.MethodTmpl, std *standard.Standard) []binding.MethodTmpl { func dropStdMethods(meths []manifest.Method, std *standard.Standard) []manifest.Method {
meths = dropManifestMethods(meths, std.Manifest.ABI.Methods) meths = dropManifestMethods(meths, std.Manifest.ABI.Methods)
if std.Optional != nil { if std.Optional != nil {
meths = dropManifestMethods(meths, std.Optional) meths = dropManifestMethods(meths, std.Optional)
@ -263,32 +292,11 @@ func scTypeToGo(name string, typ smartcontract.ParamType, overrides map[string]b
} }
} }
func scTemplateToRPC(cfg binding.Config, bctr binding.ContractTmpl) ContractTmpl { func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]struct{}) ContractTmpl {
var imports = make(map[string]struct{})
var ctr = ContractTmpl{ContractTmpl: bctr}
for i := range ctr.Imports { for i := range ctr.Imports {
imports[ctr.Imports[i]] = struct{}{} imports[ctr.Imports[i]] = struct{}{}
} }
ctr.Hash = fmt.Sprintf("%#v", cfg.Hash) ctr.Hash = fmt.Sprintf("%#v", cfg.Hash)
for _, std := range cfg.Manifest.SupportedStandards {
if std == manifest.NEP11StandardName {
imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"] = struct{}{}
if standard.ComplyABI(cfg.Manifest, standard.Nep11Divisible) == nil {
ctr.Methods = dropStdMethods(ctr.Methods, standard.Nep11Divisible)
ctr.IsNep11D = true
} else if standard.ComplyABI(cfg.Manifest, standard.Nep11NonDivisible) == nil {
ctr.Methods = dropStdMethods(ctr.Methods, standard.Nep11NonDivisible)
ctr.IsNep11ND = true
}
break // Can't be NEP-17 at the same time.
}
if std == manifest.NEP17StandardName && standard.ComplyABI(cfg.Manifest, standard.Nep17) == nil {
ctr.Methods = dropStdMethods(ctr.Methods, standard.Nep17)
imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"] = struct{}{}
ctr.IsNep17 = true
break // Can't be NEP-11 at the same time.
}
}
for i := 0; i < len(ctr.Methods); i++ { for i := 0; i < len(ctr.Methods); i++ {
abim := cfg.Manifest.ABI.GetMethod(ctr.Methods[i].NameABI, len(ctr.Methods[i].Arguments)) abim := cfg.Manifest.ABI.GetMethod(ctr.Methods[i].NameABI, len(ctr.Methods[i].Arguments))
if abim.Safe { if abim.Safe {
@ -332,9 +340,12 @@ func scTemplateToRPC(cfg binding.Config, bctr binding.ContractTmpl) ContractTmpl
} }
} }
imports["github.com/nspcc-dev/neo-go/pkg/neorpc/result"] = struct{}{} imports["github.com/nspcc-dev/neo-go/pkg/util"] = struct{}{}
if len(ctr.SafeMethods) > 0 { if len(ctr.SafeMethods) > 0 {
imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"] = struct{}{} imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"] = struct{}{}
if !(ctr.IsNep17 || ctr.IsNep11D || ctr.IsNep11ND) {
imports["github.com/nspcc-dev/neo-go/pkg/neorpc/result"] = struct{}{}
}
} }
if len(ctr.Methods) > 0 { if len(ctr.Methods) > 0 {
imports["github.com/nspcc-dev/neo-go/pkg/core/transaction"] = struct{}{} imports["github.com/nspcc-dev/neo-go/pkg/core/transaction"] = struct{}{}