Skip to content

Commit a8966da

Browse files
author
Tural Devrishev
committed
smartcontract: add support NEP-25
Close #3995. Signed-off-by: Tural Devrishev <[email protected]>
1 parent 3d162dc commit a8966da

File tree

23 files changed

+1289
-311
lines changed

23 files changed

+1289
-311
lines changed

cli/smartcontract/generate_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ func TestAssistedRPCBindings(t *testing.T) {
483483
}
484484
testName += fmt.Sprintf(", predefined hash: %t", hasDefinedHash)
485485
t.Run(testName, func(t *testing.T) {
486-
outFile := filepath.Join(tmpDir, "out.go")
486+
outFile := filepath.Join("./testdata/rpcbindings/", "out.go")
487487
if configFile == "" {
488488
if len(suffix) != 0 {
489489
configFile = filepath.Join(source, "config_"+suffix[0]+".yml")

cli/smartcontract/smart_contract.go

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
2222
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
2323
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
24-
"github.com/nspcc-dev/neo-go/pkg/smartcontract/binding"
2524
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
2625
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
2726
"github.com/nspcc-dev/neo-go/pkg/util"
@@ -419,15 +418,13 @@ func initSmartContract(ctx *cli.Context) error {
419418
SourceURL: "http://example.com/",
420419
SupportedStandards: []string{},
421420
SafeMethods: []string{},
422-
Events: []compiler.HybridEvent{
421+
Events: []manifest.Event{
423422
{
424423
Name: "Hello world!",
425-
Parameters: []compiler.HybridParameter{
424+
Parameters: []manifest.Parameter{
426425
{
427-
Parameter: manifest.Parameter{
428-
Name: "args",
429-
Type: smartcontract.ArrayType,
430-
},
426+
Name: "args",
427+
Type: smartcontract.ArrayType,
431428
},
432429
},
433430
},
@@ -517,7 +514,8 @@ func contractCompile(ctx *cli.Context) error {
517514
NoEventsCheck: ctx.Bool("no-events"),
518515
NoPermissionsCheck: ctx.Bool("no-permissions"),
519516

520-
GuessEventTypes: ctx.Bool("guess-eventtypes"),
517+
GuessEventTypes: ctx.Bool("guess-eventtypes"),
518+
CollectedNamedTypes: make(map[string]manifest.ExtendedType),
521519
}
522520

523521
if len(confFile) != 0 {
@@ -528,7 +526,6 @@ func contractCompile(ctx *cli.Context) error {
528526
o.Name = conf.Name
529527
o.SourceURL = conf.SourceURL
530528
o.ContractEvents = conf.Events
531-
o.DeclaredNamedTypes = conf.NamedTypes
532529
o.ContractSupportedStandards = conf.SupportedStandards
533530
o.Permissions = make([]manifest.Permission, len(conf.Permissions))
534531
for i := range conf.Permissions {
@@ -760,10 +757,10 @@ type ProjectConfig struct {
760757
SourceURL string
761758
SafeMethods []string
762759
SupportedStandards []string
763-
Events []compiler.HybridEvent
760+
Events []manifest.Event
764761
Permissions []permission
765-
Overloads map[string]string `yaml:"overloads,omitempty"`
766-
NamedTypes map[string]binding.ExtendedType `yaml:"namedtypes,omitempty"`
762+
Overloads map[string]string `yaml:"overloads,omitempty"`
763+
NamedTypes map[string]manifest.ExtendedType `yaml:"namedtypes,omitempty"`
767764
}
768765

769766
func inspect(ctx *cli.Context) error {

cli/smartcontract/testdata/rpcbindings/invalid5/invalid.yml

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@ events:
66
type: Array
77
extendedtype:
88
base: Array
9-
name: invalid5.NamedStruct
10-
namedtypes:
11-
invalid5.NamedStruct:
12-
base: Array
13-
name: invalid5.NamedStruct
14-
fields:
15-
- field: SomeInt
16-
base: Integer
9+
name: NamedStruct
10+
fields:
11+
- field: SomeInt
12+
base: Integer

cli/smartcontract/testdata/rpcbindings/notifications/config_extended.yml

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ events:
2626
extendedtype:
2727
base: Struct
2828
name: crazyStruct
29+
fields:
30+
- field: I
31+
base: Integer
32+
- field: B
33+
base: Boolean
2934
- name: "SomeArray"
3035
parameters:
3136
- name: a
@@ -43,18 +48,6 @@ events:
4348
extendedtype:
4449
base: Struct
4550
name: simpleStruct
46-
namedtypes:
47-
crazyStruct:
48-
base: Struct
49-
name: crazyStruct
50-
fields:
51-
- field: I
52-
base: Integer
53-
- field: B
54-
base: Boolean
55-
simpleStruct:
56-
base: Struct
57-
name: simpleStruct
58-
fields:
59-
- field: i
60-
base: Integer
51+
fields:
52+
- field: i
53+
base: Integer

internal/testchain/transaction.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ func NewDeployTx(bc Ledger, name string, sender util.Uint160, r gio.Reader, conf
7878
o.Name = conf.Name
7979
o.SourceURL = conf.SourceURL
8080
o.ContractEvents = conf.Events
81-
o.DeclaredNamedTypes = conf.NamedTypes
8281
o.ContractSupportedStandards = conf.SupportedStandards
8382
o.Permissions = make([]manifest.Permission, len(conf.Permissions))
8483
for i := range conf.Permissions {

pkg/compiler/codegen.go

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,8 +671,11 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
671671
if id.Name != "_" {
672672
if !isMapKeyCheck {
673673
if len(t.Values) == 0 {
674-
c.emitDefault(c.typeOf(t.Type))
674+
typ := c.typeOf(t.Type)
675+
c.collectNamedTypes(typ)
676+
c.emitDefault(typ)
675677
} else if i == 0 || !multiRet {
678+
c.collectNamedTypes(c.typeOf(t.Values[i]))
676679
ast.Walk(c, t.Values[i])
677680
}
678681
}
@@ -725,6 +728,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
725728
}
726729
if !isAssignOp && !isMapKeyCheck {
727730
for i := range n.Rhs {
731+
c.collectNamedTypes(c.typeOf(n.Rhs[i]))
728732
ast.Walk(c, n.Rhs[i])
729733
}
730734
}
@@ -972,6 +976,20 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
972976
t := c.typeOf(n)
973977
switch typ := t.Underlying().(type) {
974978
case *types.Struct:
979+
named, isNamed := t.(*types.Named)
980+
if c.buildInfo.options != nil {
981+
if !isNamed {
982+
name := "unnamed"
983+
if c.buildInfo.options.CollectedNamedTypes != nil {
984+
for c.buildInfo.options.CollectedNamedTypes[name].Name == name {
985+
name = name + "X"
986+
}
987+
_ = c.genStructExtended(typ, name, c.buildInfo.options.CollectedNamedTypes)
988+
}
989+
} else if !isInteropPath(named.String()) {
990+
_ = c.genStructExtended(typ, named.Obj().Name(), c.buildInfo.options.CollectedNamedTypes)
991+
}
992+
}
975993
c.convertStruct(n, false)
976994
case *types.Map:
977995
c.convertMap(n)
@@ -2161,6 +2179,52 @@ func (c *codegen) checkGetMapValueWithOKFlag(expr ast.Expr) bool {
21612179
return ok
21622180
}
21632181

2182+
func (c *codegen) collectNamedTypes(t types.Type) {
2183+
if c.buildInfo.options == nil {
2184+
return
2185+
}
2186+
var (
2187+
seen = make(map[types.Type]bool)
2188+
walk func(types.Type)
2189+
)
2190+
walk = func(tt types.Type) {
2191+
if tt == nil || seen[tt] {
2192+
return
2193+
}
2194+
seen[tt] = true
2195+
2196+
switch x := tt.(type) {
2197+
case *types.Pointer:
2198+
walk(x.Elem())
2199+
case *types.Slice:
2200+
walk(x.Elem())
2201+
case *types.Map:
2202+
walk(x.Key())
2203+
walk(x.Elem())
2204+
case *types.Struct:
2205+
name := "unnamed"
2206+
if c.buildInfo.options.CollectedNamedTypes != nil {
2207+
for c.buildInfo.options.CollectedNamedTypes[name].Name == name {
2208+
name = name + "X"
2209+
}
2210+
_ = c.genStructExtended(x, name, c.buildInfo.options.CollectedNamedTypes)
2211+
}
2212+
for i := range x.NumFields() {
2213+
walk(x.Field(i).Type())
2214+
}
2215+
case *types.Named:
2216+
if typ, ok := x.Underlying().(*types.Struct); ok && !isInteropPath(x.String()) {
2217+
_ = c.genStructExtended(typ, x.Obj().Pkg().Path()+"."+x.Obj().Name(), c.buildInfo.options.CollectedNamedTypes)
2218+
for i := range typ.NumFields() {
2219+
walk(typ.Field(i).Type())
2220+
}
2221+
}
2222+
}
2223+
}
2224+
2225+
walk(t)
2226+
}
2227+
21642228
func (c *codegen) emitGetMapValueWithOKFlag(expr ast.Expr) {
21652229
var (
21662230
idxExpr = expr.(*ast.IndexExpr)

pkg/compiler/compiler.go

Lines changed: 8 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ type Options struct {
6060
// variadic args usages. If some type is specified via config file, then the config's
6161
// one is preferable. Currently, event's parameter type is defined from the first
6262
// occurrence of event call.
63-
GuessEventTypes bool
63+
GuessEventTypes bool
64+
GuessedNamedTypes map[string]manifest.ExtendedType
65+
CollectedNamedTypes map[string]manifest.ExtendedType
6466

6567
// Name is a contract's name to be written to manifest.
6668
Name string
@@ -69,11 +71,7 @@ type Options struct {
6971
SourceURL string
7072

7173
// Runtime notifications declared in the contract configuration file.
72-
ContractEvents []HybridEvent
73-
74-
// DeclaredNamedTypes is the set of named types that were declared in the
75-
// contract configuration type and are the part of manifest events.
76-
DeclaredNamedTypes map[string]binding.ExtendedType
74+
ContractEvents []manifest.Event
7775

7876
// The list of standards supported by the contract.
7977
ContractSupportedStandards []string
@@ -92,23 +90,6 @@ type Options struct {
9290
BindingsFile string
9391
}
9492

95-
// HybridEvent represents the description of event emitted by the contract squashed
96-
// with extended event's parameters description. We have it as a separate type for
97-
// the user's convenience. It is applied for the smart contract configuration file
98-
// only.
99-
type HybridEvent struct {
100-
Name string `json:"name"`
101-
Parameters []HybridParameter `json:"parameters"`
102-
}
103-
104-
// HybridParameter contains the manifest's event parameter description united with
105-
// the extended type description for this parameter. It is applied for the smart
106-
// contract configuration file only.
107-
type HybridParameter struct {
108-
manifest.Parameter `yaml:",inline"`
109-
ExtendedType *binding.ExtendedType `yaml:"extendedtype,omitempty"`
110-
}
111-
11293
type buildInfo struct {
11394
config *packages.Config
11495
program []*packages.Package
@@ -337,15 +318,6 @@ func CompileAndSave(src string, o *Options) ([]byte, error) {
337318
cfg.Types[m.Name.Name] = *m.ReturnTypeExtended
338319
}
339320
}
340-
if len(di.NamedTypes) > 0 {
341-
cfg.NamedTypes = di.NamedTypes
342-
}
343-
for name, et := range o.DeclaredNamedTypes {
344-
if _, ok := cfg.NamedTypes[name]; ok {
345-
return nil, fmt.Errorf("configured declared named type intersects with the contract's one: `%s`", name)
346-
}
347-
cfg.NamedTypes[name] = et
348-
}
349321
for _, e := range o.ContractEvents {
350322
eStructName := rpcbinding.ToEventBindingName(e.Name)
351323
for _, p := range e.Parameters {
@@ -357,6 +329,7 @@ func CompileAndSave(src string, o *Options) ([]byte, error) {
357329
}
358330
}
359331
if o.GuessEventTypes {
332+
o.GuessedNamedTypes = make(map[string]manifest.ExtendedType)
360333
if len(di.EmittedEvents) > 0 {
361334
var keys = make([]string, 0, len(di.EmittedEvents))
362335
for k := range di.EmittedEvents {
@@ -366,7 +339,7 @@ func CompileAndSave(src string, o *Options) ([]byte, error) {
366339
for _, eventName := range keys {
367340
var (
368341
eventUsages = di.EmittedEvents[eventName]
369-
manifestEvent HybridEvent
342+
manifestEvent manifest.Event
370343
)
371344
for _, e := range o.ContractEvents {
372345
if e.Name == eventName {
@@ -408,12 +381,12 @@ func CompileAndSave(src string, o *Options) ([]byte, error) {
408381
if p.ExtendedType != nil {
409382
typeName := p.ExtendedType.Name
410383
if extType, ok := exampleUsage.ExtTypes[typeName]; ok {
411-
for _, ok := cfg.NamedTypes[typeName]; ok; _, ok = cfg.NamedTypes[typeName] {
384+
for _, ok := o.GuessedNamedTypes[typeName]; ok; _, ok = o.GuessedNamedTypes[typeName] {
412385
typeName = typeName + "X"
413386
}
414387
extType.Name = typeName
415388
p.ExtendedType.Name = typeName
416-
cfg.NamedTypes[typeName] = extType
389+
o.GuessedNamedTypes[typeName] = extType
417390
}
418391
if _, ok := cfg.Types[pname]; !ok {
419392
cfg.Types[pname] = *p.ExtendedType

pkg/compiler/compiler_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -188,36 +188,36 @@ func TestEventWarnings(t *testing.T) {
188188
})
189189
t.Run("wrong parameter number", func(t *testing.T) {
190190
_, err = compiler.CreateManifest(di, &compiler.Options{
191-
ContractEvents: []compiler.HybridEvent{{Name: "Event"}},
191+
ContractEvents: []manifest.Event{{Name: "Event"}},
192192
Name: "payable",
193193
})
194194
require.Error(t, err)
195195
})
196196
t.Run("wrong parameter type", func(t *testing.T) {
197197
_, err = compiler.CreateManifest(di, &compiler.Options{
198-
ContractEvents: []compiler.HybridEvent{{
198+
ContractEvents: []manifest.Event{{
199199
Name: "Event",
200-
Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.StringType)}},
200+
Parameters: []manifest.Parameter{manifest.NewParameter("number", smartcontract.StringType)},
201201
}},
202202
Name: "payable",
203203
})
204204
require.Error(t, err)
205205
})
206206
t.Run("any parameter type", func(t *testing.T) {
207207
_, err = compiler.CreateManifest(di, &compiler.Options{
208-
ContractEvents: []compiler.HybridEvent{{
208+
ContractEvents: []manifest.Event{{
209209
Name: "Event",
210-
Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.AnyType)}},
210+
Parameters: []manifest.Parameter{manifest.NewParameter("number", smartcontract.AnyType)},
211211
}},
212212
Name: "payable",
213213
})
214214
require.NoError(t, err)
215215
})
216216
t.Run("good", func(t *testing.T) {
217217
_, err = compiler.CreateManifest(di, &compiler.Options{
218-
ContractEvents: []compiler.HybridEvent{{
218+
ContractEvents: []manifest.Event{{
219219
Name: "Event",
220-
Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.IntegerType)}},
220+
Parameters: []manifest.Parameter{manifest.NewParameter("number", smartcontract.IntegerType)},
221221
}},
222222
Name: "payable",
223223
})
@@ -253,7 +253,7 @@ func TestEventWarnings(t *testing.T) {
253253
require.Error(t, err)
254254

255255
_, err = compiler.CreateManifest(di, &compiler.Options{
256-
ContractEvents: []compiler.HybridEvent{{Name: "Event"}},
256+
ContractEvents: []manifest.Event{{Name: "Event"}},
257257
Name: "eventTest",
258258
})
259259
require.NoError(t, err)
@@ -271,9 +271,9 @@ func TestEventWarnings(t *testing.T) {
271271

272272
_, err = compiler.CreateManifest(di, &compiler.Options{
273273
Name: "eventTest",
274-
ContractEvents: []compiler.HybridEvent{{
274+
ContractEvents: []manifest.Event{{
275275
Name: "Event",
276-
Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.IntegerType)}},
276+
Parameters: []manifest.Parameter{manifest.NewParameter("number", smartcontract.IntegerType)},
277277
}},
278278
})
279279
require.NoError(t, err)
@@ -289,7 +289,7 @@ func TestNotifyInVerify(t *testing.T) {
289289
t.Run(name, func(t *testing.T) {
290290
src := fmt.Sprintf(srcTmpl, name)
291291
_, _, err := compiler.CompileWithOptions("eventTest.go", strings.NewReader(src),
292-
&compiler.Options{ContractEvents: []compiler.HybridEvent{{Name: "Event"}}})
292+
&compiler.Options{ContractEvents: []manifest.Event{{Name: "Event"}}})
293293
require.Error(t, err)
294294

295295
t.Run("suppress", func(t *testing.T) {

0 commit comments

Comments
 (0)