smartcontract: add test for user-defined extended event types configuration

config_extended.yml contains an example of user-defined configuration
file with extended event types. User-defined event types are allowed
to be named and complicated, i.e. properly support extended types
notation.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
This commit is contained in:
Anna Shaleva 2023-05-25 18:03:05 +03:00
parent a0d991a500
commit 3e2755e66d
6 changed files with 1288 additions and 5 deletions

View file

@ -379,14 +379,24 @@ func TestAssistedRPCBindings(t *testing.T) {
app := cli.NewApp() app := cli.NewApp()
app.Commands = NewCommands() app.Commands = NewCommands()
var checkBinding = func(source string) { var checkBinding = func(source string, suffix ...string) {
t.Run(source, func(t *testing.T) { testName := source
if len(suffix) != 0 {
testName += suffix[0]
}
t.Run(testName, func(t *testing.T) {
configFile := filepath.Join(source, "config.yml")
expectedFile := filepath.Join(source, "rpcbindings.out")
if len(suffix) != 0 {
configFile = filepath.Join(source, "config"+suffix[0]+".yml")
expectedFile = filepath.Join(source, "rpcbindings"+suffix[0]+".out")
}
manifestF := filepath.Join(tmpDir, "manifest.json") manifestF := filepath.Join(tmpDir, "manifest.json")
bindingF := filepath.Join(tmpDir, "binding.yml") bindingF := filepath.Join(tmpDir, "binding.yml")
nefF := filepath.Join(tmpDir, "out.nef") nefF := filepath.Join(tmpDir, "out.nef")
require.NoError(t, app.Run([]string{"", "contract", "compile", require.NoError(t, app.Run([]string{"", "contract", "compile",
"--in", source, "--in", source,
"--config", filepath.Join(source, "config.yml"), "--config", configFile,
"--manifest", manifestF, "--manifest", manifestF,
"--bindings", bindingF, "--bindings", bindingF,
"--out", nefF, "--out", nefF,
@ -402,7 +412,6 @@ func TestAssistedRPCBindings(t *testing.T) {
data, err := os.ReadFile(outFile) data, err := os.ReadFile(outFile)
require.NoError(t, err) require.NoError(t, err)
data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows. data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows.
expectedFile := filepath.Join(source, "rpcbindings.out")
if rewriteExpectedOutputs { if rewriteExpectedOutputs {
require.NoError(t, os.WriteFile(expectedFile, data, os.ModePerm)) require.NoError(t, os.WriteFile(expectedFile, data, os.ModePerm))
} else { } else {
@ -417,6 +426,7 @@ func TestAssistedRPCBindings(t *testing.T) {
checkBinding(filepath.Join("testdata", "types")) checkBinding(filepath.Join("testdata", "types"))
checkBinding(filepath.Join("testdata", "structs")) checkBinding(filepath.Join("testdata", "structs"))
checkBinding(filepath.Join("testdata", "notifications")) checkBinding(filepath.Join("testdata", "notifications"))
checkBinding(filepath.Join("testdata", "notifications"), "_extended")
require.False(t, rewriteExpectedOutputs) require.False(t, rewriteExpectedOutputs)
} }

View file

@ -12,4 +12,8 @@ events:
- name: "SomeStruct" - name: "SomeStruct"
parameters: parameters:
- name: s - name: s
type: Struct type: Struct
- name: "SomeArray"
parameters:
- name: a
type: Array

View file

@ -0,0 +1,47 @@
name: "Notifications"
sourceurl: https://github.com/nspcc-dev/neo-go/
events:
- name: "! complicated name %$#"
parameters:
- name: ! complicated param @#$%
type: String
- name: "SomeMap"
parameters:
- name: m
type: Map
extendedtype:
base: Map
key: Integer
value:
base: Map
key: String
value:
base: Array
value:
base: Hash160
- name: "SomeStruct"
parameters:
- name: s
type: Struct
extendedtype:
base: Struct
name: crazyStruct
- name: "SomeArray"
parameters:
- name: a
type: Array
extendedtype:
base: Array
value:
base: Array
value:
base: Integer
namedtypes:
crazyStruct:
base: Struct
name: crazyStruct
fields:
- field: I
base: Integer
- field: B
base: Boolean

View file

@ -19,3 +19,7 @@ func Struct() {
B bool B bool
}{I: 123, B: true}) }{I: 123, B: true})
} }
func Array() {
runtime.Notify("SomeArray", [][]int{})
}

View file

@ -92,6 +92,11 @@ type SomeStructEvent struct {
S []any S []any
} }
// SomeArrayEvent represents "SomeArray" event emitted by the contract.
type SomeArrayEvent struct {
A []any
}
// Actor is used by Contract to call state-changing methods. // Actor is used by Contract to call state-changing methods.
type Actor interface { type Actor interface {
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
@ -112,6 +117,28 @@ func New(actor Actor) *Contract {
return &Contract{actor} return &Contract{actor}
} }
// Array creates a transaction invoking `array` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Array() (util.Uint256, uint32, error) {
return c.actor.SendCall(Hash, "array")
}
// ArrayTransaction creates a transaction invoking `array` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) ArrayTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(Hash, "array")
}
// ArrayUnsigned creates a transaction invoking `array` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) ArrayUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(Hash, "array", nil)
}
// CrazyMap creates a transaction invoking `crazyMap` method of the contract. // CrazyMap creates a transaction invoking `crazyMap` method of the contract.
// This transaction is signed and immediately sent to the network. // This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any. // The values returned are its hash, ValidUntilBlock value and error if any.
@ -936,3 +963,68 @@ func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error {
return nil return nil
} }
// SomeArrayEventsFromApplicationLog retrieves a set of all emitted events
// with "SomeArray" name from the provided ApplicationLog.
func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArrayEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*SomeArrayEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "SomeArray" {
continue
}
event := new(SomeArrayEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize SomeArrayEvent from stackitem (execution %d, event %d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided stackitem.Array to SomeArrayEvent and
// returns an error if so.
func (e *SomeArrayEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.A, err = func (item stackitem.Item) ([]any, error) {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, errors.New("not an array")
}
res := make([]any, len(arr))
for i := range res {
res[i], err = arr[i].Value(), error(nil)
if err != nil {
return nil, fmt.Errorf("item %d: %w", i, err)
}
}
return res, nil
} (arr[index])
if err != nil {
return fmt.Errorf("field A: %w", err)
}
return nil
}

File diff suppressed because it is too large Load diff