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"))
|
||||
}
|
||||
|
||||
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) {
|
||||
app := cli.NewApp()
|
||||
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
|
||||
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
|
||||
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
|
||||
|
@ -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
|
||||
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
|
||||
|
|
|
@ -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) {
|
||||
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 {
|
||||
case smartcontract.AnyType:
|
||||
return "interface{}", ""
|
||||
|
@ -383,6 +406,20 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st
|
|||
ctr.SafeMethods[i].CallFlag = "Array"
|
||||
case "*stackitem.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