From 027e94fbdea2afae66aef7b16c27663724fddf94 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 14 Nov 2022 17:11:54 +0300 Subject: [PATCH 01/11] compiler: don't push overrides for unexported methods They're internal, no one cares about them externally. --- pkg/compiler/compiler.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 8ee039189..17e2f9ed7 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -287,6 +287,9 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { cfg := binding.NewConfig() cfg.Package = di.MainPkg for _, m := range di.Methods { + if !m.IsExported { + continue + } for _, p := range m.Parameters { if p.RealType.TypeName != "" { cfg.Overrides[m.Name.Name+"."+p.Name] = p.RealType From b9d20b32e9ec616d309d9cd4f8e538752a902413 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 1 Dec 2022 21:05:54 +0300 Subject: [PATCH 02/11] compiler: push additional type data into the bindings file Structures/arrays and maps. --- pkg/compiler/codegen.go | 2 +- pkg/compiler/compiler.go | 12 +- pkg/compiler/debug.go | 180 ++++++++++++++++++-------- pkg/compiler/inline.go | 2 +- pkg/smartcontract/binding/generate.go | 34 +++-- 5 files changed, 168 insertions(+), 62 deletions(-) diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index f21d421c4..b9faf3c5d 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -914,7 +914,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { c.convertMap(n) default: if tn, ok := t.(*types.Named); ok && isInteropPath(tn.String()) { - st, _, _ := scAndVMInteropTypeFromExpr(tn, false) + st, _, _, _ := scAndVMInteropTypeFromExpr(tn, false) expectedLen := -1 switch st { case smartcontract.Hash160Type: diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 17e2f9ed7..0b75255a0 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -291,13 +291,23 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { continue } for _, p := range m.Parameters { + pname := m.Name.Name + "." + p.Name if p.RealType.TypeName != "" { - cfg.Overrides[m.Name.Name+"."+p.Name] = p.RealType + cfg.Overrides[pname] = p.RealType + } + if p.ExtendedType != nil { + cfg.Types[pname] = *p.ExtendedType } } if m.ReturnTypeReal.TypeName != "" { cfg.Overrides[m.Name.Name] = m.ReturnTypeReal } + if m.ReturnTypeExtended != nil { + cfg.Types[m.Name.Name] = *m.ReturnTypeExtended + } + } + if len(di.NamedTypes) > 0 { + cfg.NamedTypes = di.NamedTypes } data, err := yaml.Marshal(&cfg) if err != nil { diff --git a/pkg/compiler/debug.go b/pkg/compiler/debug.go index 8102ba7dc..773724d40 100644 --- a/pkg/compiler/debug.go +++ b/pkg/compiler/debug.go @@ -26,7 +26,10 @@ type DebugInfo struct { Hash util.Uint160 `json:"hash"` Documents []string `json:"documents"` Methods []MethodDebugInfo `json:"methods"` - Events []EventDebugInfo `json:"events"` + // NamedTypes are exported structured types that have some name (even + // if the original structure doesn't) and a number of internal fields. + NamedTypes map[string]binding.ExtendedType `json:"-"` + Events []EventDebugInfo `json:"events"` // EmittedEvents contains events occurring in code. EmittedEvents map[string][][]string `json:"-"` // InvokedContracts contains foreign contract invocations. @@ -55,6 +58,8 @@ type MethodDebugInfo struct { ReturnType string `json:"return"` // ReturnTypeReal is the method's return type as specified in Go code. ReturnTypeReal binding.Override `json:"-"` + // ReturnTypeExtended is the method's return type with additional data. + ReturnTypeExtended *binding.ExtendedType `json:"-"` // ReturnTypeSC is a return type to use in manifest. ReturnTypeSC smartcontract.ParamType `json:"-"` Variables []string `json:"variables"` @@ -100,10 +105,11 @@ type DebugRange struct { // DebugParam represents the variables's name and type. type DebugParam struct { - Name string `json:"name"` - Type string `json:"type"` - RealType binding.Override `json:"-"` - TypeSC smartcontract.ParamType `json:"-"` + Name string `json:"name"` + Type string `json:"type"` + RealType binding.Override `json:"-"` + ExtendedType *binding.ExtendedType `json:"-"` + TypeSC smartcontract.ParamType `json:"-"` } func (c *codegen) saveSequencePoint(n ast.Node) { @@ -185,8 +191,9 @@ func (c *codegen) emitDebugInfo(contract []byte) *DebugInfo { } start := len(d.Methods) + d.NamedTypes = make(map[string]binding.ExtendedType) for name, scope := range c.funcs { - m := c.methodInfoFromScope(name, scope) + m := c.methodInfoFromScope(name, scope, d.NamedTypes) if m.Range.Start == m.Range.End { continue } @@ -201,7 +208,7 @@ func (c *codegen) emitDebugInfo(contract []byte) *DebugInfo { } func (c *codegen) registerDebugVariable(name string, expr ast.Expr) { - _, vt, _ := c.scAndVMTypeFromExpr(expr) + _, vt, _, _ := c.scAndVMTypeFromExpr(expr, nil) if c.scope == nil { c.staticVariables = append(c.staticVariables, name+","+vt.String()) return @@ -209,24 +216,25 @@ func (c *codegen) registerDebugVariable(name string, expr ast.Expr) { c.scope.variables = append(c.scope.variables, name+","+vt.String()) } -func (c *codegen) methodInfoFromScope(name string, scope *funcScope) *MethodDebugInfo { +func (c *codegen) methodInfoFromScope(name string, scope *funcScope, exts map[string]binding.ExtendedType) *MethodDebugInfo { ps := scope.decl.Type.Params params := make([]DebugParam, 0, ps.NumFields()) for i := range ps.List { for j := range ps.List[i].Names { - st, vt, rt := c.scAndVMTypeFromExpr(ps.List[i].Type) + st, vt, rt, et := c.scAndVMTypeFromExpr(ps.List[i].Type, exts) params = append(params, DebugParam{ - Name: ps.List[i].Names[j].Name, - Type: vt.String(), - RealType: rt, - TypeSC: st, + Name: ps.List[i].Names[j].Name, + Type: vt.String(), + ExtendedType: et, + RealType: rt, + TypeSC: st, }) } } ss := strings.Split(name, ".") name = ss[len(ss)-1] r, n := utf8.DecodeRuneInString(name) - st, vt, rt := c.scAndVMReturnTypeFromScope(scope) + st, vt, rt, et := c.scAndVMReturnTypeFromScope(scope, exts) return &MethodDebugInfo{ ID: name, @@ -234,45 +242,48 @@ func (c *codegen) methodInfoFromScope(name string, scope *funcScope) *MethodDebu Name: string(unicode.ToLower(r)) + name[n:], Namespace: scope.pkg.Name(), }, - IsExported: scope.decl.Name.IsExported(), - IsFunction: scope.decl.Recv == nil, - Range: scope.rng, - Parameters: params, - ReturnType: vt, - ReturnTypeReal: rt, - ReturnTypeSC: st, - SeqPoints: c.sequencePoints[name], - Variables: scope.variables, + IsExported: scope.decl.Name.IsExported(), + IsFunction: scope.decl.Recv == nil, + Range: scope.rng, + Parameters: params, + ReturnType: vt, + ReturnTypeExtended: et, + ReturnTypeReal: rt, + ReturnTypeSC: st, + SeqPoints: c.sequencePoints[name], + Variables: scope.variables, } } -func (c *codegen) scAndVMReturnTypeFromScope(scope *funcScope) (smartcontract.ParamType, string, binding.Override) { +func (c *codegen) scAndVMReturnTypeFromScope(scope *funcScope, exts map[string]binding.ExtendedType) (smartcontract.ParamType, string, binding.Override, *binding.ExtendedType) { results := scope.decl.Type.Results switch results.NumFields() { case 0: - return smartcontract.VoidType, "Void", binding.Override{} + return smartcontract.VoidType, "Void", binding.Override{}, nil case 1: - st, vt, s := c.scAndVMTypeFromExpr(results.List[0].Type) - return st, vt.String(), s + st, vt, s, et := c.scAndVMTypeFromExpr(results.List[0].Type, exts) + return st, vt.String(), s, et default: // multiple return values are not supported in debugger - return smartcontract.AnyType, "Any", binding.Override{} + return smartcontract.AnyType, "Any", binding.Override{}, nil } } -func scAndVMInteropTypeFromExpr(named *types.Named, isPointer bool) (smartcontract.ParamType, stackitem.Type, binding.Override) { +func scAndVMInteropTypeFromExpr(named *types.Named, isPointer bool) (smartcontract.ParamType, stackitem.Type, binding.Override, *binding.ExtendedType) { name := named.Obj().Name() pkg := named.Obj().Pkg().Name() switch pkg { case "ledger", "contract": + // Block, Transaction, Contract. typeName := pkg + "." + name + et := &binding.ExtendedType{Base: smartcontract.ArrayType, Name: typeName} if isPointer { typeName = "*" + typeName } return smartcontract.ArrayType, stackitem.ArrayT, binding.Override{ Package: named.Obj().Pkg().Path(), TypeName: typeName, - } // Block, Transaction, Contract + }, et case "interop": if name != "Interface" { over := binding.Override{ @@ -281,26 +292,29 @@ func scAndVMInteropTypeFromExpr(named *types.Named, isPointer bool) (smartcontra } switch name { case "Hash160": - return smartcontract.Hash160Type, stackitem.ByteArrayT, over + return smartcontract.Hash160Type, stackitem.ByteArrayT, over, nil case "Hash256": - return smartcontract.Hash256Type, stackitem.ByteArrayT, over + return smartcontract.Hash256Type, stackitem.ByteArrayT, over, nil case "PublicKey": - return smartcontract.PublicKeyType, stackitem.ByteArrayT, over + return smartcontract.PublicKeyType, stackitem.ByteArrayT, over, nil case "Signature": - return smartcontract.SignatureType, stackitem.ByteArrayT, over + return smartcontract.SignatureType, stackitem.ByteArrayT, over, nil } } } - return smartcontract.InteropInterfaceType, stackitem.InteropT, binding.Override{TypeName: "interface{}"} + return smartcontract.InteropInterfaceType, + stackitem.InteropT, + binding.Override{TypeName: "interface{}"}, + &binding.ExtendedType{Base: smartcontract.InteropInterfaceType, Interface: "iterator"} // Temporarily all interops are iterators. } -func (c *codegen) scAndVMTypeFromExpr(typ ast.Expr) (smartcontract.ParamType, stackitem.Type, binding.Override) { - return c.scAndVMTypeFromType(c.typeOf(typ)) +func (c *codegen) scAndVMTypeFromExpr(typ ast.Expr, exts map[string]binding.ExtendedType) (smartcontract.ParamType, stackitem.Type, binding.Override, *binding.ExtendedType) { + return c.scAndVMTypeFromType(c.typeOf(typ), exts) } -func (c *codegen) scAndVMTypeFromType(t types.Type) (smartcontract.ParamType, stackitem.Type, binding.Override) { +func (c *codegen) scAndVMTypeFromType(t types.Type, exts map[string]binding.ExtendedType) (smartcontract.ParamType, stackitem.Type, binding.Override, *binding.ExtendedType) { if t == nil { - return smartcontract.AnyType, stackitem.AnyT, binding.Override{TypeName: "interface{}"} + return smartcontract.AnyType, stackitem.AnyT, binding.Override{TypeName: "interface{}"}, nil } var isPtr bool @@ -314,7 +328,11 @@ func (c *codegen) scAndVMTypeFromType(t types.Type) (smartcontract.ParamType, st } if isNamed { if isInteropPath(named.String()) { - return scAndVMInteropTypeFromExpr(named, isPtr) + st, vt, over, et := scAndVMInteropTypeFromExpr(named, isPtr) + if et != nil && et.Base == smartcontract.ArrayType && exts != nil && exts[et.Name].Name != et.Name { + _ = c.genStructExtended(named.Underlying().(*types.Struct), et.Name, exts) + } + return st, vt, over, et } } @@ -325,43 +343,103 @@ func (c *codegen) scAndVMTypeFromType(t types.Type) (smartcontract.ParamType, st switch { case info&types.IsInteger != 0: over.TypeName = "int" - return smartcontract.IntegerType, stackitem.IntegerT, over + return smartcontract.IntegerType, stackitem.IntegerT, over, nil case info&types.IsBoolean != 0: over.TypeName = "bool" - return smartcontract.BoolType, stackitem.BooleanT, over + return smartcontract.BoolType, stackitem.BooleanT, over, nil case info&types.IsString != 0: over.TypeName = "string" - return smartcontract.StringType, stackitem.ByteArrayT, over + return smartcontract.StringType, stackitem.ByteArrayT, over, nil default: over.TypeName = "interface{}" - return smartcontract.AnyType, stackitem.AnyT, over + return smartcontract.AnyType, stackitem.AnyT, over, nil } case *types.Map: - _, _, over := c.scAndVMTypeFromType(t.Elem()) + et := &binding.ExtendedType{ + Base: smartcontract.MapType, + } + et.Key, _, _, _ = c.scAndVMTypeFromType(t.Key(), exts) + vt, _, over, vet := c.scAndVMTypeFromType(t.Elem(), exts) + et.Value = vet + if et.Value == nil { + et.Value = &binding.ExtendedType{Base: vt} + } over.TypeName = "map[" + t.Key().String() + "]" + over.TypeName - return smartcontract.MapType, stackitem.MapT, over + return smartcontract.MapType, stackitem.MapT, over, et case *types.Struct: if isNamed { over.Package = named.Obj().Pkg().Path() over.TypeName = named.Obj().Pkg().Name() + "." + named.Obj().Name() + _ = c.genStructExtended(t, over.TypeName, exts) + } else { + name := "unnamed" + if exts != nil { + for exts[name].Name == name { + name = name + "X" + } + _ = c.genStructExtended(t, name, exts) + } } - return smartcontract.ArrayType, stackitem.StructT, over + return smartcontract.ArrayType, stackitem.StructT, over, + &binding.ExtendedType{ // Value-less, refer to exts. + Base: smartcontract.ArrayType, + Name: over.TypeName, + } + case *types.Slice: if isByte(t.Elem()) { over.TypeName = "[]byte" - return smartcontract.ByteArrayType, stackitem.ByteArrayT, over + return smartcontract.ByteArrayType, stackitem.ByteArrayT, over, nil + } + et := &binding.ExtendedType{ + Base: smartcontract.ArrayType, + } + vt, _, over, vet := c.scAndVMTypeFromType(t.Elem(), exts) + et.Value = vet + if et.Value == nil { + et.Value = &binding.ExtendedType{ + Base: vt, + } } - _, _, over := c.scAndVMTypeFromType(t.Elem()) if over.TypeName != "" { over.TypeName = "[]" + over.TypeName } - return smartcontract.ArrayType, stackitem.ArrayT, over + return smartcontract.ArrayType, stackitem.ArrayT, over, et default: over.TypeName = "interface{}" - return smartcontract.AnyType, stackitem.AnyT, over + return smartcontract.AnyType, stackitem.AnyT, over, nil } } +func (c *codegen) genStructExtended(t *types.Struct, name string, exts map[string]binding.ExtendedType) *binding.ExtendedType { + var et *binding.ExtendedType + if exts != nil { + if exts[name].Name != name { + et = &binding.ExtendedType{ + Base: smartcontract.ArrayType, + Name: name, + Fields: make([]binding.FieldExtendedType, t.NumFields()), + } + exts[name] = *et // Prefill to solve recursive structures. + for i := range et.Fields { + field := t.Field(i) + ft, _, _, fet := c.scAndVMTypeFromType(field.Type(), exts) + if fet == nil { + et.Fields[i].ExtendedType.Base = ft + } else { + et.Fields[i].ExtendedType = *fet + } + et.Fields[i].Field = field.Name() + } + exts[name] = *et // Set real structure data. + } else { + et = new(binding.ExtendedType) + *et = exts[name] + } + } + return et +} + // MarshalJSON implements the json.Marshaler interface. func (d *DebugRange) MarshalJSON() ([]byte, error) { return []byte(`"` + strconv.FormatUint(uint64(d.Start), 10) + `-` + diff --git a/pkg/compiler/inline.go b/pkg/compiler/inline.go index 0f82d95ba..1653d8638 100644 --- a/pkg/compiler/inline.go +++ b/pkg/compiler/inline.go @@ -175,7 +175,7 @@ func (c *codegen) processNotify(f *funcScope, args []ast.Expr, hasEllipsis bool) params := make([]string, 0, len(args[1:])) vParams := make([]*stackitem.Type, 0, len(args[1:])) for _, p := range args[1:] { - st, vt, _ := c.scAndVMTypeFromExpr(p) + st, vt, _, _ := c.scAndVMTypeFromExpr(p, nil) params = append(params, st.String()) vParams = append(vParams, &vt) } diff --git a/pkg/smartcontract/binding/generate.go b/pkg/smartcontract/binding/generate.go index 97b1fdbfb..48ba4b4ec 100644 --- a/pkg/smartcontract/binding/generate.go +++ b/pkg/smartcontract/binding/generate.go @@ -48,12 +48,28 @@ const Hash = "{{ .Hash }}" type ( // Config contains parameter for the generated binding. Config struct { - Package string `yaml:"package,omitempty"` - Manifest *manifest.Manifest `yaml:"-"` - Hash util.Uint160 `yaml:"hash,omitempty"` - Overrides map[string]Override `yaml:"overrides,omitempty"` - CallFlags map[string]callflag.CallFlag `yaml:"callflags,omitempty"` - Output io.Writer `yaml:"-"` + Package string `yaml:"package,omitempty"` + Manifest *manifest.Manifest `yaml:"-"` + Hash util.Uint160 `yaml:"hash,omitempty"` + Overrides map[string]Override `yaml:"overrides,omitempty"` + CallFlags map[string]callflag.CallFlag `yaml:"callflags,omitempty"` + NamedTypes map[string]ExtendedType `yaml:"namedtypes,omitempty"` + Types map[string]ExtendedType `yaml:"types,omitempty"` + Output io.Writer `yaml:"-"` + } + + ExtendedType struct { + Base smartcontract.ParamType `yaml:"base"` + Name string `yaml:"name,omitempty"` // Structure name, omitted for arrays, interfaces and maps. + Interface string `yaml:"interface,omitempty"` // Interface type name, "iterator" only for now. + Key smartcontract.ParamType `yaml:"key,omitempty"` // Key type (only simple types can be used for keys) for maps. + Value *ExtendedType `yaml:"value,omitempty"` // Value type for iterators and arrays. + Fields []FieldExtendedType `yaml:"fields,omitempty"` // Ordered type data for structure fields. + } + + FieldExtendedType struct { + Field string `yaml:"field"` + ExtendedType `yaml:",inline"` } ContractTmpl struct { @@ -84,8 +100,10 @@ var srcTemplate = template.Must(template.New("generate").Parse(srcTmpl)) // NewConfig initializes and returns a new config instance. func NewConfig() Config { return Config{ - Overrides: make(map[string]Override), - CallFlags: make(map[string]callflag.CallFlag), + Overrides: make(map[string]Override), + CallFlags: make(map[string]callflag.CallFlag), + NamedTypes: make(map[string]ExtendedType), + Types: make(map[string]ExtendedType), } } From 0f61a13006232c45129794d5b4d855983f8a1971 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 25 Nov 2022 10:35:13 +0300 Subject: [PATCH 03/11] compiler: Contract type is in the management package --- pkg/compiler/debug.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/compiler/debug.go b/pkg/compiler/debug.go index 773724d40..dc712697d 100644 --- a/pkg/compiler/debug.go +++ b/pkg/compiler/debug.go @@ -273,7 +273,7 @@ func scAndVMInteropTypeFromExpr(named *types.Named, isPointer bool) (smartcontra name := named.Obj().Name() pkg := named.Obj().Pkg().Name() switch pkg { - case "ledger", "contract": + case "ledger", "management": // Block, Transaction, Contract. typeName := pkg + "." + name et := &binding.ExtendedType{Base: smartcontract.ArrayType, Name: typeName} From d7fafea328df511f7686e0562211ab93fed55d35 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 25 Nov 2022 13:00:43 +0300 Subject: [PATCH 04/11] compiler: handle ledger/management enums better for debug Fix scAndVMInteropTypeFromExpr(), these types are not structures at all. --- pkg/compiler/debug.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/compiler/debug.go b/pkg/compiler/debug.go index dc712697d..efad1efc2 100644 --- a/pkg/compiler/debug.go +++ b/pkg/compiler/debug.go @@ -274,6 +274,10 @@ func scAndVMInteropTypeFromExpr(named *types.Named, isPointer bool) (smartcontra pkg := named.Obj().Pkg().Name() switch pkg { case "ledger", "management": + switch name { + case "ParameterType", "SignerScope", "WitnessAction", "WitnessConditionType", "VMState": + return smartcontract.IntegerType, stackitem.IntegerT, binding.Override{TypeName: "int"}, nil + } // Block, Transaction, Contract. typeName := pkg + "." + name et := &binding.ExtendedType{Base: smartcontract.ArrayType, Name: typeName} From 2bcf3a4ad5c007e03beb6dcba5ce75a09100e44f Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 25 Nov 2022 13:01:57 +0300 Subject: [PATCH 05/11] compiler: unwrap pointers for debug types We have almost no difference between Type and *Type. --- pkg/compiler/debug.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/compiler/debug.go b/pkg/compiler/debug.go index efad1efc2..9785de401 100644 --- a/pkg/compiler/debug.go +++ b/pkg/compiler/debug.go @@ -339,7 +339,9 @@ func (c *codegen) scAndVMTypeFromType(t types.Type, exts map[string]binding.Exte return st, vt, over, et } } - + if ptr, isPtr := t.(*types.Pointer); isPtr { + t = ptr.Elem() + } var over binding.Override switch t := t.Underlying().(type) { case *types.Basic: From 0214628127c2bc354860ace7c764331ead9ead0e Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 25 Nov 2022 13:04:24 +0300 Subject: [PATCH 06/11] rpcbinding: generate bindings using new extended type data --- cli/smartcontract/generate_test.go | 1 + cli/smartcontract/testdata/structs/config.yml | 3 + .../testdata/structs/rpcbindings.out | 1423 +++++++++++++++++ cli/smartcontract/testdata/structs/structs.go | 39 + docs/compiler.md | 5 +- pkg/smartcontract/binding/generate.go | 10 +- pkg/smartcontract/rpcbinding/binding.go | 244 ++- 7 files changed, 1687 insertions(+), 38 deletions(-) create mode 100644 cli/smartcontract/testdata/structs/config.yml create mode 100644 cli/smartcontract/testdata/structs/rpcbindings.out create mode 100644 cli/smartcontract/testdata/structs/structs.go diff --git a/cli/smartcontract/generate_test.go b/cli/smartcontract/generate_test.go index ed0e32157..47328dbcc 100644 --- a/cli/smartcontract/generate_test.go +++ b/cli/smartcontract/generate_test.go @@ -401,6 +401,7 @@ func TestAssistedRPCBindings(t *testing.T) { } checkBinding(filepath.Join("testdata", "types")) + checkBinding(filepath.Join("testdata", "structs")) } func TestGenerate_Errors(t *testing.T) { diff --git a/cli/smartcontract/testdata/structs/config.yml b/cli/smartcontract/testdata/structs/config.yml new file mode 100644 index 000000000..04192ba7b --- /dev/null +++ b/cli/smartcontract/testdata/structs/config.yml @@ -0,0 +1,3 @@ +name: "Types" +sourceurl: https://github.com/nspcc-dev/neo-go/ +safemethods: ["contract", "block", "transaction", "struct"] diff --git a/cli/smartcontract/testdata/structs/rpcbindings.out b/cli/smartcontract/testdata/structs/rpcbindings.out new file mode 100644 index 000000000..aa1bf2e77 --- /dev/null +++ b/cli/smartcontract/testdata/structs/rpcbindings.out @@ -0,0 +1,1423 @@ +// Package structs contains RPC wrappers for Types contract. +package structs + +import ( + "crypto/elliptic" + "errors" + "fmt" + "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" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "math/big" + "unicode/utf8" +) + +// 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} + + +// LedgerBlock is a contract-specific ledger.Block type used by its methods. +type LedgerBlock struct { + Hash util.Uint256 + Version *big.Int + PrevHash util.Uint256 + MerkleRoot util.Uint256 + Timestamp *big.Int + Nonce *big.Int + Index *big.Int + NextConsensus util.Uint160 + TransactionsLength *big.Int +} + +// LedgerBlockSR is a contract-specific ledger.BlockSR type used by its methods. +type LedgerBlockSR struct { + Hash util.Uint256 + Version *big.Int + PrevHash util.Uint256 + MerkleRoot util.Uint256 + Timestamp *big.Int + Nonce *big.Int + Index *big.Int + NextConsensus util.Uint160 + TransactionsLength *big.Int + PrevStateRoot util.Uint256 +} + +// LedgerTransaction is a contract-specific ledger.Transaction type used by its methods. +type LedgerTransaction struct { + Hash util.Uint256 + Version *big.Int + Nonce *big.Int + Sender util.Uint160 + SysFee *big.Int + NetFee *big.Int + ValidUntilBlock *big.Int + Script []byte +} + +// LedgerTransactionSigner is a contract-specific ledger.TransactionSigner type used by its methods. +type LedgerTransactionSigner struct { + Account util.Uint160 + Scopes *big.Int + AllowedContracts []util.Uint160 + AllowedGroups keys.PublicKeys + Rules []*LedgerWitnessRule +} + +// LedgerWitnessCondition is a contract-specific ledger.WitnessCondition type used by its methods. +type LedgerWitnessCondition struct { + Type *big.Int + Value interface{} +} + +// LedgerWitnessRule is a contract-specific ledger.WitnessRule type used by its methods. +type LedgerWitnessRule struct { + Action *big.Int + Condition *LedgerWitnessCondition +} + +// ManagementABI is a contract-specific management.ABI type used by its methods. +type ManagementABI struct { + Methods []*ManagementMethod + Events []*ManagementEvent +} + +// ManagementContract is a contract-specific management.Contract type used by its methods. +type ManagementContract struct { + ID *big.Int + UpdateCounter *big.Int + Hash util.Uint160 + NEF []byte + Manifest *ManagementManifest +} + +// ManagementEvent is a contract-specific management.Event type used by its methods. +type ManagementEvent struct { + Name string + Params []*ManagementParameter +} + +// ManagementGroup is a contract-specific management.Group type used by its methods. +type ManagementGroup struct { + PublicKey *keys.PublicKey + Signature []byte +} + +// ManagementManifest is a contract-specific management.Manifest type used by its methods. +type ManagementManifest struct { + Name string + Groups []*ManagementGroup + Features *stackitem.Map + SupportedStandards []string + ABI *ManagementABI + Permissions []*ManagementPermission + Trusts []util.Uint160 + Extra interface{} +} + +// ManagementMethod is a contract-specific management.Method type used by its methods. +type ManagementMethod struct { + Name string + Params []*ManagementParameter + ReturnType *big.Int + Offset *big.Int + Safe bool +} + +// ManagementParameter is a contract-specific management.Parameter type used by its methods. +type ManagementParameter struct { + Name string + Type *big.Int +} + +// ManagementPermission is a contract-specific management.Permission type used by its methods. +type ManagementPermission struct { + Contract util.Uint160 + Methods []string +} + +// StructsInternal is a contract-specific structs.Internal type used by its methods. +type StructsInternal struct { + Bool bool + Int *big.Int + Bytes []byte + String string + H160 util.Uint160 + H256 util.Uint256 + PK *keys.PublicKey + PubKey *keys.PublicKey + Sign []byte + ArrOfBytes [][]byte + ArrOfH160 []util.Uint160 + Map *stackitem.Map + Struct *StructsInternal +} +// 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} +} + + +// Block invokes `block` method of contract. +func (c *ContractReader) Block(b *LedgerBlock) (*LedgerBlock, error) { + return itemToLedgerBlock(unwrap.Item(c.invoker.Call(Hash, "block", b))) +} + +// Contract invokes `contract` method of contract. +func (c *ContractReader) Contract(mc *ManagementContract) (*ManagementContract, error) { + return itemToManagementContract(unwrap.Item(c.invoker.Call(Hash, "contract", mc))) +} + +// Struct invokes `struct` method of contract. +func (c *ContractReader) Struct(s *StructsInternal) (*StructsInternal, error) { + return itemToStructsInternal(unwrap.Item(c.invoker.Call(Hash, "struct", s))) +} + +// Transaction invokes `transaction` method of contract. +func (c *ContractReader) Transaction(t *LedgerTransaction) (*LedgerTransaction, error) { + return itemToLedgerTransaction(unwrap.Item(c.invoker.Call(Hash, "transaction", t))) +} + +// itemToLedgerBlock converts stack item into *LedgerBlock. +func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != 9 { + return nil, errors.New("wrong number of structure elements") + } + + var res = new(LedgerBlock) + var index = -1 + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.PrevHash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.MerkleRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.NextConsensus, err = func (item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + + return res, err +} + +// itemToLedgerBlockSR converts stack item into *LedgerBlockSR. +func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != 10 { + return nil, errors.New("wrong number of structure elements") + } + + var res = new(LedgerBlockSR) + var index = -1 + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.PrevHash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.MerkleRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.NextConsensus, err = func (item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.PrevStateRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + + return res, err +} + +// itemToLedgerTransaction converts stack item into *LedgerTransaction. +func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction, error) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != 8 { + return nil, errors.New("wrong number of structure elements") + } + + var res = new(LedgerTransaction) + var index = -1 + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.Sender, err = func (item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.SysFee, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.NetFee, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.ValidUntilBlock, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.Script, err = arr[index].TryBytes() + if err != nil { + return nil, err + } + + + return res, err +} + +// itemToLedgerTransactionSigner converts stack item into *LedgerTransactionSigner. +func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTransactionSigner, error) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != 5 { + return nil, errors.New("wrong number of structure elements") + } + + var res = new(LedgerTransactionSigner) + var index = -1 + index++ + res.Account, err = func (item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Scopes, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.AllowedContracts, err = func (item stackitem.Item) ([]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]util.Uint160, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.AllowedGroups, err = func (item stackitem.Item) (keys.PublicKeys, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make(keys.PublicKeys, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Rules, err = func (item stackitem.Item) ([]*LedgerWitnessRule, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*LedgerWitnessRule, len(arr)) + for i := range res { + res[i], err = itemToLedgerWitnessRule(arr[i], nil) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[index]) + if err != nil { + return nil, err + } + + + return res, err +} + +// itemToLedgerWitnessCondition converts stack item into *LedgerWitnessCondition. +func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnessCondition, error) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != 2 { + return nil, errors.New("wrong number of structure elements") + } + + var res = new(LedgerWitnessCondition) + var index = -1 + index++ + res.Type, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.Value, err = arr[index].Value(), nil + if err != nil { + return nil, err + } + + + return res, err +} + +// itemToLedgerWitnessRule converts stack item into *LedgerWitnessRule. +func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule, error) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != 2 { + return nil, errors.New("wrong number of structure elements") + } + + var res = new(LedgerWitnessRule) + var index = -1 + index++ + res.Action, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.Condition, err = itemToLedgerWitnessCondition(arr[index], nil) + if err != nil { + return nil, err + } + + + return res, err +} + +// itemToManagementABI converts stack item into *ManagementABI. +func itemToManagementABI(item stackitem.Item, err error) (*ManagementABI, error) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != 2 { + return nil, errors.New("wrong number of structure elements") + } + + var res = new(ManagementABI) + var index = -1 + index++ + res.Methods, err = func (item stackitem.Item) ([]*ManagementMethod, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementMethod, len(arr)) + for i := range res { + res[i], err = itemToManagementMethod(arr[i], nil) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Events, err = func (item stackitem.Item) ([]*ManagementEvent, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementEvent, len(arr)) + for i := range res { + res[i], err = itemToManagementEvent(arr[i], nil) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[index]) + if err != nil { + return nil, err + } + + + return res, err +} + +// itemToManagementContract converts stack item into *ManagementContract. +func itemToManagementContract(item stackitem.Item, err error) (*ManagementContract, error) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != 5 { + return nil, errors.New("wrong number of structure elements") + } + + var res = new(ManagementContract) + var index = -1 + index++ + res.ID, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.UpdateCounter, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.NEF, err = arr[index].TryBytes() + if err != nil { + return nil, err + } + + index++ + res.Manifest, err = itemToManagementManifest(arr[index], nil) + if err != nil { + return nil, err + } + + + return res, err +} + +// itemToManagementEvent converts stack item into *ManagementEvent. +func itemToManagementEvent(item stackitem.Item, err error) (*ManagementEvent, error) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != 2 { + return nil, errors.New("wrong number of structure elements") + } + + var res = new(ManagementEvent) + var index = -1 + index++ + res.Name, err = func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Params, err = func (item stackitem.Item) ([]*ManagementParameter, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementParameter, len(arr)) + for i := range res { + res[i], err = itemToManagementParameter(arr[i], nil) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[index]) + if err != nil { + return nil, err + } + + + return res, err +} + +// itemToManagementGroup converts stack item into *ManagementGroup. +func itemToManagementGroup(item stackitem.Item, err error) (*ManagementGroup, error) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != 2 { + return nil, errors.New("wrong number of structure elements") + } + + var res = new(ManagementGroup) + var index = -1 + index++ + res.PublicKey, err = func (item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Signature, err = arr[index].TryBytes() + if err != nil { + return nil, err + } + + + return res, err +} + +// itemToManagementManifest converts stack item into *ManagementManifest. +func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManifest, error) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != 8 { + return nil, errors.New("wrong number of structure elements") + } + + var res = new(ManagementManifest) + var index = -1 + index++ + res.Name, err = func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Groups, err = func (item stackitem.Item) ([]*ManagementGroup, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementGroup, len(arr)) + for i := range res { + res[i], err = itemToManagementGroup(arr[i], nil) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Features, err = func (item stackitem.Item) (*stackitem.Map, error) { + if t := item.Type(); t != stackitem.MapT { + return nil, fmt.Errorf("%s is not a map", t.String()) + } + return item.(*stackitem.Map), nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.SupportedStandards, err = func (item stackitem.Item) ([]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]string, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.ABI, err = itemToManagementABI(arr[index], nil) + if err != nil { + return nil, err + } + + index++ + res.Permissions, err = func (item stackitem.Item) ([]*ManagementPermission, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementPermission, len(arr)) + for i := range res { + res[i], err = itemToManagementPermission(arr[i], nil) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Trusts, err = func (item stackitem.Item) ([]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]util.Uint160, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Extra, err = arr[index].Value(), nil + if err != nil { + return nil, err + } + + + return res, err +} + +// itemToManagementMethod converts stack item into *ManagementMethod. +func itemToManagementMethod(item stackitem.Item, err error) (*ManagementMethod, error) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != 5 { + return nil, errors.New("wrong number of structure elements") + } + + var res = new(ManagementMethod) + var index = -1 + index++ + res.Name, err = func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Params, err = func (item stackitem.Item) ([]*ManagementParameter, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*ManagementParameter, len(arr)) + for i := range res { + res[i], err = itemToManagementParameter(arr[i], nil) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.ReturnType, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.Offset, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.Safe, err = arr[index].TryBool() + if err != nil { + return nil, err + } + + + return res, err +} + +// itemToManagementParameter converts stack item into *ManagementParameter. +func itemToManagementParameter(item stackitem.Item, err error) (*ManagementParameter, error) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != 2 { + return nil, errors.New("wrong number of structure elements") + } + + var res = new(ManagementParameter) + var index = -1 + index++ + res.Name, err = func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Type, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + + return res, err +} + +// itemToManagementPermission converts stack item into *ManagementPermission. +func itemToManagementPermission(item stackitem.Item, err error) (*ManagementPermission, error) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != 2 { + return nil, errors.New("wrong number of structure elements") + } + + var res = new(ManagementPermission) + var index = -1 + index++ + res.Contract, err = func (item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Methods, err = func (item stackitem.Item) ([]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]string, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[index]) + if err != nil { + return nil, err + } + + + return res, err +} + +// itemToStructsInternal converts stack item into *StructsInternal. +func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, error) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != 13 { + return nil, errors.New("wrong number of structure elements") + } + + var res = new(StructsInternal) + var index = -1 + index++ + res.Bool, err = arr[index].TryBool() + if err != nil { + return nil, err + } + + index++ + res.Int, err = arr[index].TryInteger() + if err != nil { + return nil, err + } + + index++ + res.Bytes, err = arr[index].TryBytes() + if err != nil { + return nil, err + } + + index++ + res.String, err = func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.H160, err = func (item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.H256, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.PK, err = func (item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.PubKey, err = func (item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Sign, err = arr[index].TryBytes() + if err != nil { + return nil, err + } + + index++ + res.ArrOfBytes, err = func (item stackitem.Item) ([][]byte, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([][]byte, len(arr)) + for i := range res { + res[i], err = arr[i].TryBytes() + if err != nil { + return nil, err + } + } + return res, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.ArrOfH160, err = func (item stackitem.Item) ([]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]util.Uint160, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Map, err = func (item stackitem.Item) (*stackitem.Map, error) { + if t := item.Type(); t != stackitem.MapT { + return nil, fmt.Errorf("%s is not a map", t.String()) + } + return item.(*stackitem.Map), nil + } (arr[index]) + if err != nil { + return nil, err + } + + index++ + res.Struct, err = itemToStructsInternal(arr[index], nil) + if err != nil { + return nil, err + } + + + return res, err +} diff --git a/cli/smartcontract/testdata/structs/structs.go b/cli/smartcontract/testdata/structs/structs.go new file mode 100644 index 000000000..5a54fb224 --- /dev/null +++ b/cli/smartcontract/testdata/structs/structs.go @@ -0,0 +1,39 @@ +package structs + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" + "github.com/nspcc-dev/neo-go/pkg/interop/native/management" +) + +type Internal struct { + Bool bool + Int int + Bytes []byte + String string + H160 interop.Hash160 + H256 interop.Hash256 + PK interop.PublicKey + PubKey interop.PublicKey + Sign interop.Signature + ArrOfBytes [][]byte + ArrOfH160 []interop.Hash160 + Map map[int][]interop.PublicKey + Struct *Internal +} + +func Contract(mc management.Contract) management.Contract { + return mc +} + +func Block(b *ledger.Block) *ledger.Block { + return b +} + +func Transaction(t *ledger.Transaction) *ledger.Transaction { + return t +} + +func Struct(s *Internal) *Internal { + return s +} diff --git a/docs/compiler.md b/docs/compiler.md index 3d7edd638..7675ba4a6 100644 --- a/docs/compiler.md +++ b/docs/compiler.md @@ -459,8 +459,9 @@ 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: +configuration file generated during compilation. This can cover arrays, maps +and structures. Notice that structured types returned by methods can't be Null +at the moment (see #2795). ``` $ ./bin/neo-go contract compile -i contract.go --config contract.yml -o contract.nef --manifest manifest.json --bindings contract.bindings.yml diff --git a/pkg/smartcontract/binding/generate.go b/pkg/smartcontract/binding/generate.go index 48ba4b4ec..62733b7ac 100644 --- a/pkg/smartcontract/binding/generate.go +++ b/pkg/smartcontract/binding/generate.go @@ -119,8 +119,8 @@ func Generate(cfg Config) error { return srcTemplate.Execute(cfg.Output, ctr) } -func scTypeToGo(name string, typ smartcontract.ParamType, overrides map[string]Override) (string, string) { - if over, ok := overrides[name]; ok { +func scTypeToGo(name string, typ smartcontract.ParamType, cfg *Config) (string, string) { + if over, ok := cfg.Overrides[name]; ok { return over.TypeName, over.Package } @@ -159,7 +159,7 @@ func scTypeToGo(name string, typ smartcontract.ParamType, overrides map[string]O // TemplateFromManifest create a contract template using the given configuration // and type conversion function. It assumes manifest to be present in the // configuration and assumes it to be correct (passing IsValid check). -func TemplateFromManifest(cfg Config, scTypeConverter func(string, smartcontract.ParamType, map[string]Override) (string, string)) ContractTmpl { +func TemplateFromManifest(cfg Config, scTypeConverter func(string, smartcontract.ParamType, *Config) (string, string)) ContractTmpl { hStr := "" for _, b := range cfg.Hash.BytesBE() { hStr += fmt.Sprintf("\\x%02x", b) @@ -221,7 +221,7 @@ func TemplateFromManifest(cfg Config, scTypeConverter func(string, smartcontract var varnames = make(map[string]bool) for i := range m.Parameters { name := m.Parameters[i].Name - typeStr, pkg := scTypeConverter(m.Name+"."+name, m.Parameters[i].Type, cfg.Overrides) + typeStr, pkg := scTypeConverter(m.Name+"."+name, m.Parameters[i].Type, &cfg) if pkg != "" { imports[pkg] = struct{}{} } @@ -238,7 +238,7 @@ func TemplateFromManifest(cfg Config, scTypeConverter func(string, smartcontract }) } - typeStr, pkg := scTypeConverter(m.Name, m.ReturnType, cfg.Overrides) + typeStr, pkg := scTypeConverter(m.Name, m.ReturnType, &cfg) if pkg != "" { imports[pkg] = struct{}{} } diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index 6c330cab3..1bfda8417 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -3,6 +3,7 @@ package rpcbinding import ( "fmt" "sort" + "strings" "text/template" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -18,8 +19,12 @@ func (c *ContractReader) {{.Name}}({{range $index, $arg := .Arguments -}} {{- if ne $index 0}}, {{end}} {{- .Name}} {{.Type}} {{- end}}) {{if .ReturnType }}({{ .ReturnType }}, error) { - return unwrap.{{.CallFlag}}(c.invoker.Call(Hash, "{{ .NameABI }}"{{/* CallFlag field is used for function name */}} - {{- range $arg := .Arguments -}}, {{.Name}}{{end}})) + return {{if .CallFlag -}} + unwrap.{{.CallFlag}}(c.invoker.Call(Hash, "{{ .NameABI }}"{{/* CallFlag field is used for function name */}} + {{- else -}} + itemTo{{ cutPointer .ReturnType }}(unwrap.Item(c.invoker.Call(Hash, "{{ .NameABI }}"{{/* CallFlag field is used for function name */}} + {{- end -}} + {{- range $arg := .Arguments -}}, {{.Name}}{{end}})){{if not .CallFlag}}){{end}} {{- else -}} (*result.Invoke, error) { c.invoker.Call(Hash, "{{ .NameABI }}" {{- range $arg := .Arguments -}}, {{.Name}}{{end}}) @@ -101,6 +106,14 @@ import ( // Hash contains contract hash. var Hash = {{ .Hash }} +{{range $name, $typ := .NamedTypes}} +// {{toTypeName $name}} is a contract-specific {{$name}} type used by its methods. +type {{toTypeName $name}} struct { +{{- range $m := $typ.Fields}} + {{.Field}} {{etTypeToStr .ExtendedType}} +{{- end}} +} +{{end -}} {{if .HasReader}}// Invoker is used by ContractReader to call various safe methods. type Invoker interface { {{if or .IsNep11D .IsNep11ND}} nep11.Invoker @@ -199,15 +212,41 @@ func New(actor Actor) *Contract { {{end}} {{- range $m := .Methods}} {{template "METHOD" $m }} -{{end}}` +{{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) { + if err != nil { + return nil, err + } + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + if len(arr) != {{len $typ.Fields}} { + return nil, errors.New("wrong number of structure elements") + } -var srcTemplate = template.Must(template.New("generate").Parse(srcTmpl)) + var res = new({{toTypeName $name}}) +{{if len .Fields}} var index = -1 +{{- range $m := $typ.Fields}} + index++ + res.{{.Field}}, err = {{etTypeConverter .ExtendedType "arr[index]"}} + if err != nil { + return nil, err + } +{{end}} +{{end}} + return res, err +} +{{end}}` type ( ContractTmpl struct { binding.ContractTmpl SafeMethods []binding.MethodTmpl + NamedTypes map[string]binding.ExtendedType IsNep11D bool IsNep11ND bool @@ -268,6 +307,17 @@ func Generate(cfg binding.Config) error { ctr.ContractTmpl = binding.TemplateFromManifest(cfg, scTypeToGo) ctr = scTemplateToRPC(cfg, ctr, imports) + ctr.NamedTypes = cfg.NamedTypes + + var srcTemplate = template.Must(template.New("generate").Funcs(template.FuncMap{ + "etTypeConverter": etTypeConverter, + "etTypeToStr": func(et binding.ExtendedType) string { + r, _ := extendedTypeToGo(et, ctr.NamedTypes) + return r + }, + "toTypeName": toTypeName, + "cutPointer": cutPointer, + }).Parse(srcTmpl)) return srcTemplate.Execute(cfg.Output, ctr) } @@ -295,31 +345,8 @@ func dropStdMethods(meths []manifest.Method, std *standard.Standard) []manifest. return meths } -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 { +func extendedTypeToGo(et binding.ExtendedType, named map[string]binding.ExtendedType) (string, string) { + switch et.Base { case smartcontract.AnyType: return "interface{}", "" case smartcontract.BoolType: @@ -339,16 +366,132 @@ func scTypeToGo(name string, typ smartcontract.ParamType, overrides map[string]b case smartcontract.SignatureType: return "[]byte", "" case smartcontract.ArrayType: + if len(et.Name) > 0 { + return "*" + toTypeName(et.Name), "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + } else if et.Value != nil { + if et.Value.Base == smartcontract.PublicKeyType { // Special array wrapper. + return "keys.PublicKeys", "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + } + sub, pkg := extendedTypeToGo(*et.Value, named) + return "[]" + sub, pkg + } return "[]interface{}", "" + case smartcontract.MapType: return "*stackitem.Map", "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" case smartcontract.InteropInterfaceType: return "interface{}", "" case smartcontract.VoidType: return "", "" - default: - panic("unreachable") } + panic("unreachable") +} + +func etTypeConverter(et binding.ExtendedType, v string) string { + switch et.Base { + case smartcontract.AnyType: + return v + ".Value(), nil" + case smartcontract.BoolType: + return v + ".TryBool()" + case smartcontract.IntegerType: + return v + ".TryInteger()" + case smartcontract.ByteArrayType, smartcontract.SignatureType: + return v + ".TryBytes()" + case smartcontract.StringType: + return `func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (` + v + `)` + case smartcontract.Hash160Type: + return `func (item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (` + v + `)` + case smartcontract.Hash256Type: + return `func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (` + v + `)` + case smartcontract.PublicKeyType: + return `func (item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + } (` + v + `)` + case smartcontract.ArrayType: + if len(et.Name) > 0 { + return "itemTo" + toTypeName(et.Name) + "(" + v + ", nil)" + } else if et.Value != nil { + at, _ := extendedTypeToGo(et, nil) + return `func (item stackitem.Item) (` + at + `, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make(` + at + `, len(arr)) + for i := range res { + res[i], err = ` + etTypeConverter(*et.Value, "arr[i]") + ` + if err != nil { + return nil, err + } + } + return res, nil + } (` + v + `)` + } + return etTypeConverter(binding.ExtendedType{ + Base: smartcontract.ArrayType, + Value: &binding.ExtendedType{ + Base: smartcontract.AnyType, + }, + }, v) + + case smartcontract.MapType: + return `func (item stackitem.Item) (*stackitem.Map, error) { + if t := item.Type(); t != stackitem.MapT { + return nil, fmt.Errorf("%s is not a map", t.String()) + } + return item.(*stackitem.Map), nil + } (` + v + `)` + case smartcontract.InteropInterfaceType: + return "item.Value(), nil" + case smartcontract.VoidType: + return "" + } + panic("unreachable") +} + +func scTypeToGo(name string, typ smartcontract.ParamType, cfg *binding.Config) (string, string) { + et, ok := cfg.Types[name] + if !ok { + et = binding.ExtendedType{Base: typ} + } + return extendedTypeToGo(et, cfg.NamedTypes) } func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]struct{}) ContractTmpl { @@ -369,6 +512,27 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st } } } + for _, et := range cfg.NamedTypes { + for _, fet := range et.Fields { + _, pkg := extendedTypeToGo(fet.ExtendedType, ctr.NamedTypes) + if pkg != "" { + imports[pkg] = struct{}{} + } + // Additional packages used during decoding. + switch fet.Base { + case smartcontract.StringType: + imports["unicode/utf8"] = struct{}{} + case smartcontract.PublicKeyType: + imports["crypto/elliptic"] = struct{}{} + case smartcontract.MapType: + imports["fmt"] = struct{}{} + } + } + } + if len(cfg.NamedTypes) > 0 { + imports["errors"] = struct{}{} + } + // We're misusing CallFlag field for function name here. for i := range ctr.SafeMethods { switch ctr.SafeMethods[i].ReturnType { @@ -420,6 +584,8 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st ctr.SafeMethods[i].CallFlag = "ArrayOfUint256" case "keys.PublicKeys": ctr.SafeMethods[i].CallFlag = "ArrayOfPublicKeys" + default: + ctr.SafeMethods[i].CallFlag = "" } } @@ -446,3 +612,19 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st sort.Strings(ctr.Imports) return ctr } + +func cutPointer(s string) string { + if s[0] == '*' { + return s[1:] + } + return s +} + +func toTypeName(s string) string { + return strings.Map(func(c rune) rune { + if c == '.' { + return -1 + } + return c + }, strings.ToUpper(s[0:1])+s[1:]) +} From 8e0be6e7a56add9fa90683929799f6c186bee378 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 1 Dec 2022 21:59:28 +0300 Subject: [PATCH 07/11] rpcbinding: improve indentation for internal array code --- .../testdata/structs/rpcbindings.out | 116 +++++++++--------- pkg/smartcontract/rpcbinding/binding.go | 2 +- 2 files changed, 59 insertions(+), 59 deletions(-) diff --git a/cli/smartcontract/testdata/structs/rpcbindings.out b/cli/smartcontract/testdata/structs/rpcbindings.out index aa1bf2e77..24e03b316 100644 --- a/cli/smartcontract/testdata/structs/rpcbindings.out +++ b/cli/smartcontract/testdata/structs/rpcbindings.out @@ -565,16 +565,16 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans res := make([]util.Uint160, len(arr)) for i := range res { res[i], err = func (item stackitem.Item) (util.Uint160, error) { - b, err := item.TryBytes() - if err != nil { - return util.Uint160{}, err - } - u, err := util.Uint160DecodeBytesBE(b) - if err != nil { - return util.Uint160{}, err - } - return u, nil - } (arr[i]) + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (arr[i]) if err != nil { return nil, err } @@ -594,16 +594,16 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans res := make(keys.PublicKeys, len(arr)) for i := range res { res[i], err = func (item stackitem.Item) (*keys.PublicKey, error) { - b, err := item.TryBytes() - if err != nil { - return nil, err - } - k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) - if err != nil { - return nil, err - } - return k, nil - } (arr[i]) + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + } (arr[i]) if err != nil { return nil, err } @@ -978,15 +978,15 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife res := make([]string, len(arr)) for i := range res { res[i], err = func (item stackitem.Item) (string, error) { - b, err := item.TryBytes() - if err != nil { - return "", err - } - if !utf8.Valid(b) { - return "", errors.New("not a UTF-8 string") - } - return string(b), nil - } (arr[i]) + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (arr[i]) if err != nil { return nil, err } @@ -1031,16 +1031,16 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife res := make([]util.Uint160, len(arr)) for i := range res { res[i], err = func (item stackitem.Item) (util.Uint160, error) { - b, err := item.TryBytes() - if err != nil { - return util.Uint160{}, err - } - u, err := util.Uint160DecodeBytesBE(b) - if err != nil { - return util.Uint160{}, err - } - return u, nil - } (arr[i]) + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (arr[i]) if err != nil { return nil, err } @@ -1212,15 +1212,15 @@ func itemToManagementPermission(item stackitem.Item, err error) (*ManagementPerm res := make([]string, len(arr)) for i := range res { res[i], err = func (item stackitem.Item) (string, error) { - b, err := item.TryBytes() - if err != nil { - return "", err - } - if !utf8.Valid(b) { - return "", errors.New("not a UTF-8 string") - } - return string(b), nil - } (arr[i]) + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (arr[i]) if err != nil { return nil, err } @@ -1381,16 +1381,16 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er res := make([]util.Uint160, len(arr)) for i := range res { res[i], err = func (item stackitem.Item) (util.Uint160, error) { - b, err := item.TryBytes() - if err != nil { - return util.Uint160{}, err - } - u, err := util.Uint160DecodeBytesBE(b) - if err != nil { - return util.Uint160{}, err - } - return u, nil - } (arr[i]) + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (arr[i]) if err != nil { return nil, err } diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index 1bfda8417..1e9b40f14 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -456,7 +456,7 @@ func etTypeConverter(et binding.ExtendedType, v string) string { } res := make(` + at + `, len(arr)) for i := range res { - res[i], err = ` + etTypeConverter(*et.Value, "arr[i]") + ` + res[i], err = ` + strings.ReplaceAll(etTypeConverter(*et.Value, "arr[i]"), "\n", "\n\t\t") + ` if err != nil { return nil, err } From ce67e6795e573d070c86b9721cd68ed263734c10 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 1 Dec 2022 22:16:37 +0300 Subject: [PATCH 08/11] rpcbinding: properly support maps I'm not sure that map with a `*big.Int` key is a good one, but it can work this way. --- .../testdata/structs/rpcbindings.out | 87 ++++++++++++++++--- pkg/smartcontract/rpcbinding/binding.go | 26 ++++-- 2 files changed, 98 insertions(+), 15 deletions(-) diff --git a/cli/smartcontract/testdata/structs/rpcbindings.out b/cli/smartcontract/testdata/structs/rpcbindings.out index 24e03b316..60c8e07ef 100644 --- a/cli/smartcontract/testdata/structs/rpcbindings.out +++ b/cli/smartcontract/testdata/structs/rpcbindings.out @@ -109,7 +109,7 @@ type ManagementGroup struct { type ManagementManifest struct { Name string Groups []*ManagementGroup - Features *stackitem.Map + Features map[string]string SupportedStandards []string ABI *ManagementABI Permissions []*ManagementPermission @@ -151,7 +151,7 @@ type StructsInternal struct { Sign []byte ArrOfBytes [][]byte ArrOfH160 []util.Uint160 - Map *stackitem.Map + Map map[*big.Int]keys.PublicKeys Struct *StructsInternal } // Invoker is used by ContractReader to call various safe methods. @@ -959,11 +959,42 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife } index++ - res.Features, err = func (item stackitem.Item) (*stackitem.Map, error) { - if t := item.Type(); t != stackitem.MapT { - return nil, fmt.Errorf("%s is not a map", t.String()) + res.Features, err = func (item stackitem.Item) (map[string]string, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) } - return item.(*stackitem.Map), nil + res := make(map[string]string) + for i := range m { + k, err := func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (m[i].Key) + if err != nil { + return nil, err + } + v, err := func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (m[i].Value) + if err != nil { + return nil, err + } + res[k] = v + } + return res, nil } (arr[index]) if err != nil { return nil, err @@ -1402,11 +1433,47 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er } index++ - res.Map, err = func (item stackitem.Item) (*stackitem.Map, error) { - if t := item.Type(); t != stackitem.MapT { - return nil, fmt.Errorf("%s is not a map", t.String()) + res.Map, err = func (item stackitem.Item) (map[*big.Int]keys.PublicKeys, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) } - return item.(*stackitem.Map), nil + res := make(map[*big.Int]keys.PublicKeys) + for i := range m { + k, err := m[i].Key.TryInteger() + if err != nil { + return nil, err + } + v, err := func (item stackitem.Item) (keys.PublicKeys, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make(keys.PublicKeys, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (m[i].Value) + if err != nil { + return nil, err + } + res[k] = v + } + return res, nil } (arr[index]) if err != nil { return nil, err diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index 1e9b40f14..6b10a5163 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -378,7 +378,9 @@ func extendedTypeToGo(et binding.ExtendedType, named map[string]binding.Extended return "[]interface{}", "" case smartcontract.MapType: - return "*stackitem.Map", "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + kt, _ := extendedTypeToGo(binding.ExtendedType{Base: et.Key}, named) + vt, _ := extendedTypeToGo(*et.Value, named) + return "map[" + kt + "]" + vt, "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" case smartcontract.InteropInterfaceType: return "interface{}", "" case smartcontract.VoidType: @@ -472,11 +474,25 @@ func etTypeConverter(et binding.ExtendedType, v string) string { }, v) case smartcontract.MapType: - return `func (item stackitem.Item) (*stackitem.Map, error) { - if t := item.Type(); t != stackitem.MapT { - return nil, fmt.Errorf("%s is not a map", t.String()) + at, _ := extendedTypeToGo(et, nil) + return `func (item stackitem.Item) (` + at + `, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) } - return item.(*stackitem.Map), nil + res := make(` + at + `) + for i := range m { + k, err := ` + strings.ReplaceAll(etTypeConverter(binding.ExtendedType{Base: et.Key}, "m[i].Key"), "\n", "\n\t\t") + ` + if err != nil { + return nil, err + } + v, err := ` + strings.ReplaceAll(etTypeConverter(*et.Value, "m[i].Value"), "\n", "\n\t\t") + ` + if err != nil { + return nil, err + } + res[k] = v + } + return res, nil } (` + v + `)` case smartcontract.InteropInterfaceType: return "item.Value(), nil" From c058ab5604392b4454f269892c96ab0e3a1e6573 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 2 Dec 2022 10:20:55 +0300 Subject: [PATCH 09/11] rpcbinding: handle more complex non-structured types --- cli/smartcontract/testdata/types/config.yml | 2 +- .../testdata/types/rpcbindings.out | 199 ++++++++++++++++++ cli/smartcontract/testdata/types/types.go | 12 ++ pkg/smartcontract/rpcbinding/binding.go | 117 ++++++---- 4 files changed, 285 insertions(+), 45 deletions(-) diff --git a/cli/smartcontract/testdata/types/config.yml b/cli/smartcontract/testdata/types/config.yml index 52ca5bbfc..a36d2e655 100644 --- a/cli/smartcontract/testdata/types/config.yml +++ b/cli/smartcontract/testdata/types/config.yml @@ -1,3 +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"] +safemethods: ["bool", "int", "bytes", "string", "hash160", "hash256", "publicKey", "signature", "bools", "ints", "bytess", "strings", "hash160s", "hash256s", "publicKeys", "signatures", "aAAStrings", "maps", "crazyMaps"] diff --git a/cli/smartcontract/testdata/types/rpcbindings.out b/cli/smartcontract/testdata/types/rpcbindings.out index 3f7cbb48d..65df997c9 100644 --- a/cli/smartcontract/testdata/types/rpcbindings.out +++ b/cli/smartcontract/testdata/types/rpcbindings.out @@ -2,11 +2,15 @@ package types import ( + "errors" + "fmt" "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" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "math/big" + "unicode/utf8" ) // Hash contains contract hash. @@ -28,6 +32,64 @@ func NewReader(invoker Invoker) *ContractReader { } +// AAAStrings invokes `aAAStrings` method of contract. +func (c *ContractReader) AAAStrings(s [][][]string) ([][][]string, error) { + return func (item stackitem.Item, err error) ([][][]string, error) { + if err != nil { + return nil, err + } + return func (item stackitem.Item) ([][][]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([][][]string, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) ([][]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([][]string, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) ([]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]string, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (item) + } (unwrap.Item(c.invoker.Call(Hash, "aAAStrings", s))) +} + // Bool invokes `bool` method of contract. func (c *ContractReader) Bool(b bool) (bool, error) { return unwrap.Bool(c.invoker.Call(Hash, "bool", b)) @@ -48,6 +110,97 @@ func (c *ContractReader) Bytess(b [][]byte) ([][]byte, error) { return unwrap.ArrayOfBytes(c.invoker.Call(Hash, "bytess", b)) } +// CrazyMaps invokes `crazyMaps` method of contract. +func (c *ContractReader) CrazyMaps(m map[*big.Int][]map[string][]util.Uint160) (map[*big.Int][]map[string][]util.Uint160, error) { + return func (item stackitem.Item, err error) (map[*big.Int][]map[string][]util.Uint160, error) { + if err != nil { + return nil, err + } + return func (item stackitem.Item) (map[*big.Int][]map[string][]util.Uint160, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[*big.Int][]map[string][]util.Uint160) + for i := range m { + k, err := m[i].Key.TryInteger() + if err != nil { + return nil, err + } + v, err := func (item stackitem.Item) ([]map[string][]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]map[string][]util.Uint160, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (map[string][]util.Uint160, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[string][]util.Uint160) + for i := range m { + k, err := func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (m[i].Key) + if err != nil { + return nil, err + } + v, err := func (item stackitem.Item) ([]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]util.Uint160, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (m[i].Value) + if err != nil { + return nil, err + } + res[k] = v + } + return res, nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (m[i].Value) + if err != nil { + return nil, err + } + res[k] = v + } + return res, nil + } (item) + } (unwrap.Item(c.invoker.Call(Hash, "crazyMaps", m))) +} + // 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)) @@ -78,6 +231,52 @@ func (c *ContractReader) Ints(i []*big.Int) ([]*big.Int, error) { return unwrap.ArrayOfBigInts(c.invoker.Call(Hash, "ints", i)) } +// Maps invokes `maps` method of contract. +func (c *ContractReader) Maps(m map[string]string) (map[string]string, error) { + return func (item stackitem.Item, err error) (map[string]string, error) { + if err != nil { + return nil, err + } + return func (item stackitem.Item) (map[string]string, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[string]string) + for i := range m { + k, err := func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (m[i].Key) + if err != nil { + return nil, err + } + v, err := func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (m[i].Value) + if err != nil { + return nil, err + } + res[k] = v + } + return res, nil + } (item) + } (unwrap.Item(c.invoker.Call(Hash, "maps", m))) +} + // 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)) diff --git a/cli/smartcontract/testdata/types/types.go b/cli/smartcontract/testdata/types/types.go index dcd52ae90..2d97dd081 100644 --- a/cli/smartcontract/testdata/types/types.go +++ b/cli/smartcontract/testdata/types/types.go @@ -67,3 +67,15 @@ func PublicKeys(k []interop.PublicKey) []interop.PublicKey { func Signatures(s []interop.Signature) []interop.Signature { return nil } + +func AAAStrings(s [][][]string) [][][]string { + return s +} + +func Maps(m map[string]string) map[string]string { + return m +} + +func CrazyMaps(m map[int][]map[string][]interop.Hash160) map[int][]map[string][]interop.Hash160 { + return m +} diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index 6b10a5163..e89988041 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -19,18 +19,20 @@ func (c *ContractReader) {{.Name}}({{range $index, $arg := .Arguments -}} {{- if ne $index 0}}, {{end}} {{- .Name}} {{.Type}} {{- end}}) {{if .ReturnType }}({{ .ReturnType }}, error) { - return {{if .CallFlag -}} - unwrap.{{.CallFlag}}(c.invoker.Call(Hash, "{{ .NameABI }}"{{/* CallFlag field is used for function name */}} - {{- else -}} - itemTo{{ cutPointer .ReturnType }}(unwrap.Item(c.invoker.Call(Hash, "{{ .NameABI }}"{{/* CallFlag field is used for function name */}} - {{- end -}} - {{- range $arg := .Arguments -}}, {{.Name}}{{end}})){{if not .CallFlag}}){{end}} + return {{if and (not .ItemTo) (eq .Unwrapper "Item")}}func (item stackitem.Item, err error) ({{ .ReturnType }}, error) { + if err != nil { + return nil, err + } + return {{etTypeConverter .ExtendedReturn "item"}} + } ( {{- end -}} {{if .ItemTo -}} itemTo{{ .ItemTo }}( {{- end -}} + unwrap.{{.Unwrapper}}(c.invoker.Call(Hash, "{{ .NameABI }}" + {{- range $arg := .Arguments -}}, {{.Name}}{{end -}} )) {{- if or .ItemTo (eq .Unwrapper "Item") -}} ) {{- end}} {{- else -}} (*result.Invoke, error) { c.invoker.Call(Hash, "{{ .NameABI }}" {{- range $arg := .Arguments -}}, {{.Name}}{{end}}) {{- end}} } -{{- if eq .CallFlag "SessionIterator"}} +{{- if eq .Unwrapper "SessionIterator"}} // {{.Name}}Expanded is similar to {{.Name}} (uses the same contract // method), but can be useful if the server used doesn't support sessions and @@ -245,7 +247,7 @@ type ( ContractTmpl struct { binding.ContractTmpl - SafeMethods []binding.MethodTmpl + SafeMethods []SafeMethodTmpl NamedTypes map[string]binding.ExtendedType IsNep11D bool @@ -256,6 +258,13 @@ type ( HasWriter bool HasIterator bool } + + SafeMethodTmpl struct { + binding.MethodTmpl + Unwrapper string + ItemTo string + ExtendedReturn binding.ExtendedType + } ) // NewConfig initializes and returns a new config instance. @@ -518,7 +527,15 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st for i := 0; i < len(ctr.Methods); i++ { abim := cfg.Manifest.ABI.GetMethod(ctr.Methods[i].NameABI, len(ctr.Methods[i].Arguments)) if abim.Safe { - ctr.SafeMethods = append(ctr.SafeMethods, ctr.Methods[i]) + ctr.SafeMethods = append(ctr.SafeMethods, SafeMethodTmpl{MethodTmpl: ctr.Methods[i]}) + et, ok := cfg.Types[abim.Name] + if ok { + ctr.SafeMethods[len(ctr.SafeMethods)-1].ExtendedReturn = et + if abim.ReturnType == smartcontract.ArrayType && len(et.Name) > 0 { + imports["errors"] = struct{}{} + ctr.SafeMethods[len(ctr.SafeMethods)-1].ItemTo = cutPointer(ctr.Methods[i].ReturnType) + } + } ctr.Methods = append(ctr.Methods[:i], ctr.Methods[i+1:]...) i-- } else { @@ -529,27 +546,12 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st } } for _, et := range cfg.NamedTypes { - for _, fet := range et.Fields { - _, pkg := extendedTypeToGo(fet.ExtendedType, ctr.NamedTypes) - if pkg != "" { - imports[pkg] = struct{}{} - } - // Additional packages used during decoding. - switch fet.Base { - case smartcontract.StringType: - imports["unicode/utf8"] = struct{}{} - case smartcontract.PublicKeyType: - imports["crypto/elliptic"] = struct{}{} - case smartcontract.MapType: - imports["fmt"] = struct{}{} - } - } + addETImports(et, ctr.NamedTypes, imports) } if len(cfg.NamedTypes) > 0 { imports["errors"] = struct{}{} } - // We're misusing CallFlag field for function name here. for i := range ctr.SafeMethods { switch ctr.SafeMethods[i].ReturnType { case "interface{}": @@ -559,49 +561,50 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st imports["github.com/nspcc-dev/neo-go/pkg/vm/stackitem"] = struct{}{} imports["github.com/nspcc-dev/neo-go/pkg/neorpc/result"] = struct{}{} ctr.SafeMethods[i].ReturnType = "uuid.UUID, result.Iterator" - ctr.SafeMethods[i].CallFlag = "SessionIterator" + ctr.SafeMethods[i].Unwrapper = "SessionIterator" ctr.HasIterator = true } else { imports["github.com/nspcc-dev/neo-go/pkg/vm/stackitem"] = struct{}{} ctr.SafeMethods[i].ReturnType = "stackitem.Item" - ctr.SafeMethods[i].CallFlag = "Item" + ctr.SafeMethods[i].Unwrapper = "Item" } case "bool": - ctr.SafeMethods[i].CallFlag = "Bool" + ctr.SafeMethods[i].Unwrapper = "Bool" case "*big.Int": - ctr.SafeMethods[i].CallFlag = "BigInt" + ctr.SafeMethods[i].Unwrapper = "BigInt" case "string": - ctr.SafeMethods[i].CallFlag = "UTF8String" + ctr.SafeMethods[i].Unwrapper = "UTF8String" case "util.Uint160": - ctr.SafeMethods[i].CallFlag = "Uint160" + ctr.SafeMethods[i].Unwrapper = "Uint160" case "util.Uint256": - ctr.SafeMethods[i].CallFlag = "Uint256" + ctr.SafeMethods[i].Unwrapper = "Uint256" case "*keys.PublicKey": - ctr.SafeMethods[i].CallFlag = "PublicKey" + ctr.SafeMethods[i].Unwrapper = "PublicKey" case "[]byte": - ctr.SafeMethods[i].CallFlag = "Bytes" + ctr.SafeMethods[i].Unwrapper = "Bytes" case "[]interface{}": imports["github.com/nspcc-dev/neo-go/pkg/vm/stackitem"] = struct{}{} ctr.SafeMethods[i].ReturnType = "[]stackitem.Item" - ctr.SafeMethods[i].CallFlag = "Array" + ctr.SafeMethods[i].Unwrapper = "Array" case "*stackitem.Map": - ctr.SafeMethods[i].CallFlag = "Map" + ctr.SafeMethods[i].Unwrapper = "Map" case "[]bool": - ctr.SafeMethods[i].CallFlag = "ArrayOfBools" + ctr.SafeMethods[i].Unwrapper = "ArrayOfBools" case "[]*big.Int": - ctr.SafeMethods[i].CallFlag = "ArrayOfBigInts" + ctr.SafeMethods[i].Unwrapper = "ArrayOfBigInts" case "[][]byte": - ctr.SafeMethods[i].CallFlag = "ArrayOfBytes" + ctr.SafeMethods[i].Unwrapper = "ArrayOfBytes" case "[]string": - ctr.SafeMethods[i].CallFlag = "ArrayOfUTF8Strings" + ctr.SafeMethods[i].Unwrapper = "ArrayOfUTF8Strings" case "[]util.Uint160": - ctr.SafeMethods[i].CallFlag = "ArrayOfUint160" + ctr.SafeMethods[i].Unwrapper = "ArrayOfUint160" case "[]util.Uint256": - ctr.SafeMethods[i].CallFlag = "ArrayOfUint256" + ctr.SafeMethods[i].Unwrapper = "ArrayOfUint256" case "keys.PublicKeys": - ctr.SafeMethods[i].CallFlag = "ArrayOfPublicKeys" + ctr.SafeMethods[i].Unwrapper = "ArrayOfPublicKeys" default: - ctr.SafeMethods[i].CallFlag = "" + addETImports(ctr.SafeMethods[i].ExtendedReturn, ctr.NamedTypes, imports) + ctr.SafeMethods[i].Unwrapper = "Item" } } @@ -629,6 +632,32 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st return ctr } +func addETImports(et binding.ExtendedType, named map[string]binding.ExtendedType, imports map[string]struct{}) { + _, pkg := extendedTypeToGo(et, named) + if pkg != "" { + imports[pkg] = struct{}{} + } + // Additional packages used during decoding. + switch et.Base { + case smartcontract.StringType: + imports["unicode/utf8"] = struct{}{} + imports["errors"] = struct{}{} + case smartcontract.PublicKeyType: + imports["crypto/elliptic"] = struct{}{} + case smartcontract.MapType: + imports["fmt"] = struct{}{} + } + if et.Value != nil { + addETImports(*et.Value, named, imports) + } + if et.Base == smartcontract.MapType { + addETImports(binding.ExtendedType{Base: et.Key}, named, imports) + } + for i := range et.Fields { + addETImports(et.Fields[i].ExtendedType, named, imports) + } +} + func cutPointer(s string) string { if s[0] == '*' { return s[1:] From ec5ebc8c18ba3a5bc95586c0647809e33c06ae79 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 2 Dec 2022 15:52:52 +0300 Subject: [PATCH 10/11] rpcbinding: improve indentation for internal unwrappers --- .../testdata/types/rpcbindings.out | 308 +++++++++--------- pkg/smartcontract/rpcbinding/binding.go | 13 +- 2 files changed, 163 insertions(+), 158 deletions(-) diff --git a/cli/smartcontract/testdata/types/rpcbindings.out b/cli/smartcontract/testdata/types/rpcbindings.out index 65df997c9..472f2aee1 100644 --- a/cli/smartcontract/testdata/types/rpcbindings.out +++ b/cli/smartcontract/testdata/types/rpcbindings.out @@ -39,54 +39,54 @@ func (c *ContractReader) AAAStrings(s [][][]string) ([][][]string, error) { return nil, err } return func (item stackitem.Item) ([][][]string, error) { - arr, ok := item.Value().([]stackitem.Item) - if !ok { - return nil, errors.New("not an array") - } - res := make([][][]string, len(arr)) - for i := range res { - res[i], err = func (item stackitem.Item) ([][]string, error) { - arr, ok := item.Value().([]stackitem.Item) - if !ok { - return nil, errors.New("not an array") - } - res := make([][]string, len(arr)) - for i := range res { - res[i], err = func (item stackitem.Item) ([]string, error) { - arr, ok := item.Value().([]stackitem.Item) - if !ok { - return nil, errors.New("not an array") - } - res := make([]string, len(arr)) - for i := range res { - res[i], err = func (item stackitem.Item) (string, error) { - b, err := item.TryBytes() - if err != nil { - return "", err - } - if !utf8.Valid(b) { - return "", errors.New("not a UTF-8 string") - } - return string(b), nil - } (arr[i]) - if err != nil { - return nil, err - } - } - return res, nil - } (arr[i]) - if err != nil { - return nil, err - } - } - return res, nil - } (arr[i]) - if err != nil { - return nil, err + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") } - } - return res, nil - } (item) + res := make([][][]string, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) ([][]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([][]string, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) ([]string, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]string, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (item) } (unwrap.Item(c.invoker.Call(Hash, "aAAStrings", s))) } @@ -117,87 +117,87 @@ func (c *ContractReader) CrazyMaps(m map[*big.Int][]map[string][]util.Uint160) ( return nil, err } return func (item stackitem.Item) (map[*big.Int][]map[string][]util.Uint160, error) { - m, ok := item.Value().([]stackitem.MapElement) - if !ok { - return nil, fmt.Errorf("%s is not a map", item.Type().String()) - } - res := make(map[*big.Int][]map[string][]util.Uint160) - for i := range m { - k, err := m[i].Key.TryInteger() - if err != nil { - return nil, err + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) } - v, err := func (item stackitem.Item) ([]map[string][]util.Uint160, error) { - arr, ok := item.Value().([]stackitem.Item) - if !ok { - return nil, errors.New("not an array") + res := make(map[*big.Int][]map[string][]util.Uint160) + for i := range m { + k, err := m[i].Key.TryInteger() + if err != nil { + return nil, err } - res := make([]map[string][]util.Uint160, len(arr)) - for i := range res { - res[i], err = func (item stackitem.Item) (map[string][]util.Uint160, error) { - m, ok := item.Value().([]stackitem.MapElement) - if !ok { - return nil, fmt.Errorf("%s is not a map", item.Type().String()) - } - res := make(map[string][]util.Uint160) - for i := range m { - k, err := func (item stackitem.Item) (string, error) { - b, err := item.TryBytes() - if err != nil { - return "", err - } - if !utf8.Valid(b) { - return "", errors.New("not a UTF-8 string") - } - return string(b), nil - } (m[i].Key) - if err != nil { - return nil, err - } - v, err := func (item stackitem.Item) ([]util.Uint160, error) { - arr, ok := item.Value().([]stackitem.Item) - if !ok { - return nil, errors.New("not an array") - } - res := make([]util.Uint160, len(arr)) - for i := range res { - res[i], err = func (item stackitem.Item) (util.Uint160, error) { - b, err := item.TryBytes() - if err != nil { - return util.Uint160{}, err - } - u, err := util.Uint160DecodeBytesBE(b) - if err != nil { - return util.Uint160{}, err - } - return u, nil - } (arr[i]) - if err != nil { - return nil, err - } - } - return res, nil - } (m[i].Value) - if err != nil { - return nil, err - } - res[k] = v - } - return res, nil - } (arr[i]) - if err != nil { - return nil, err + v, err := func (item stackitem.Item) ([]map[string][]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") } + res := make([]map[string][]util.Uint160, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (map[string][]util.Uint160, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[string][]util.Uint160) + for i := range m { + k, err := func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (m[i].Key) + if err != nil { + return nil, err + } + v, err := func (item stackitem.Item) ([]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]util.Uint160, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (util.Uint160, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint160{}, err + } + u, err := util.Uint160DecodeBytesBE(b) + if err != nil { + return util.Uint160{}, err + } + return u, nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (m[i].Value) + if err != nil { + return nil, err + } + res[k] = v + } + return res, nil + } (arr[i]) + if err != nil { + return nil, err + } + } + return res, nil + } (m[i].Value) + if err != nil { + return nil, err } - return res, nil - } (m[i].Value) - if err != nil { - return nil, err + res[k] = v } - res[k] = v - } - return res, nil - } (item) + return res, nil + } (item) } (unwrap.Item(c.invoker.Call(Hash, "crazyMaps", m))) } @@ -238,42 +238,42 @@ func (c *ContractReader) Maps(m map[string]string) (map[string]string, error) { return nil, err } return func (item stackitem.Item) (map[string]string, error) { - m, ok := item.Value().([]stackitem.MapElement) - if !ok { - return nil, fmt.Errorf("%s is not a map", item.Type().String()) - } - res := make(map[string]string) - for i := range m { - k, err := func (item stackitem.Item) (string, error) { - b, err := item.TryBytes() - if err != nil { - return "", err - } - if !utf8.Valid(b) { - return "", errors.New("not a UTF-8 string") - } - return string(b), nil - } (m[i].Key) - if err != nil { - return nil, err + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) } - v, err := func (item stackitem.Item) (string, error) { - b, err := item.TryBytes() + res := make(map[string]string) + for i := range m { + k, err := func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (m[i].Key) if err != nil { - return "", err + return nil, err } - if !utf8.Valid(b) { - return "", errors.New("not a UTF-8 string") + v, err := func (item stackitem.Item) (string, error) { + b, err := item.TryBytes() + if err != nil { + return "", err + } + if !utf8.Valid(b) { + return "", errors.New("not a UTF-8 string") + } + return string(b), nil + } (m[i].Value) + if err != nil { + return nil, err } - return string(b), nil - } (m[i].Value) - if err != nil { - return nil, err + res[k] = v } - res[k] = v - } - return res, nil - } (item) + return res, nil + } (item) } (unwrap.Item(c.invoker.Call(Hash, "maps", m))) } diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index e89988041..949ffc4d7 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -23,7 +23,7 @@ func (c *ContractReader) {{.Name}}({{range $index, $arg := .Arguments -}} if err != nil { return nil, err } - return {{etTypeConverter .ExtendedReturn "item"}} + return {{addIndent (etTypeConverter .ExtendedReturn "item") "\t"}} } ( {{- end -}} {{if .ItemTo -}} itemTo{{ .ItemTo }}( {{- end -}} unwrap.{{.Unwrapper}}(c.invoker.Call(Hash, "{{ .NameABI }}" {{- range $arg := .Arguments -}}, {{.Name}}{{end -}} )) {{- if or .ItemTo (eq .Unwrapper "Item") -}} ) {{- end}} @@ -319,6 +319,7 @@ func Generate(cfg binding.Config) error { ctr.NamedTypes = cfg.NamedTypes var srcTemplate = template.Must(template.New("generate").Funcs(template.FuncMap{ + "addIndent": addIndent, "etTypeConverter": etTypeConverter, "etTypeToStr": func(et binding.ExtendedType) string { r, _ := extendedTypeToGo(et, ctr.NamedTypes) @@ -467,7 +468,7 @@ func etTypeConverter(et binding.ExtendedType, v string) string { } res := make(` + at + `, len(arr)) for i := range res { - res[i], err = ` + strings.ReplaceAll(etTypeConverter(*et.Value, "arr[i]"), "\n", "\n\t\t") + ` + res[i], err = ` + addIndent(etTypeConverter(*et.Value, "arr[i]"), "\t\t") + ` if err != nil { return nil, err } @@ -491,11 +492,11 @@ func etTypeConverter(et binding.ExtendedType, v string) string { } res := make(` + at + `) for i := range m { - k, err := ` + strings.ReplaceAll(etTypeConverter(binding.ExtendedType{Base: et.Key}, "m[i].Key"), "\n", "\n\t\t") + ` + k, err := ` + addIndent(etTypeConverter(binding.ExtendedType{Base: et.Key}, "m[i].Key"), "\t\t") + ` if err != nil { return nil, err } - v, err := ` + strings.ReplaceAll(etTypeConverter(*et.Value, "m[i].Value"), "\n", "\n\t\t") + ` + v, err := ` + addIndent(etTypeConverter(*et.Value, "m[i].Value"), "\t\t") + ` if err != nil { return nil, err } @@ -673,3 +674,7 @@ func toTypeName(s string) string { return c }, strings.ToUpper(s[0:1])+s[1:]) } + +func addIndent(str string, ind string) string { + return strings.ReplaceAll(str, "\n", "\n"+ind) +} From e5aa5ca29401840b44b79e13f730537650c5a411 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 2 Dec 2022 16:12:56 +0300 Subject: [PATCH 11/11] rpcbinding: improve error reporting in generated code --- .../testdata/structs/rpcbindings.out | 192 +++++++++--------- .../testdata/types/rpcbindings.out | 22 +- pkg/smartcontract/rpcbinding/binding.go | 12 +- 3 files changed, 114 insertions(+), 112 deletions(-) diff --git a/cli/smartcontract/testdata/structs/rpcbindings.out b/cli/smartcontract/testdata/structs/rpcbindings.out index 60c8e07ef..a8330072b 100644 --- a/cli/smartcontract/testdata/structs/rpcbindings.out +++ b/cli/smartcontract/testdata/structs/rpcbindings.out @@ -218,13 +218,13 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Hash: %w", err) } index++ res.Version, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Version: %w", err) } index++ @@ -240,7 +240,7 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field PrevHash: %w", err) } index++ @@ -256,25 +256,25 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field MerkleRoot: %w", err) } index++ res.Timestamp, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Timestamp: %w", err) } index++ res.Nonce, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Nonce: %w", err) } index++ res.Index, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Index: %w", err) } index++ @@ -290,13 +290,13 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field NextConsensus: %w", err) } index++ res.TransactionsLength, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field TransactionsLength: %w", err) } @@ -331,13 +331,13 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Hash: %w", err) } index++ res.Version, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Version: %w", err) } index++ @@ -353,7 +353,7 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field PrevHash: %w", err) } index++ @@ -369,25 +369,25 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field MerkleRoot: %w", err) } index++ res.Timestamp, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Timestamp: %w", err) } index++ res.Nonce, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Nonce: %w", err) } index++ res.Index, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Index: %w", err) } index++ @@ -403,13 +403,13 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field NextConsensus: %w", err) } index++ res.TransactionsLength, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field TransactionsLength: %w", err) } index++ @@ -425,7 +425,7 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field PrevStateRoot: %w", err) } @@ -460,19 +460,19 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Hash: %w", err) } index++ res.Version, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Version: %w", err) } index++ res.Nonce, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Nonce: %w", err) } index++ @@ -488,31 +488,31 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Sender: %w", err) } index++ res.SysFee, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field SysFee: %w", err) } index++ res.NetFee, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field NetFee: %w", err) } index++ res.ValidUntilBlock, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field ValidUntilBlock: %w", err) } index++ res.Script, err = arr[index].TryBytes() if err != nil { - return nil, err + return nil, fmt.Errorf("field Script: %w", err) } @@ -547,13 +547,13 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Account: %w", err) } index++ res.Scopes, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Scopes: %w", err) } index++ @@ -576,13 +576,13 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans return u, nil } (arr[i]) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field AllowedContracts: %w", err) } index++ @@ -605,13 +605,13 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans return k, nil } (arr[i]) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field AllowedGroups: %w", err) } index++ @@ -624,13 +624,13 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans for i := range res { res[i], err = itemToLedgerWitnessRule(arr[i], nil) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Rules: %w", err) } @@ -655,13 +655,13 @@ func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnes index++ res.Type, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Type: %w", err) } index++ res.Value, err = arr[index].Value(), nil if err != nil { - return nil, err + return nil, fmt.Errorf("field Value: %w", err) } @@ -686,13 +686,13 @@ func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule index++ res.Action, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Action: %w", err) } index++ res.Condition, err = itemToLedgerWitnessCondition(arr[index], nil) if err != nil { - return nil, err + return nil, fmt.Errorf("field Condition: %w", err) } @@ -724,13 +724,13 @@ func itemToManagementABI(item stackitem.Item, err error) (*ManagementABI, error) for i := range res { res[i], err = itemToManagementMethod(arr[i], nil) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Methods: %w", err) } index++ @@ -743,13 +743,13 @@ func itemToManagementABI(item stackitem.Item, err error) (*ManagementABI, error) for i := range res { res[i], err = itemToManagementEvent(arr[i], nil) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Events: %w", err) } @@ -774,13 +774,13 @@ func itemToManagementContract(item stackitem.Item, err error) (*ManagementContra index++ res.ID, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field ID: %w", err) } index++ res.UpdateCounter, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field UpdateCounter: %w", err) } index++ @@ -796,19 +796,19 @@ func itemToManagementContract(item stackitem.Item, err error) (*ManagementContra return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Hash: %w", err) } index++ res.NEF, err = arr[index].TryBytes() if err != nil { - return nil, err + return nil, fmt.Errorf("field NEF: %w", err) } index++ res.Manifest, err = itemToManagementManifest(arr[index], nil) if err != nil { - return nil, err + return nil, fmt.Errorf("field Manifest: %w", err) } @@ -842,7 +842,7 @@ func itemToManagementEvent(item stackitem.Item, err error) (*ManagementEvent, er return string(b), nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Name: %w", err) } index++ @@ -855,13 +855,13 @@ func itemToManagementEvent(item stackitem.Item, err error) (*ManagementEvent, er for i := range res { res[i], err = itemToManagementParameter(arr[i], nil) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Params: %w", err) } @@ -896,13 +896,13 @@ func itemToManagementGroup(item stackitem.Item, err error) (*ManagementGroup, er return k, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field PublicKey: %w", err) } index++ res.Signature, err = arr[index].TryBytes() if err != nil { - return nil, err + return nil, fmt.Errorf("field Signature: %w", err) } @@ -936,7 +936,7 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return string(b), nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Name: %w", err) } index++ @@ -949,13 +949,13 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife for i := range res { res[i], err = itemToManagementGroup(arr[i], nil) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Groups: %w", err) } index++ @@ -977,7 +977,7 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return string(b), nil } (m[i].Key) if err != nil { - return nil, err + return nil, fmt.Errorf("key %d: %w", i, err) } v, err := func (item stackitem.Item) (string, error) { b, err := item.TryBytes() @@ -990,14 +990,14 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return string(b), nil } (m[i].Value) if err != nil { - return nil, err + return nil, fmt.Errorf("value %d: %w", i, err) } res[k] = v } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Features: %w", err) } index++ @@ -1019,19 +1019,19 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return string(b), nil } (arr[i]) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field SupportedStandards: %w", err) } index++ res.ABI, err = itemToManagementABI(arr[index], nil) if err != nil { - return nil, err + return nil, fmt.Errorf("field ABI: %w", err) } index++ @@ -1044,13 +1044,13 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife for i := range res { res[i], err = itemToManagementPermission(arr[i], nil) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Permissions: %w", err) } index++ @@ -1073,19 +1073,19 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return u, nil } (arr[i]) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Trusts: %w", err) } index++ res.Extra, err = arr[index].Value(), nil if err != nil { - return nil, err + return nil, fmt.Errorf("field Extra: %w", err) } @@ -1119,7 +1119,7 @@ func itemToManagementMethod(item stackitem.Item, err error) (*ManagementMethod, return string(b), nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Name: %w", err) } index++ @@ -1132,31 +1132,31 @@ func itemToManagementMethod(item stackitem.Item, err error) (*ManagementMethod, for i := range res { res[i], err = itemToManagementParameter(arr[i], nil) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Params: %w", err) } index++ res.ReturnType, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field ReturnType: %w", err) } index++ res.Offset, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Offset: %w", err) } index++ res.Safe, err = arr[index].TryBool() if err != nil { - return nil, err + return nil, fmt.Errorf("field Safe: %w", err) } @@ -1190,13 +1190,13 @@ func itemToManagementParameter(item stackitem.Item, err error) (*ManagementParam return string(b), nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Name: %w", err) } index++ res.Type, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Type: %w", err) } @@ -1231,7 +1231,7 @@ func itemToManagementPermission(item stackitem.Item, err error) (*ManagementPerm return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Contract: %w", err) } index++ @@ -1253,13 +1253,13 @@ func itemToManagementPermission(item stackitem.Item, err error) (*ManagementPerm return string(b), nil } (arr[i]) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Methods: %w", err) } @@ -1284,19 +1284,19 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er index++ res.Bool, err = arr[index].TryBool() if err != nil { - return nil, err + return nil, fmt.Errorf("field Bool: %w", err) } index++ res.Int, err = arr[index].TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("field Int: %w", err) } index++ res.Bytes, err = arr[index].TryBytes() if err != nil { - return nil, err + return nil, fmt.Errorf("field Bytes: %w", err) } index++ @@ -1311,7 +1311,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return string(b), nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field String: %w", err) } index++ @@ -1327,7 +1327,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field H160: %w", err) } index++ @@ -1343,7 +1343,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return u, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field H256: %w", err) } index++ @@ -1359,7 +1359,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return k, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field PK: %w", err) } index++ @@ -1375,13 +1375,13 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return k, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field PubKey: %w", err) } index++ res.Sign, err = arr[index].TryBytes() if err != nil { - return nil, err + return nil, fmt.Errorf("field Sign: %w", err) } index++ @@ -1394,13 +1394,13 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er for i := range res { res[i], err = arr[i].TryBytes() if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field ArrOfBytes: %w", err) } index++ @@ -1423,13 +1423,13 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return u, nil } (arr[i]) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field ArrOfH160: %w", err) } index++ @@ -1442,7 +1442,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er for i := range m { k, err := m[i].Key.TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("key %d: %w", i, err) } v, err := func (item stackitem.Item) (keys.PublicKeys, error) { arr, ok := item.Value().([]stackitem.Item) @@ -1463,26 +1463,26 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return k, nil } (arr[i]) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (m[i].Value) if err != nil { - return nil, err + return nil, fmt.Errorf("value %d: %w", i, err) } res[k] = v } return res, nil } (arr[index]) if err != nil { - return nil, err + return nil, fmt.Errorf("field Map: %w", err) } index++ res.Struct, err = itemToStructsInternal(arr[index], nil) if err != nil { - return nil, err + return nil, fmt.Errorf("field Struct: %w", err) } diff --git a/cli/smartcontract/testdata/types/rpcbindings.out b/cli/smartcontract/testdata/types/rpcbindings.out index 472f2aee1..602e589ac 100644 --- a/cli/smartcontract/testdata/types/rpcbindings.out +++ b/cli/smartcontract/testdata/types/rpcbindings.out @@ -70,19 +70,19 @@ func (c *ContractReader) AAAStrings(s [][][]string) ([][][]string, error) { return string(b), nil } (arr[i]) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[i]) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (arr[i]) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil @@ -125,7 +125,7 @@ func (c *ContractReader) CrazyMaps(m map[*big.Int][]map[string][]util.Uint160) ( for i := range m { k, err := m[i].Key.TryInteger() if err != nil { - return nil, err + return nil, fmt.Errorf("key %d: %w", i, err) } v, err := func (item stackitem.Item) ([]map[string][]util.Uint160, error) { arr, ok := item.Value().([]stackitem.Item) @@ -152,7 +152,7 @@ func (c *ContractReader) CrazyMaps(m map[*big.Int][]map[string][]util.Uint160) ( return string(b), nil } (m[i].Key) if err != nil { - return nil, err + return nil, fmt.Errorf("key %d: %w", i, err) } v, err := func (item stackitem.Item) ([]util.Uint160, error) { arr, ok := item.Value().([]stackitem.Item) @@ -173,26 +173,26 @@ func (c *ContractReader) CrazyMaps(m map[*big.Int][]map[string][]util.Uint160) ( return u, nil } (arr[i]) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (m[i].Value) if err != nil { - return nil, err + return nil, fmt.Errorf("value %d: %w", i, err) } res[k] = v } return res, nil } (arr[i]) if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil } (m[i].Value) if err != nil { - return nil, err + return nil, fmt.Errorf("value %d: %w", i, err) } res[k] = v } @@ -255,7 +255,7 @@ func (c *ContractReader) Maps(m map[string]string) (map[string]string, error) { return string(b), nil } (m[i].Key) if err != nil { - return nil, err + return nil, fmt.Errorf("key %d: %w", i, err) } v, err := func (item stackitem.Item) (string, error) { b, err := item.TryBytes() @@ -268,7 +268,7 @@ func (c *ContractReader) Maps(m map[string]string) (map[string]string, error) { return string(b), nil } (m[i].Value) if err != nil { - return nil, err + return nil, fmt.Errorf("value %d: %w", i, err) } res[k] = v } diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index 949ffc4d7..b447c1dd0 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -235,7 +235,7 @@ func itemTo{{toTypeName $name}}(item stackitem.Item, err error) (*{{toTypeName $ index++ res.{{.Field}}, err = {{etTypeConverter .ExtendedType "arr[index]"}} if err != nil { - return nil, err + return nil, fmt.Errorf("field {{.Field}}: %w", err) } {{end}} {{end}} @@ -470,7 +470,7 @@ func etTypeConverter(et binding.ExtendedType, v string) string { for i := range res { res[i], err = ` + addIndent(etTypeConverter(*et.Value, "arr[i]"), "\t\t") + ` if err != nil { - return nil, err + return nil, fmt.Errorf("item %d: %w", i, err) } } return res, nil @@ -494,11 +494,11 @@ func etTypeConverter(et binding.ExtendedType, v string) string { for i := range m { k, err := ` + addIndent(etTypeConverter(binding.ExtendedType{Base: et.Key}, "m[i].Key"), "\t\t") + ` if err != nil { - return nil, err + return nil, fmt.Errorf("key %d: %w", i, err) } v, err := ` + addIndent(etTypeConverter(*et.Value, "m[i].Value"), "\t\t") + ` if err != nil { - return nil, err + return nil, fmt.Errorf("value %d: %w", i, err) } res[k] = v } @@ -533,7 +533,6 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st if ok { ctr.SafeMethods[len(ctr.SafeMethods)-1].ExtendedReturn = et if abim.ReturnType == smartcontract.ArrayType && len(et.Name) > 0 { - imports["errors"] = struct{}{} ctr.SafeMethods[len(ctr.SafeMethods)-1].ItemTo = cutPointer(ctr.Methods[i].ReturnType) } } @@ -647,6 +646,9 @@ func addETImports(et binding.ExtendedType, named map[string]binding.ExtendedType imports["crypto/elliptic"] = struct{}{} case smartcontract.MapType: imports["fmt"] = struct{}{} + case smartcontract.ArrayType: + imports["errors"] = struct{}{} + imports["fmt"] = struct{}{} } if et.Value != nil { addETImports(*et.Value, named, imports)