rpcbinding: use binding condig to generate code for simple arrays
Part of #2767.
This commit is contained in:
parent
b5c79f4be3
commit
82c6ce218b
6 changed files with 266 additions and 1 deletions
|
@ -365,6 +365,44 @@ func TestGenerateRPCBindings(t *testing.T) {
|
||||||
filepath.Join("testdata", "nonepiter", "iter.go"))
|
filepath.Join("testdata", "nonepiter", "iter.go"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAssistedRPCBindings(t *testing.T) {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.Commands = NewCommands()
|
||||||
|
|
||||||
|
var checkBinding = func(source string) {
|
||||||
|
t.Run(source, func(t *testing.T) {
|
||||||
|
manifestF := filepath.Join(tmpDir, "manifest.json")
|
||||||
|
bindingF := filepath.Join(tmpDir, "binding.yml")
|
||||||
|
nefF := filepath.Join(tmpDir, "out.nef")
|
||||||
|
require.NoError(t, app.Run([]string{"", "contract", "compile",
|
||||||
|
"--in", source,
|
||||||
|
"--config", filepath.Join(source, "config.yml"),
|
||||||
|
"--manifest", manifestF,
|
||||||
|
"--bindings", bindingF,
|
||||||
|
"--out", nefF,
|
||||||
|
}))
|
||||||
|
outFile := filepath.Join(tmpDir, "out.go")
|
||||||
|
require.NoError(t, app.Run([]string{"", "contract", "generate-rpcwrapper",
|
||||||
|
"--config", bindingF,
|
||||||
|
"--manifest", manifestF,
|
||||||
|
"--out", outFile,
|
||||||
|
"--hash", "0x00112233445566778899aabbccddeeff00112233",
|
||||||
|
}))
|
||||||
|
|
||||||
|
data, err := os.ReadFile(outFile)
|
||||||
|
require.NoError(t, err)
|
||||||
|
data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows.
|
||||||
|
expected, err := os.ReadFile(filepath.Join(source, "rpcbindings.out"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows.
|
||||||
|
require.Equal(t, string(expected), string(data))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
checkBinding(filepath.Join("testdata", "types"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestGenerate_Errors(t *testing.T) {
|
func TestGenerate_Errors(t *testing.T) {
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Commands = []cli.Command{generateWrapperCmd}
|
app.Commands = []cli.Command{generateWrapperCmd}
|
||||||
|
|
3
cli/smartcontract/testdata/types/config.yml
vendored
Normal file
3
cli/smartcontract/testdata/types/config.yml
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
name: "Types"
|
||||||
|
sourceurl: https://github.com/nspcc-dev/neo-go/
|
||||||
|
safemethods: ["bool", "int", "bytes", "string", "hash160", "hash256", "publicKey", "signature", "bools", "ints", "bytess", "strings", "hash160s", "hash256s", "publicKeys", "signatures"]
|
109
cli/smartcontract/testdata/types/rpcbindings.out
vendored
Normal file
109
cli/smartcontract/testdata/types/rpcbindings.out
vendored
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
// Package types contains RPC wrappers for Types contract.
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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/unwrap"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hash contains contract hash.
|
||||||
|
var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0}
|
||||||
|
|
||||||
|
// Invoker is used by ContractReader to call various safe methods.
|
||||||
|
type Invoker interface {
|
||||||
|
Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContractReader implements safe contract methods.
|
||||||
|
type ContractReader struct {
|
||||||
|
invoker Invoker
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
|
||||||
|
func NewReader(invoker Invoker) *ContractReader {
|
||||||
|
return &ContractReader{invoker}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Bool invokes `bool` method of contract.
|
||||||
|
func (c *ContractReader) Bool(b bool) (bool, error) {
|
||||||
|
return unwrap.Bool(c.invoker.Call(Hash, "bool", b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bools invokes `bools` method of contract.
|
||||||
|
func (c *ContractReader) Bools(b []bool) ([]bool, error) {
|
||||||
|
return unwrap.ArrayOfBools(c.invoker.Call(Hash, "bools", b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes invokes `bytes` method of contract.
|
||||||
|
func (c *ContractReader) Bytes(b []byte) ([]byte, error) {
|
||||||
|
return unwrap.Bytes(c.invoker.Call(Hash, "bytes", b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytess invokes `bytess` method of contract.
|
||||||
|
func (c *ContractReader) Bytess(b [][]byte) ([][]byte, error) {
|
||||||
|
return unwrap.ArrayOfBytes(c.invoker.Call(Hash, "bytess", b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash160 invokes `hash160` method of contract.
|
||||||
|
func (c *ContractReader) Hash160(h util.Uint160) (util.Uint160, error) {
|
||||||
|
return unwrap.Uint160(c.invoker.Call(Hash, "hash160", h))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash160s invokes `hash160s` method of contract.
|
||||||
|
func (c *ContractReader) Hash160s(h []util.Uint160) ([]util.Uint160, error) {
|
||||||
|
return unwrap.ArrayOfUint160(c.invoker.Call(Hash, "hash160s", h))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash256 invokes `hash256` method of contract.
|
||||||
|
func (c *ContractReader) Hash256(h util.Uint256) (util.Uint256, error) {
|
||||||
|
return unwrap.Uint256(c.invoker.Call(Hash, "hash256", h))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash256s invokes `hash256s` method of contract.
|
||||||
|
func (c *ContractReader) Hash256s(h []util.Uint256) ([]util.Uint256, error) {
|
||||||
|
return unwrap.ArrayOfUint256(c.invoker.Call(Hash, "hash256s", h))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int invokes `int` method of contract.
|
||||||
|
func (c *ContractReader) Int(i *big.Int) (*big.Int, error) {
|
||||||
|
return unwrap.BigInt(c.invoker.Call(Hash, "int", i))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ints invokes `ints` method of contract.
|
||||||
|
func (c *ContractReader) Ints(i []*big.Int) ([]*big.Int, error) {
|
||||||
|
return unwrap.ArrayOfBigInts(c.invoker.Call(Hash, "ints", i))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKey invokes `publicKey` method of contract.
|
||||||
|
func (c *ContractReader) PublicKey(k *keys.PublicKey) (*keys.PublicKey, error) {
|
||||||
|
return unwrap.PublicKey(c.invoker.Call(Hash, "publicKey", k))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeys invokes `publicKeys` method of contract.
|
||||||
|
func (c *ContractReader) PublicKeys(k keys.PublicKeys) (keys.PublicKeys, error) {
|
||||||
|
return unwrap.ArrayOfPublicKeys(c.invoker.Call(Hash, "publicKeys", k))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature invokes `signature` method of contract.
|
||||||
|
func (c *ContractReader) Signature(s []byte) ([]byte, error) {
|
||||||
|
return unwrap.Bytes(c.invoker.Call(Hash, "signature", s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signatures invokes `signatures` method of contract.
|
||||||
|
func (c *ContractReader) Signatures(s [][]byte) ([][]byte, error) {
|
||||||
|
return unwrap.ArrayOfBytes(c.invoker.Call(Hash, "signatures", s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// String invokes `string` method of contract.
|
||||||
|
func (c *ContractReader) String(s string) (string, error) {
|
||||||
|
return unwrap.UTF8String(c.invoker.Call(Hash, "string", s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strings invokes `strings` method of contract.
|
||||||
|
func (c *ContractReader) Strings(s []string) ([]string, error) {
|
||||||
|
return unwrap.ArrayOfUTF8Strings(c.invoker.Call(Hash, "strings", s))
|
||||||
|
}
|
69
cli/smartcontract/testdata/types/types.go
vendored
Normal file
69
cli/smartcontract/testdata/types/types.go
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Bool(b bool) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func Int(i int) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func Bytes(b []byte) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func String(s string) string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func Hash160(h interop.Hash160) interop.Hash160 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Hash256(h interop.Hash256) interop.Hash256 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PublicKey(k interop.PublicKey) interop.PublicKey {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Signature(s interop.Signature) interop.Signature {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Bools(b []bool) []bool {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ints(i []int) []int {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Bytess(b [][]byte) [][]byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Strings(s []string) []string {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Hash160s(h []interop.Hash160) []interop.Hash160 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Hash256s(h []interop.Hash256) []interop.Hash256 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PublicKeys(k []interop.PublicKey) []interop.PublicKey {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Signatures(s []interop.Signature) []interop.Signature {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -446,6 +446,10 @@ does not do anything else unless the method's returned value is of a boolean
|
||||||
type, in this case an ASSERT is added to script making it fail when the method
|
type, in this case an ASSERT is added to script making it fail when the method
|
||||||
returns false.
|
returns false.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./bin/neo-go contract generate-rpcwrapper --manifest manifest.json --out rpcwrapper.go --hash 0x1b4357bff5a01bdf2a6581247cf9ed1e24629176
|
||||||
|
```
|
||||||
|
|
||||||
If your contract is NEP-11 or NEP-17 that's autodetected and an appropriate
|
If your contract is NEP-11 or NEP-17 that's autodetected and an appropriate
|
||||||
package is included as well. Notice that the type data available in the
|
package is included as well. Notice that the type data available in the
|
||||||
manifest is limited, so in some cases the interface generated may use generic
|
manifest is limited, so in some cases the interface generated may use generic
|
||||||
|
@ -454,8 +458,13 @@ iterator and an appropriate unwrapper is used with UUID and iterator structure
|
||||||
result. This pair can then be used in Invoker `TraverseIterator` method to
|
result. This pair can then be used in Invoker `TraverseIterator` method to
|
||||||
retrieve actual resulting items.
|
retrieve actual resulting items.
|
||||||
|
|
||||||
|
Go contracts can also make use of additional type data from bindings
|
||||||
|
configuration file generated during compilation. At the moment it allows to
|
||||||
|
generate proper wrappers for simple array types, but doesn't cover structures:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ ./bin/neo-go contract generate-rpcwrapper --manifest manifest.json --out rpcwrapper.go --hash 0x1b4357bff5a01bdf2a6581247cf9ed1e24629176
|
$ ./bin/neo-go contract compile -i contract.go --config contract.yml -o contract.nef --manifest manifest.json --bindings contract.bindings.yml
|
||||||
|
$ ./bin/neo-go contract generate-rpcwrapper --manifest manifest.json --config contract.bindings.yml --out rpcwrapper.go --hash 0x1b4357bff5a01bdf2a6581247cf9ed1e24629176
|
||||||
```
|
```
|
||||||
|
|
||||||
## Smart contract examples
|
## Smart contract examples
|
||||||
|
|
|
@ -296,6 +296,29 @@ func dropStdMethods(meths []manifest.Method, std *standard.Standard) []manifest.
|
||||||
}
|
}
|
||||||
|
|
||||||
func scTypeToGo(name string, typ smartcontract.ParamType, overrides map[string]binding.Override) (string, string) {
|
func scTypeToGo(name string, typ smartcontract.ParamType, overrides map[string]binding.Override) (string, string) {
|
||||||
|
over, ok := overrides[name]
|
||||||
|
if ok {
|
||||||
|
switch over.TypeName {
|
||||||
|
case "[]bool":
|
||||||
|
return "[]bool", ""
|
||||||
|
case "[]int", "[]uint", "[]int8", "[]uint8", "[]int16",
|
||||||
|
"[]uint16", "[]int32", "[]uint32", "[]int64", "[]uint64":
|
||||||
|
return "[]*big.Int", "math/big"
|
||||||
|
case "[][]byte":
|
||||||
|
return "[][]byte", ""
|
||||||
|
case "[]string":
|
||||||
|
return "[]string", ""
|
||||||
|
case "[]interop.Hash160":
|
||||||
|
return "[]util.Uint160", "github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
case "[]interop.Hash256":
|
||||||
|
return "[]util.Uint256", "github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
case "[]interop.PublicKey":
|
||||||
|
return "keys.PublicKeys", "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
case "[]interop.Signature":
|
||||||
|
return "[][]byte", ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch typ {
|
switch typ {
|
||||||
case smartcontract.AnyType:
|
case smartcontract.AnyType:
|
||||||
return "interface{}", ""
|
return "interface{}", ""
|
||||||
|
@ -383,6 +406,20 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st
|
||||||
ctr.SafeMethods[i].CallFlag = "Array"
|
ctr.SafeMethods[i].CallFlag = "Array"
|
||||||
case "*stackitem.Map":
|
case "*stackitem.Map":
|
||||||
ctr.SafeMethods[i].CallFlag = "Map"
|
ctr.SafeMethods[i].CallFlag = "Map"
|
||||||
|
case "[]bool":
|
||||||
|
ctr.SafeMethods[i].CallFlag = "ArrayOfBools"
|
||||||
|
case "[]*big.Int":
|
||||||
|
ctr.SafeMethods[i].CallFlag = "ArrayOfBigInts"
|
||||||
|
case "[][]byte":
|
||||||
|
ctr.SafeMethods[i].CallFlag = "ArrayOfBytes"
|
||||||
|
case "[]string":
|
||||||
|
ctr.SafeMethods[i].CallFlag = "ArrayOfUTF8Strings"
|
||||||
|
case "[]util.Uint160":
|
||||||
|
ctr.SafeMethods[i].CallFlag = "ArrayOfUint160"
|
||||||
|
case "[]util.Uint256":
|
||||||
|
ctr.SafeMethods[i].CallFlag = "ArrayOfUint256"
|
||||||
|
case "keys.PublicKeys":
|
||||||
|
ctr.SafeMethods[i].CallFlag = "ArrayOfPublicKeys"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue