rpcbinding: sort named types to stabilize output
Same input -> same output, otherwise tests fail and @AnnaShaleva is annoyed. Signed-off-by: Roman Khimov <roman@nspcc.ru>
This commit is contained in:
parent
eade327b9b
commit
4c9cd438f8
5 changed files with 226 additions and 16 deletions
|
@ -1,3 +1,3 @@
|
|||
name: "Types"
|
||||
sourceurl: https://github.com/nspcc-dev/neo-go/
|
||||
safemethods: ["bool", "int", "bytes", "string", "any", "hash160", "hash256", "publicKey", "signature", "bools", "ints", "bytess", "strings", "hash160s", "hash256s", "publicKeys", "signatures", "aAAStrings", "maps", "crazyMaps", "anyMaps"]
|
||||
safemethods: ["bool", "int", "bytes", "string", "any", "hash160", "hash256", "publicKey", "signature", "bools", "ints", "bytess", "strings", "hash160s", "hash256s", "publicKeys", "signatures", "aAAStrings", "maps", "crazyMaps", "anyMaps", "unnamedStructs", "unnamedStructsX"]
|
||||
|
|
|
@ -18,6 +18,17 @@ import (
|
|||
// 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}
|
||||
|
||||
// Unnamed is a contract-specific unnamed type used by its methods.
|
||||
type Unnamed struct {
|
||||
I *big.Int
|
||||
}
|
||||
|
||||
// UnnamedX is a contract-specific unnamedX type used by its methods.
|
||||
type UnnamedX struct {
|
||||
I *big.Int
|
||||
B bool
|
||||
}
|
||||
|
||||
// Invoker is used by ContractReader to call various safe methods.
|
||||
type Invoker interface {
|
||||
Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error)
|
||||
|
@ -347,3 +358,87 @@ func (c *ContractReader) String(s string) (string, error) {
|
|||
func (c *ContractReader) Strings(s []string) ([]string, error) {
|
||||
return unwrap.ArrayOfUTF8Strings(c.invoker.Call(c.hash, "strings", s))
|
||||
}
|
||||
|
||||
// UnnamedStructs invokes `unnamedStructs` method of contract.
|
||||
func (c *ContractReader) UnnamedStructs() (*Unnamed, error) {
|
||||
return itemToUnnamed(unwrap.Item(c.invoker.Call(c.hash, "unnamedStructs")))
|
||||
}
|
||||
|
||||
// UnnamedStructsX invokes `unnamedStructsX` method of contract.
|
||||
func (c *ContractReader) UnnamedStructsX() (*UnnamedX, error) {
|
||||
return itemToUnnamedX(unwrap.Item(c.invoker.Call(c.hash, "unnamedStructsX")))
|
||||
}
|
||||
|
||||
// itemToUnnamed converts stack item into *Unnamed.
|
||||
func itemToUnnamed(item stackitem.Item, err error) (*Unnamed, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res = new(Unnamed)
|
||||
err = res.FromStackItem(item)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// FromStackItem retrieves fields of Unnamed from the given
|
||||
// [stackitem.Item] or returns an error if it's not possible to do to so.
|
||||
func (res *Unnamed) FromStackItem(item stackitem.Item) error {
|
||||
arr, ok := item.Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
return errors.New("not an array")
|
||||
}
|
||||
if len(arr) != 1 {
|
||||
return errors.New("wrong number of structure elements")
|
||||
}
|
||||
|
||||
var (
|
||||
index = -1
|
||||
err error
|
||||
)
|
||||
index++
|
||||
res.I, err = arr[index].TryInteger()
|
||||
if err != nil {
|
||||
return fmt.Errorf("field I: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// itemToUnnamedX converts stack item into *UnnamedX.
|
||||
func itemToUnnamedX(item stackitem.Item, err error) (*UnnamedX, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res = new(UnnamedX)
|
||||
err = res.FromStackItem(item)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// FromStackItem retrieves fields of UnnamedX from the given
|
||||
// [stackitem.Item] or returns an error if it's not possible to do to so.
|
||||
func (res *UnnamedX) FromStackItem(item stackitem.Item) error {
|
||||
arr, ok := item.Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
return errors.New("not an array")
|
||||
}
|
||||
if len(arr) != 2 {
|
||||
return errors.New("wrong number of structure elements")
|
||||
}
|
||||
|
||||
var (
|
||||
index = -1
|
||||
err error
|
||||
)
|
||||
index++
|
||||
res.I, err = arr[index].TryInteger()
|
||||
if err != nil {
|
||||
return fmt.Errorf("field I: %w", err)
|
||||
}
|
||||
|
||||
index++
|
||||
res.B, err = arr[index].TryBool()
|
||||
if err != nil {
|
||||
return fmt.Errorf("field B: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -15,6 +15,17 @@ import (
|
|||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Unnamed is a contract-specific unnamed type used by its methods.
|
||||
type Unnamed struct {
|
||||
I *big.Int
|
||||
}
|
||||
|
||||
// UnnamedX is a contract-specific unnamedX type used by its methods.
|
||||
type UnnamedX struct {
|
||||
I *big.Int
|
||||
B bool
|
||||
}
|
||||
|
||||
// Invoker is used by ContractReader to call various safe methods.
|
||||
type Invoker interface {
|
||||
Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error)
|
||||
|
@ -343,3 +354,87 @@ func (c *ContractReader) String(s string) (string, error) {
|
|||
func (c *ContractReader) Strings(s []string) ([]string, error) {
|
||||
return unwrap.ArrayOfUTF8Strings(c.invoker.Call(c.hash, "strings", s))
|
||||
}
|
||||
|
||||
// UnnamedStructs invokes `unnamedStructs` method of contract.
|
||||
func (c *ContractReader) UnnamedStructs() (*Unnamed, error) {
|
||||
return itemToUnnamed(unwrap.Item(c.invoker.Call(c.hash, "unnamedStructs")))
|
||||
}
|
||||
|
||||
// UnnamedStructsX invokes `unnamedStructsX` method of contract.
|
||||
func (c *ContractReader) UnnamedStructsX() (*UnnamedX, error) {
|
||||
return itemToUnnamedX(unwrap.Item(c.invoker.Call(c.hash, "unnamedStructsX")))
|
||||
}
|
||||
|
||||
// itemToUnnamed converts stack item into *Unnamed.
|
||||
func itemToUnnamed(item stackitem.Item, err error) (*Unnamed, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res = new(Unnamed)
|
||||
err = res.FromStackItem(item)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// FromStackItem retrieves fields of Unnamed from the given
|
||||
// [stackitem.Item] or returns an error if it's not possible to do to so.
|
||||
func (res *Unnamed) FromStackItem(item stackitem.Item) error {
|
||||
arr, ok := item.Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
return errors.New("not an array")
|
||||
}
|
||||
if len(arr) != 1 {
|
||||
return errors.New("wrong number of structure elements")
|
||||
}
|
||||
|
||||
var (
|
||||
index = -1
|
||||
err error
|
||||
)
|
||||
index++
|
||||
res.I, err = arr[index].TryInteger()
|
||||
if err != nil {
|
||||
return fmt.Errorf("field I: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// itemToUnnamedX converts stack item into *UnnamedX.
|
||||
func itemToUnnamedX(item stackitem.Item, err error) (*UnnamedX, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res = new(UnnamedX)
|
||||
err = res.FromStackItem(item)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// FromStackItem retrieves fields of UnnamedX from the given
|
||||
// [stackitem.Item] or returns an error if it's not possible to do to so.
|
||||
func (res *UnnamedX) FromStackItem(item stackitem.Item) error {
|
||||
arr, ok := item.Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
return errors.New("not an array")
|
||||
}
|
||||
if len(arr) != 2 {
|
||||
return errors.New("wrong number of structure elements")
|
||||
}
|
||||
|
||||
var (
|
||||
index = -1
|
||||
err error
|
||||
)
|
||||
index++
|
||||
res.I, err = arr[index].TryInteger()
|
||||
if err != nil {
|
||||
return fmt.Errorf("field I: %w", err)
|
||||
}
|
||||
|
||||
index++
|
||||
res.B, err = arr[index].TryBool()
|
||||
if err != nil {
|
||||
return fmt.Errorf("field B: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -87,3 +87,17 @@ func CrazyMaps(m map[int][]map[string][]interop.Hash160) map[int][]map[string][]
|
|||
func AnyMaps(m map[int]any) map[int]any {
|
||||
return m
|
||||
}
|
||||
|
||||
func UnnamedStructs() struct{ I int } {
|
||||
return struct{ I int }{I: 123}
|
||||
}
|
||||
|
||||
func UnnamedStructsX() struct {
|
||||
I int
|
||||
B bool
|
||||
} {
|
||||
return struct {
|
||||
I int
|
||||
B bool
|
||||
}{I: 123, B: true}
|
||||
}
|
||||
|
|
|
@ -124,9 +124,9 @@ import (
|
|||
// Hash contains contract hash.
|
||||
var Hash = {{ .Hash }}
|
||||
{{end -}}
|
||||
{{- range $name, $typ := .NamedTypes }}
|
||||
// {{toTypeName $name}} is a contract-specific {{$name}} type used by its methods.
|
||||
type {{toTypeName $name}} struct {
|
||||
{{- range $index, $typ := .NamedTypes }}
|
||||
// {{toTypeName $typ.Name}} is a contract-specific {{$typ.Name}} type used by its methods.
|
||||
type {{toTypeName $typ.Name}} struct {
|
||||
{{- range $m := $typ.Fields}}
|
||||
{{ upperFirst .Field}} {{etTypeToStr .ExtendedType}}
|
||||
{{- end}}
|
||||
|
@ -236,20 +236,20 @@ func New(actor Actor{{- if not (len .Hash) -}}, hash util.Uint160{{- end -}}) *C
|
|||
{{end -}}
|
||||
{{- range $m := .SafeMethods }}{{template "SAFEMETHOD" $m }}{{ end -}}
|
||||
{{- range $m := .Methods -}}{{template "METHOD" $m }}{{ end -}}
|
||||
{{- range $name, $typ := .NamedTypes }}
|
||||
// itemTo{{toTypeName $name}} converts stack item into *{{toTypeName $name}}.
|
||||
func itemTo{{toTypeName $name}}(item stackitem.Item, err error) (*{{toTypeName $name}}, error) {
|
||||
{{- range $index, $typ := .NamedTypes }}
|
||||
// itemTo{{toTypeName $typ.Name}} converts stack item into *{{toTypeName $typ.Name}}.
|
||||
func itemTo{{toTypeName $typ.Name}}(item stackitem.Item, err error) (*{{toTypeName $typ.Name}}, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res = new({{toTypeName $name}})
|
||||
var res = new({{toTypeName $typ.Name}})
|
||||
err = res.FromStackItem(item)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// FromStackItem retrieves fields of {{toTypeName $name}} from the given
|
||||
// FromStackItem retrieves fields of {{toTypeName $typ.Name}} from the given
|
||||
// [stackitem.Item] or returns an error if it's not possible to do to so.
|
||||
func (res *{{toTypeName $name}}) FromStackItem(item stackitem.Item) error {
|
||||
func (res *{{toTypeName $typ.Name}}) FromStackItem(item stackitem.Item) error {
|
||||
arr, ok := item.Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
return errors.New("not an array")
|
||||
|
@ -341,7 +341,7 @@ type (
|
|||
|
||||
SafeMethods []SafeMethodTmpl
|
||||
CustomEvents []CustomEventTemplate
|
||||
NamedTypes map[string]binding.ExtendedType
|
||||
NamedTypes []binding.ExtendedType
|
||||
|
||||
IsNep11D bool
|
||||
IsNep11ND bool
|
||||
|
@ -430,7 +430,13 @@ func Generate(cfg binding.Config) error {
|
|||
|
||||
ctr.ContractTmpl = binding.TemplateFromManifest(cfg, scTypeToGo)
|
||||
ctr = scTemplateToRPC(cfg, ctr, imports, scTypeToGo)
|
||||
ctr.NamedTypes = cfg.NamedTypes
|
||||
ctr.NamedTypes = make([]binding.ExtendedType, 0, len(cfg.NamedTypes))
|
||||
for k := range cfg.NamedTypes {
|
||||
ctr.NamedTypes = append(ctr.NamedTypes, cfg.NamedTypes[k])
|
||||
}
|
||||
sort.Slice(ctr.NamedTypes, func(i, j int) bool {
|
||||
return strings.Compare(ctr.NamedTypes[i].Name, ctr.NamedTypes[j].Name) < 0
|
||||
})
|
||||
|
||||
// Check resulting named types and events don't have duplicating field names.
|
||||
for _, t := range ctr.NamedTypes {
|
||||
|
@ -458,7 +464,7 @@ func Generate(cfg binding.Config) error {
|
|||
"addIndent": addIndent,
|
||||
"etTypeConverter": etTypeConverter,
|
||||
"etTypeToStr": func(et binding.ExtendedType) string {
|
||||
r, _ := extendedTypeToGo(et, ctr.NamedTypes)
|
||||
r, _ := extendedTypeToGo(et, cfg.NamedTypes)
|
||||
return r
|
||||
},
|
||||
"toTypeName": toTypeName,
|
||||
|
@ -719,7 +725,7 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st
|
|||
}
|
||||
}
|
||||
for _, et := range cfg.NamedTypes {
|
||||
addETImports(et, ctr.NamedTypes, imports)
|
||||
addETImports(et, cfg.NamedTypes, imports)
|
||||
}
|
||||
if len(cfg.NamedTypes) > 0 {
|
||||
imports["errors"] = struct{}{}
|
||||
|
@ -746,7 +752,7 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st
|
|||
extType = binding.ExtendedType{
|
||||
Base: abiEvent.Parameters[i].Type,
|
||||
}
|
||||
addETImports(extType, ctr.NamedTypes, imports)
|
||||
addETImports(extType, cfg.NamedTypes, imports)
|
||||
}
|
||||
eTmp.Parameters = append(eTmp.Parameters, EventParamTmpl{
|
||||
ParamTmpl: binding.ParamTmpl{
|
||||
|
@ -817,7 +823,7 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st
|
|||
case "keys.PublicKeys":
|
||||
ctr.SafeMethods[i].Unwrapper = "ArrayOfPublicKeys"
|
||||
default:
|
||||
addETImports(ctr.SafeMethods[i].ExtendedReturn, ctr.NamedTypes, imports)
|
||||
addETImports(ctr.SafeMethods[i].ExtendedReturn, cfg.NamedTypes, imports)
|
||||
ctr.SafeMethods[i].Unwrapper = "Item"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue