From 3e2755e66d95a9aa381b3b8156f3b0b3fed1fc7f Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Thu, 25 May 2023 18:03:05 +0300 Subject: [PATCH] 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 --- cli/smartcontract/generate_test.go | 18 +- .../testdata/notifications/config.yml | 6 +- .../notifications/config_extended.yml | 47 + .../testdata/notifications/notifications.go | 4 + .../testdata/notifications/rpcbindings.out | 92 ++ .../notifications/rpcbindings_extended.out | 1126 +++++++++++++++++ 6 files changed, 1288 insertions(+), 5 deletions(-) create mode 100644 cli/smartcontract/testdata/notifications/config_extended.yml create mode 100755 cli/smartcontract/testdata/notifications/rpcbindings_extended.out diff --git a/cli/smartcontract/generate_test.go b/cli/smartcontract/generate_test.go index 67a46fc12..a9dfaca67 100644 --- a/cli/smartcontract/generate_test.go +++ b/cli/smartcontract/generate_test.go @@ -379,14 +379,24 @@ func TestAssistedRPCBindings(t *testing.T) { app := cli.NewApp() app.Commands = NewCommands() - var checkBinding = func(source string) { - t.Run(source, func(t *testing.T) { + var checkBinding = func(source string, suffix ...string) { + 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") bindingF := filepath.Join(tmpDir, "binding.yml") nefF := filepath.Join(tmpDir, "out.nef") require.NoError(t, app.Run([]string{"", "contract", "compile", "--in", source, - "--config", filepath.Join(source, "config.yml"), + "--config", configFile, "--manifest", manifestF, "--bindings", bindingF, "--out", nefF, @@ -402,7 +412,6 @@ func TestAssistedRPCBindings(t *testing.T) { data, err := os.ReadFile(outFile) require.NoError(t, err) data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows. - expectedFile := filepath.Join(source, "rpcbindings.out") if rewriteExpectedOutputs { require.NoError(t, os.WriteFile(expectedFile, data, os.ModePerm)) } else { @@ -417,6 +426,7 @@ func TestAssistedRPCBindings(t *testing.T) { checkBinding(filepath.Join("testdata", "types")) checkBinding(filepath.Join("testdata", "structs")) checkBinding(filepath.Join("testdata", "notifications")) + checkBinding(filepath.Join("testdata", "notifications"), "_extended") require.False(t, rewriteExpectedOutputs) } diff --git a/cli/smartcontract/testdata/notifications/config.yml b/cli/smartcontract/testdata/notifications/config.yml index 5fd018d53..ef2866318 100644 --- a/cli/smartcontract/testdata/notifications/config.yml +++ b/cli/smartcontract/testdata/notifications/config.yml @@ -12,4 +12,8 @@ events: - name: "SomeStruct" parameters: - name: s - type: Struct \ No newline at end of file + type: Struct + - name: "SomeArray" + parameters: + - name: a + type: Array \ No newline at end of file diff --git a/cli/smartcontract/testdata/notifications/config_extended.yml b/cli/smartcontract/testdata/notifications/config_extended.yml new file mode 100644 index 000000000..ac8f06d6d --- /dev/null +++ b/cli/smartcontract/testdata/notifications/config_extended.yml @@ -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 \ No newline at end of file diff --git a/cli/smartcontract/testdata/notifications/notifications.go b/cli/smartcontract/testdata/notifications/notifications.go index f679901af..80564a4e4 100644 --- a/cli/smartcontract/testdata/notifications/notifications.go +++ b/cli/smartcontract/testdata/notifications/notifications.go @@ -19,3 +19,7 @@ func Struct() { B bool }{I: 123, B: true}) } + +func Array() { + runtime.Notify("SomeArray", [][]int{}) +} diff --git a/cli/smartcontract/testdata/notifications/rpcbindings.out b/cli/smartcontract/testdata/notifications/rpcbindings.out index ae0724975..ea40f64ea 100644 --- a/cli/smartcontract/testdata/notifications/rpcbindings.out +++ b/cli/smartcontract/testdata/notifications/rpcbindings.out @@ -92,6 +92,11 @@ type SomeStructEvent struct { 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. type Actor interface { MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) @@ -112,6 +117,28 @@ func New(actor Actor) *Contract { 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. // This transaction is signed and immediately sent to the network. // 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 } + +// 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 +} diff --git a/cli/smartcontract/testdata/notifications/rpcbindings_extended.out b/cli/smartcontract/testdata/notifications/rpcbindings_extended.out new file mode 100755 index 000000000..0c64320a8 --- /dev/null +++ b/cli/smartcontract/testdata/notifications/rpcbindings_extended.out @@ -0,0 +1,1126 @@ +// Package structs contains RPC wrappers for Notifications contract. +package structs + +import ( + "crypto/elliptic" + "errors" + "fmt" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "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/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} + +// CrazyStruct is a contract-specific crazyStruct type used by its methods. +type CrazyStruct struct { + I *big.Int + B bool +} + +// 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 any +} + +// LedgerWitnessRule is a contract-specific ledger.WitnessRule type used by its methods. +type LedgerWitnessRule struct { + Action *big.Int + Condition *LedgerWitnessCondition +} + +// ComplicatedNameEvent represents "! complicated name %$#" event emitted by the contract. +type ComplicatedNameEvent struct { + ComplicatedParam string +} + +// SomeMapEvent represents "SomeMap" event emitted by the contract. +type SomeMapEvent struct { + M map[*big.Int]map[string][]util.Uint160 +} + +// SomeStructEvent represents "SomeStruct" event emitted by the contract. +type SomeStructEvent struct { + S *CrazyStruct +} + +// SomeArrayEvent represents "SomeArray" event emitted by the contract. +type SomeArrayEvent struct { + A [][]*big.Int +} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) + MakeRun(script []byte) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) + MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) + SendRun(script []byte) (util.Uint256, uint32, error) +} + +// Contract implements all contract methods. +type Contract struct { + actor Actor +} + +// New creates an instance of Contract using Hash and the given Actor. +func New(actor Actor) *Contract { + 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. +// 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) CrazyMap() (util.Uint256, uint32, error) { + return c.actor.SendCall(Hash, "crazyMap") +} + +// CrazyMapTransaction creates a transaction invoking `crazyMap` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) CrazyMapTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(Hash, "crazyMap") +} + +// CrazyMapUnsigned creates a transaction invoking `crazyMap` 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) CrazyMapUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(Hash, "crazyMap", nil) +} + +// Main creates a transaction invoking `main` 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) Main() (util.Uint256, uint32, error) { + return c.actor.SendCall(Hash, "main") +} + +// MainTransaction creates a transaction invoking `main` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) MainTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(Hash, "main") +} + +// MainUnsigned creates a transaction invoking `main` 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) MainUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(Hash, "main", nil) +} + +// Struct creates a transaction invoking `struct` 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) Struct() (util.Uint256, uint32, error) { + return c.actor.SendCall(Hash, "struct") +} + +// StructTransaction creates a transaction invoking `struct` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) StructTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(Hash, "struct") +} + +// StructUnsigned creates a transaction invoking `struct` 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) StructUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(Hash, "struct", nil) +} + +// itemToCrazyStruct converts stack item into *CrazyStruct. +func itemToCrazyStruct(item stackitem.Item, err error) (*CrazyStruct, error) { + if err != nil { + return nil, err + } + var res = new(CrazyStruct) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of CrazyStruct from the given stack item +// and returns an error if so. +func (res *CrazyStruct) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.I, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field I: %w", err) + } + + index++ + res.B, err = arr[index].TryBool() + if err != nil { + return fmt.Errorf("field B: %w", err) + } + + return nil +} + +// itemToLedgerBlock converts stack item into *LedgerBlock. +func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { + if err != nil { + return nil, err + } + var res = new(LedgerBlock) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerBlock from the given stack item +// and returns an error if so. +func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 9 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + 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 fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", 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 fmt.Errorf("field PrevHash: %w", 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 fmt.Errorf("field MerkleRoot: %w", err) + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Timestamp: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Index: %w", 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 fmt.Errorf("field NextConsensus: %w", err) + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field TransactionsLength: %w", err) + } + + return nil +} + +// itemToLedgerBlockSR converts stack item into *LedgerBlockSR. +func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) { + if err != nil { + return nil, err + } + var res = new(LedgerBlockSR) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerBlockSR from the given stack item +// and returns an error if so. +func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 10 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + 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 fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", 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 fmt.Errorf("field PrevHash: %w", 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 fmt.Errorf("field MerkleRoot: %w", err) + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Timestamp: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Index: %w", 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 fmt.Errorf("field NextConsensus: %w", err) + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field TransactionsLength: %w", 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 fmt.Errorf("field PrevStateRoot: %w", err) + } + + return nil +} + +// itemToLedgerTransaction converts stack item into *LedgerTransaction. +func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction, error) { + if err != nil { + return nil, err + } + var res = new(LedgerTransaction) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerTransaction from the given stack item +// and returns an error if so. +func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 8 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + 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 fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", 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 fmt.Errorf("field Sender: %w", err) + } + + index++ + res.SysFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field SysFee: %w", err) + } + + index++ + res.NetFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field NetFee: %w", err) + } + + index++ + res.ValidUntilBlock, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field ValidUntilBlock: %w", err) + } + + index++ + res.Script, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Script: %w", err) + } + + return nil +} + +// itemToLedgerTransactionSigner converts stack item into *LedgerTransactionSigner. +func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTransactionSigner, error) { + if err != nil { + return nil, err + } + var res = new(LedgerTransactionSigner) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerTransactionSigner from the given stack item +// and returns an error if so. +func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 5 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + 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 fmt.Errorf("field Account: %w", err) + } + + index++ + res.Scopes, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Scopes: %w", 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, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field AllowedContracts: %w", 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, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field AllowedGroups: %w", 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, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Rules: %w", err) + } + + return nil +} + +// itemToLedgerWitnessCondition converts stack item into *LedgerWitnessCondition. +func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnessCondition, error) { + if err != nil { + return nil, err + } + var res = new(LedgerWitnessCondition) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerWitnessCondition from the given stack item +// and returns an error if so. +func (res *LedgerWitnessCondition) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Type, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Type: %w", err) + } + + index++ + res.Value, err = arr[index].Value(), error(nil) + if err != nil { + return fmt.Errorf("field Value: %w", err) + } + + return nil +} + +// itemToLedgerWitnessRule converts stack item into *LedgerWitnessRule. +func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule, error) { + if err != nil { + return nil, err + } + var res = new(LedgerWitnessRule) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerWitnessRule from the given stack item +// and returns an error if so. +func (res *LedgerWitnessRule) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 2 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Action, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Action: %w", err) + } + + index++ + res.Condition, err = itemToLedgerWitnessCondition(arr[index], nil) + if err != nil { + return fmt.Errorf("field Condition: %w", err) + } + + return nil +} + +// ComplicatedNameEventsFromApplicationLog retrieves a set of all emitted events +// with "! complicated name %$#" name from the provided ApplicationLog. +func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*ComplicatedNameEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*ComplicatedNameEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "! complicated name %$#" { + continue + } + event := new(ComplicatedNameEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize ComplicatedNameEvent from stackitem (execution %d, event %d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided stackitem.Array to ComplicatedNameEvent and +// returns an error if so. +func (e *ComplicatedNameEvent) 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.ComplicatedParam, 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 fmt.Errorf("field ComplicatedParam: %w", err) + } + + return nil +} + +// SomeMapEventsFromApplicationLog retrieves a set of all emitted events +// with "SomeMap" name from the provided ApplicationLog. +func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SomeMapEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SomeMap" { + continue + } + event := new(SomeMapEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SomeMapEvent from stackitem (execution %d, event %d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided stackitem.Array to SomeMapEvent and +// returns an error if so. +func (e *SomeMapEvent) 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.M, err = 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, fmt.Errorf("key %d: %w", i, err) + } + v, 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, fmt.Errorf("key %d: %w", i, 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, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + } (m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + } (m[i].Value) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field M: %w", err) + } + + return nil +} + +// SomeStructEventsFromApplicationLog retrieves a set of all emitted events +// with "SomeStruct" name from the provided ApplicationLog. +func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStructEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SomeStructEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SomeStruct" { + continue + } + event := new(SomeStructEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SomeStructEvent from stackitem (execution %d, event %d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided stackitem.Array to SomeStructEvent and +// returns an error if so. +func (e *SomeStructEvent) 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.S, err = itemToCrazyStruct(arr[index], nil) + if err != nil { + return fmt.Errorf("field S: %w", err) + } + + 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) ([][]*big.Int, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([][]*big.Int, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) ([]*big.Int, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*big.Int, len(arr)) + for i := range res { + res[i], err = arr[i].TryInteger() + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + } (arr[i]) + 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 +}