From 7a06cea885a735708d819777935b92986984cf72 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 23 May 2023 10:42:27 +0300 Subject: [PATCH 01/22] cli: allow to rewrite expected test output Don't perform it manually every time. Signed-off-by: Anna Shaleva --- cli/smartcontract/generate_test.go | 33 ++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/cli/smartcontract/generate_test.go b/cli/smartcontract/generate_test.go index ae0ef741f..6756adb82 100644 --- a/cli/smartcontract/generate_test.go +++ b/cli/smartcontract/generate_test.go @@ -324,6 +324,10 @@ func (c *ContractReader) Get() (*big.Int, error) { `, string(data)) } +// rewriteExpectedOutputs denotes whether expected output files should be rewritten +// for TestGenerateRPCBindings and TestAssistedRPCBindings. +const rewriteExpectedOutputs = false + func TestGenerateRPCBindings(t *testing.T) { tmpDir := t.TempDir() app := cli.NewApp() @@ -341,10 +345,14 @@ func TestGenerateRPCBindings(t *testing.T) { data, err := os.ReadFile(outFile) require.NoError(t, err) data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows. - expected, err := os.ReadFile(good) - require.NoError(t, err) - expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows. - require.Equal(t, string(expected), string(data)) + if rewriteExpectedOutputs { + require.NoError(t, os.WriteFile(good, data, os.ModePerm)) + } else { + expected, err := os.ReadFile(good) + require.NoError(t, err) + expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows. + require.Equal(t, string(expected), string(data)) + } }) } @@ -363,6 +371,8 @@ func TestGenerateRPCBindings(t *testing.T) { checkBinding(filepath.Join("testdata", "nonepiter", "iter.manifest.json"), "0x00112233445566778899aabbccddeeff00112233", filepath.Join("testdata", "nonepiter", "iter.go")) + + require.False(t, rewriteExpectedOutputs) } func TestAssistedRPCBindings(t *testing.T) { @@ -393,15 +403,22 @@ func TestAssistedRPCBindings(t *testing.T) { data, err := os.ReadFile(outFile) require.NoError(t, err) data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows. - expected, err := os.ReadFile(filepath.Join(source, "rpcbindings.out")) - require.NoError(t, err) - expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows. - require.Equal(t, string(expected), string(data)) + expectedFile := filepath.Join(source, "rpcbindings.out") + if rewriteExpectedOutputs { + require.NoError(t, os.WriteFile(expectedFile, data, os.ModePerm)) + } else { + expected, err := os.ReadFile(expectedFile) + require.NoError(t, err) + expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows. + require.Equal(t, string(expected), string(data)) + } }) } checkBinding(filepath.Join("testdata", "types")) checkBinding(filepath.Join("testdata", "structs")) + + require.False(t, rewriteExpectedOutputs) } func TestGenerate_Errors(t *testing.T) { From b5ec1271d5ae3ff1daff5c7453320d2a4cfe9310 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 23 May 2023 14:16:43 +0300 Subject: [PATCH 02/22] .gitignore: do not ignore compiler testdata cli's testdata folder stores contracts examples and expected compilation output, thus doesn't need to be ignored. *.out files are the bindings outputs only, do not ignore them as far. Signed-off-by: Anna Shaleva --- .gitignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index fbc04a091..3262aa4e4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,6 @@ # Test binary, build with `go test -c` *.test -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - # Added by CoZ developers vendor/ bin/ @@ -54,6 +51,7 @@ testdata/ !pkg/services/notary/testdata !pkg/services/oracle/testdata !pkg/smartcontract/testdata +!cli/smartcontract/testdata pkg/vm/testdata/fuzz !pkg/vm/testdata !pkg/wallet/testdata From 36af361c2bf51833933ed5bf4868e957cc5564f6 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 3 May 2023 14:55:02 +0300 Subject: [PATCH 03/22] smartcontract: add FromStackItem wrapper for RPC bindings Make it exported and reusable from the user's code. Signed-off-by: Anna Shaleva --- .../testdata/structs/rpcbindings.out | 454 ++++++++++++------ pkg/smartcontract/rpcbinding/binding.go | 22 +- 2 files changed, 318 insertions(+), 158 deletions(-) diff --git a/cli/smartcontract/testdata/structs/rpcbindings.out b/cli/smartcontract/testdata/structs/rpcbindings.out index cbe2a6e67..039b15e7e 100644 --- a/cli/smartcontract/testdata/structs/rpcbindings.out +++ b/cli/smartcontract/testdata/structs/rpcbindings.out @@ -195,16 +195,26 @@ 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 nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 9 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(LedgerBlock) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { b, err := item.TryBytes() @@ -218,13 +228,13 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Hash: %w", err) + return fmt.Errorf("field Hash: %w", err) } index++ res.Version, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Version: %w", err) + return fmt.Errorf("field Version: %w", err) } index++ @@ -240,7 +250,7 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field PrevHash: %w", err) + return fmt.Errorf("field PrevHash: %w", err) } index++ @@ -256,25 +266,25 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field MerkleRoot: %w", err) + return fmt.Errorf("field MerkleRoot: %w", err) } index++ res.Timestamp, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Timestamp: %w", err) + return fmt.Errorf("field Timestamp: %w", err) } index++ res.Nonce, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Nonce: %w", err) + return fmt.Errorf("field Nonce: %w", err) } index++ res.Index, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Index: %w", err) + return fmt.Errorf("field Index: %w", err) } index++ @@ -290,17 +300,17 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field NextConsensus: %w", err) + return fmt.Errorf("field NextConsensus: %w", err) } index++ res.TransactionsLength, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field TransactionsLength: %w", err) + return fmt.Errorf("field TransactionsLength: %w", err) } - return res, err + return nil } // itemToLedgerBlockSR converts stack item into *LedgerBlockSR. @@ -308,16 +318,26 @@ 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 nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 10 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(LedgerBlockSR) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { b, err := item.TryBytes() @@ -331,13 +351,13 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Hash: %w", err) + return fmt.Errorf("field Hash: %w", err) } index++ res.Version, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Version: %w", err) + return fmt.Errorf("field Version: %w", err) } index++ @@ -353,7 +373,7 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field PrevHash: %w", err) + return fmt.Errorf("field PrevHash: %w", err) } index++ @@ -369,25 +389,25 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field MerkleRoot: %w", err) + return fmt.Errorf("field MerkleRoot: %w", err) } index++ res.Timestamp, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Timestamp: %w", err) + return fmt.Errorf("field Timestamp: %w", err) } index++ res.Nonce, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Nonce: %w", err) + return fmt.Errorf("field Nonce: %w", err) } index++ res.Index, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Index: %w", err) + return fmt.Errorf("field Index: %w", err) } index++ @@ -403,13 +423,13 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field NextConsensus: %w", err) + return fmt.Errorf("field NextConsensus: %w", err) } index++ res.TransactionsLength, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field TransactionsLength: %w", err) + return fmt.Errorf("field TransactionsLength: %w", err) } index++ @@ -425,11 +445,11 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field PrevStateRoot: %w", err) + return fmt.Errorf("field PrevStateRoot: %w", err) } - return res, err + return nil } // itemToLedgerTransaction converts stack item into *LedgerTransaction. @@ -437,16 +457,26 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction 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 nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 8 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(LedgerTransaction) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { b, err := item.TryBytes() @@ -460,19 +490,19 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Hash: %w", err) + return fmt.Errorf("field Hash: %w", err) } index++ res.Version, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Version: %w", err) + return fmt.Errorf("field Version: %w", err) } index++ res.Nonce, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Nonce: %w", err) + return fmt.Errorf("field Nonce: %w", err) } index++ @@ -488,35 +518,35 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Sender: %w", err) + return fmt.Errorf("field Sender: %w", err) } index++ res.SysFee, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field SysFee: %w", err) + return fmt.Errorf("field SysFee: %w", err) } index++ res.NetFee, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field NetFee: %w", err) + return fmt.Errorf("field NetFee: %w", err) } index++ res.ValidUntilBlock, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field ValidUntilBlock: %w", err) + return fmt.Errorf("field ValidUntilBlock: %w", err) } index++ res.Script, err = arr[index].TryBytes() if err != nil { - return nil, fmt.Errorf("field Script: %w", err) + return fmt.Errorf("field Script: %w", err) } - return res, err + return nil } // itemToLedgerTransactionSigner converts stack item into *LedgerTransactionSigner. @@ -524,16 +554,26 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans 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 nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 5 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(LedgerTransactionSigner) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Account, err = func (item stackitem.Item) (util.Uint160, error) { b, err := item.TryBytes() @@ -547,13 +587,13 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Account: %w", err) + return fmt.Errorf("field Account: %w", err) } index++ res.Scopes, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Scopes: %w", err) + return fmt.Errorf("field Scopes: %w", err) } index++ @@ -582,7 +622,7 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field AllowedContracts: %w", err) + return fmt.Errorf("field AllowedContracts: %w", err) } index++ @@ -611,7 +651,7 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field AllowedGroups: %w", err) + return fmt.Errorf("field AllowedGroups: %w", err) } index++ @@ -630,11 +670,11 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Rules: %w", err) + return fmt.Errorf("field Rules: %w", err) } - return res, err + return nil } // itemToLedgerWitnessCondition converts stack item into *LedgerWitnessCondition. @@ -642,30 +682,40 @@ func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnes 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 nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 2 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(LedgerWitnessCondition) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Type, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Type: %w", err) + return fmt.Errorf("field Type: %w", err) } index++ res.Value, err = arr[index].Value(), nil if err != nil { - return nil, fmt.Errorf("field Value: %w", err) + return fmt.Errorf("field Value: %w", err) } - return res, err + return nil } // itemToLedgerWitnessRule converts stack item into *LedgerWitnessRule. @@ -673,30 +723,40 @@ func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule 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 nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 2 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(LedgerWitnessRule) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Action, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Action: %w", err) + return fmt.Errorf("field Action: %w", err) } index++ res.Condition, err = itemToLedgerWitnessCondition(arr[index], nil) if err != nil { - return nil, fmt.Errorf("field Condition: %w", err) + return fmt.Errorf("field Condition: %w", err) } - return res, err + return nil } // itemToManagementABI converts stack item into *ManagementABI. @@ -704,16 +764,26 @@ func itemToManagementABI(item stackitem.Item, err error) (*ManagementABI, error) if err != nil { return nil, err } + var res = new(ManagementABI) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementABI from the given stack item +// and returns an error if so. +func (res *ManagementABI) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 2 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementABI) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Methods, err = func (item stackitem.Item) ([]*ManagementMethod, error) { arr, ok := item.Value().([]stackitem.Item) @@ -730,7 +800,7 @@ func itemToManagementABI(item stackitem.Item, err error) (*ManagementABI, error) return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Methods: %w", err) + return fmt.Errorf("field Methods: %w", err) } index++ @@ -749,11 +819,11 @@ func itemToManagementABI(item stackitem.Item, err error) (*ManagementABI, error) return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Events: %w", err) + return fmt.Errorf("field Events: %w", err) } - return res, err + return nil } // itemToManagementContract converts stack item into *ManagementContract. @@ -761,26 +831,36 @@ func itemToManagementContract(item stackitem.Item, err error) (*ManagementContra if err != nil { return nil, err } + var res = new(ManagementContract) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementContract from the given stack item +// and returns an error if so. +func (res *ManagementContract) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 5 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementContract) - var index = -1 + var ( + index = -1 + err error + ) index++ res.ID, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field ID: %w", err) + return fmt.Errorf("field ID: %w", err) } index++ res.UpdateCounter, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field UpdateCounter: %w", err) + return fmt.Errorf("field UpdateCounter: %w", err) } index++ @@ -796,23 +876,23 @@ func itemToManagementContract(item stackitem.Item, err error) (*ManagementContra return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Hash: %w", err) + return fmt.Errorf("field Hash: %w", err) } index++ res.NEF, err = arr[index].TryBytes() if err != nil { - return nil, fmt.Errorf("field NEF: %w", err) + return fmt.Errorf("field NEF: %w", err) } index++ res.Manifest, err = itemToManagementManifest(arr[index], nil) if err != nil { - return nil, fmt.Errorf("field Manifest: %w", err) + return fmt.Errorf("field Manifest: %w", err) } - return res, err + return nil } // itemToManagementEvent converts stack item into *ManagementEvent. @@ -820,16 +900,26 @@ func itemToManagementEvent(item stackitem.Item, err error) (*ManagementEvent, er if err != nil { return nil, err } + var res = new(ManagementEvent) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementEvent from the given stack item +// and returns an error if so. +func (res *ManagementEvent) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 2 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementEvent) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Name, err = func (item stackitem.Item) (string, error) { b, err := item.TryBytes() @@ -842,7 +932,7 @@ func itemToManagementEvent(item stackitem.Item, err error) (*ManagementEvent, er return string(b), nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Name: %w", err) + return fmt.Errorf("field Name: %w", err) } index++ @@ -861,11 +951,11 @@ func itemToManagementEvent(item stackitem.Item, err error) (*ManagementEvent, er return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Params: %w", err) + return fmt.Errorf("field Params: %w", err) } - return res, err + return nil } // itemToManagementGroup converts stack item into *ManagementGroup. @@ -873,16 +963,26 @@ func itemToManagementGroup(item stackitem.Item, err error) (*ManagementGroup, er if err != nil { return nil, err } + var res = new(ManagementGroup) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementGroup from the given stack item +// and returns an error if so. +func (res *ManagementGroup) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 2 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementGroup) - var index = -1 + var ( + index = -1 + err error + ) index++ res.PublicKey, err = func (item stackitem.Item) (*keys.PublicKey, error) { b, err := item.TryBytes() @@ -896,17 +996,17 @@ func itemToManagementGroup(item stackitem.Item, err error) (*ManagementGroup, er return k, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field PublicKey: %w", err) + return fmt.Errorf("field PublicKey: %w", err) } index++ res.Signature, err = arr[index].TryBytes() if err != nil { - return nil, fmt.Errorf("field Signature: %w", err) + return fmt.Errorf("field Signature: %w", err) } - return res, err + return nil } // itemToManagementManifest converts stack item into *ManagementManifest. @@ -914,16 +1014,26 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife if err != nil { return nil, err } + var res = new(ManagementManifest) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementManifest from the given stack item +// and returns an error if so. +func (res *ManagementManifest) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 8 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementManifest) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Name, err = func (item stackitem.Item) (string, error) { b, err := item.TryBytes() @@ -936,7 +1046,7 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return string(b), nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Name: %w", err) + return fmt.Errorf("field Name: %w", err) } index++ @@ -955,7 +1065,7 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Groups: %w", err) + return fmt.Errorf("field Groups: %w", err) } index++ @@ -997,7 +1107,7 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Features: %w", err) + return fmt.Errorf("field Features: %w", err) } index++ @@ -1025,13 +1135,13 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field SupportedStandards: %w", err) + return fmt.Errorf("field SupportedStandards: %w", err) } index++ res.ABI, err = itemToManagementABI(arr[index], nil) if err != nil { - return nil, fmt.Errorf("field ABI: %w", err) + return fmt.Errorf("field ABI: %w", err) } index++ @@ -1050,7 +1160,7 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Permissions: %w", err) + return fmt.Errorf("field Permissions: %w", err) } index++ @@ -1079,17 +1189,17 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Trusts: %w", err) + return fmt.Errorf("field Trusts: %w", err) } index++ res.Extra, err = arr[index].Value(), nil if err != nil { - return nil, fmt.Errorf("field Extra: %w", err) + return fmt.Errorf("field Extra: %w", err) } - return res, err + return nil } // itemToManagementMethod converts stack item into *ManagementMethod. @@ -1097,16 +1207,26 @@ func itemToManagementMethod(item stackitem.Item, err error) (*ManagementMethod, if err != nil { return nil, err } + var res = new(ManagementMethod) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementMethod from the given stack item +// and returns an error if so. +func (res *ManagementMethod) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 5 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementMethod) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Name, err = func (item stackitem.Item) (string, error) { b, err := item.TryBytes() @@ -1119,7 +1239,7 @@ func itemToManagementMethod(item stackitem.Item, err error) (*ManagementMethod, return string(b), nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Name: %w", err) + return fmt.Errorf("field Name: %w", err) } index++ @@ -1138,29 +1258,29 @@ func itemToManagementMethod(item stackitem.Item, err error) (*ManagementMethod, return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Params: %w", err) + return fmt.Errorf("field Params: %w", err) } index++ res.ReturnType, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field ReturnType: %w", err) + return fmt.Errorf("field ReturnType: %w", err) } index++ res.Offset, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Offset: %w", err) + return fmt.Errorf("field Offset: %w", err) } index++ res.Safe, err = arr[index].TryBool() if err != nil { - return nil, fmt.Errorf("field Safe: %w", err) + return fmt.Errorf("field Safe: %w", err) } - return res, err + return nil } // itemToManagementParameter converts stack item into *ManagementParameter. @@ -1168,16 +1288,26 @@ func itemToManagementParameter(item stackitem.Item, err error) (*ManagementParam if err != nil { return nil, err } + var res = new(ManagementParameter) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementParameter from the given stack item +// and returns an error if so. +func (res *ManagementParameter) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 2 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementParameter) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Name, err = func (item stackitem.Item) (string, error) { b, err := item.TryBytes() @@ -1190,17 +1320,17 @@ func itemToManagementParameter(item stackitem.Item, err error) (*ManagementParam return string(b), nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Name: %w", err) + return fmt.Errorf("field Name: %w", err) } index++ res.Type, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Type: %w", err) + return fmt.Errorf("field Type: %w", err) } - return res, err + return nil } // itemToManagementPermission converts stack item into *ManagementPermission. @@ -1208,16 +1338,26 @@ func itemToManagementPermission(item stackitem.Item, err error) (*ManagementPerm if err != nil { return nil, err } + var res = new(ManagementPermission) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of ManagementPermission from the given stack item +// and returns an error if so. +func (res *ManagementPermission) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 2 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(ManagementPermission) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Contract, err = func (item stackitem.Item) (util.Uint160, error) { b, err := item.TryBytes() @@ -1231,7 +1371,7 @@ func itemToManagementPermission(item stackitem.Item, err error) (*ManagementPerm return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Contract: %w", err) + return fmt.Errorf("field Contract: %w", err) } index++ @@ -1259,11 +1399,11 @@ func itemToManagementPermission(item stackitem.Item, err error) (*ManagementPerm return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Methods: %w", err) + return fmt.Errorf("field Methods: %w", err) } - return res, err + return nil } // itemToStructsInternal converts stack item into *StructsInternal. @@ -1271,32 +1411,42 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er if err != nil { return nil, err } + var res = new(StructsInternal) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of StructsInternal from the given stack item +// and returns an error if so. +func (res *StructsInternal) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != 13 { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new(StructsInternal) - var index = -1 + var ( + index = -1 + err error + ) index++ res.Bool, err = arr[index].TryBool() if err != nil { - return nil, fmt.Errorf("field Bool: %w", err) + return fmt.Errorf("field Bool: %w", err) } index++ res.Int, err = arr[index].TryInteger() if err != nil { - return nil, fmt.Errorf("field Int: %w", err) + return fmt.Errorf("field Int: %w", err) } index++ res.Bytes, err = arr[index].TryBytes() if err != nil { - return nil, fmt.Errorf("field Bytes: %w", err) + return fmt.Errorf("field Bytes: %w", err) } index++ @@ -1311,7 +1461,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return string(b), nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field String: %w", err) + return fmt.Errorf("field String: %w", err) } index++ @@ -1327,7 +1477,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field H160: %w", err) + return fmt.Errorf("field H160: %w", err) } index++ @@ -1343,7 +1493,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return u, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field H256: %w", err) + return fmt.Errorf("field H256: %w", err) } index++ @@ -1359,7 +1509,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return k, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field PK: %w", err) + return fmt.Errorf("field PK: %w", err) } index++ @@ -1375,13 +1525,13 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return k, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field PubKey: %w", err) + return fmt.Errorf("field PubKey: %w", err) } index++ res.Sign, err = arr[index].TryBytes() if err != nil { - return nil, fmt.Errorf("field Sign: %w", err) + return fmt.Errorf("field Sign: %w", err) } index++ @@ -1400,7 +1550,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field ArrOfBytes: %w", err) + return fmt.Errorf("field ArrOfBytes: %w", err) } index++ @@ -1429,7 +1579,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field ArrOfH160: %w", err) + return fmt.Errorf("field ArrOfH160: %w", err) } index++ @@ -1476,15 +1626,15 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return res, nil } (arr[index]) if err != nil { - return nil, fmt.Errorf("field Map: %w", err) + return fmt.Errorf("field Map: %w", err) } index++ res.Struct, err = itemToStructsInternal(arr[index], nil) if err != nil { - return nil, fmt.Errorf("field Struct: %w", err) + return fmt.Errorf("field Struct: %w", err) } - return res, err + return nil } diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index 3db076bd7..310cee233 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -221,25 +221,35 @@ func itemTo{{toTypeName $name}}(item stackitem.Item, err error) (*{{toTypeName $ if err != nil { return nil, err } + var res = new({{toTypeName $name}}) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of {{toTypeName $name}} from the given stack item +// and returns an error if so. +func (res *{{toTypeName $name}}) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { - return nil, errors.New("not an array") + return errors.New("not an array") } if len(arr) != {{len $typ.Fields}} { - return nil, errors.New("wrong number of structure elements") + return errors.New("wrong number of structure elements") } - var res = new({{toTypeName $name}}) -{{if len .Fields}} var index = -1 +{{if len .Fields}} var ( + index = -1 + err error + ) {{- range $m := $typ.Fields}} index++ res.{{.Field}}, err = {{etTypeConverter .ExtendedType "arr[index]"}} if err != nil { - return nil, fmt.Errorf("field {{.Field}}: %w", err) + return fmt.Errorf("field {{.Field}}: %w", err) } {{end}} {{end}} - return res, err + return nil } {{end}}` From 8beb9f23c3471e4e8d91c85a4de5c12b8eb2618a Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 23 May 2023 12:18:26 +0300 Subject: [PATCH 04/22] smartcontract: add comments to binding config fields Signed-off-by: Anna Shaleva --- pkg/smartcontract/binding/generate.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pkg/smartcontract/binding/generate.go b/pkg/smartcontract/binding/generate.go index c02bbd49a..3f9bca863 100644 --- a/pkg/smartcontract/binding/generate.go +++ b/pkg/smartcontract/binding/generate.go @@ -48,14 +48,22 @@ 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"` - NamedTypes map[string]ExtendedType `yaml:"namedtypes,omitempty"` - Types map[string]ExtendedType `yaml:"types,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 contains exported structured types that have some name (even + // if the original structure doesn't) and a number of internal fields. The + // map key is in the form of `namespace.name`, the value is fully-qualified + // and possibly nested description of the type structure. + NamedTypes map[string]ExtendedType `yaml:"namedtypes,omitempty"` + // Types contains type structure description for various types used in + // smartcontract. The map key has one of the following forms: + // - `methodName` for method return value; + // - `mathodName.paramName` for method's parameter value. + Types map[string]ExtendedType `yaml:"types,omitempty"` + Output io.Writer `yaml:"-"` } ExtendedType struct { From 0f0f7b364fcda76c52b9d94dde17cf8c80e45453 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 3 May 2023 19:18:43 +0300 Subject: [PATCH 05/22] rpcbinding: use typed return err value in etTypeConverter Otherwise the resulting code can't be compiled. Signed-off-by: Anna Shaleva --- cli/smartcontract/testdata/structs/rpcbindings.out | 4 ++-- cli/smartcontract/testdata/types/rpcbindings.out | 2 +- pkg/smartcontract/rpcbinding/binding.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/smartcontract/testdata/structs/rpcbindings.out b/cli/smartcontract/testdata/structs/rpcbindings.out index 039b15e7e..8b165ec20 100644 --- a/cli/smartcontract/testdata/structs/rpcbindings.out +++ b/cli/smartcontract/testdata/structs/rpcbindings.out @@ -709,7 +709,7 @@ func (res *LedgerWitnessCondition) FromStackItem(item stackitem.Item) error { } index++ - res.Value, err = arr[index].Value(), nil + res.Value, err = arr[index].Value(), error(nil) if err != nil { return fmt.Errorf("field Value: %w", err) } @@ -1193,7 +1193,7 @@ func (res *ManagementManifest) FromStackItem(item stackitem.Item) error { } index++ - res.Extra, err = arr[index].Value(), nil + res.Extra, err = arr[index].Value(), error(nil) if err != nil { return fmt.Errorf("field Extra: %w", err) } diff --git a/cli/smartcontract/testdata/types/rpcbindings.out b/cli/smartcontract/testdata/types/rpcbindings.out index e6a14ecea..94bf63910 100644 --- a/cli/smartcontract/testdata/types/rpcbindings.out +++ b/cli/smartcontract/testdata/types/rpcbindings.out @@ -96,7 +96,7 @@ func (c *ContractReader) Any(a any) (any, error) { if err != nil { return nil, err } - return item.Value(), nil + return item.Value(), error(nil) } (unwrap.Item(c.invoker.Call(Hash, "any", a))) } diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index 310cee233..6443f5c64 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -412,7 +412,7 @@ func extendedTypeToGo(et binding.ExtendedType, named map[string]binding.Extended func etTypeConverter(et binding.ExtendedType, v string) string { switch et.Base { case smartcontract.AnyType: - return v + ".Value(), nil" + return v + ".Value(), error(nil)" case smartcontract.BoolType: return v + ".TryBool()" case smartcontract.IntegerType: From ae52b2c2fa8340552dbdc6025ae1cc67ea1b2d78 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 24 May 2023 11:44:31 +0300 Subject: [PATCH 06/22] rpcbinding: fix binding indentation New rule for writing blocks of code to our template: new line before the block starts and new line after the block ends. This rule is the same as the one we use during manual typing. Signed-off-by: Anna Shaleva --- cli/smartcontract/generate_test.go | 1 - cli/smartcontract/testdata/gas/gas.go | 1 - cli/smartcontract/testdata/nameservice/nns.go | 1 - cli/smartcontract/testdata/nex/nex.go | 1 - cli/smartcontract/testdata/nonepiter/iter.go | 1 - .../testdata/structs/rpcbindings.out | 18 +---- .../testdata/types/rpcbindings.out | 1 - .../testdata/verifyrpc/verify.go | 1 - pkg/smartcontract/rpcbinding/binding.go | 79 ++++++++++--------- 9 files changed, 42 insertions(+), 62 deletions(-) diff --git a/cli/smartcontract/generate_test.go b/cli/smartcontract/generate_test.go index 6756adb82..b274e99f3 100644 --- a/cli/smartcontract/generate_test.go +++ b/cli/smartcontract/generate_test.go @@ -316,7 +316,6 @@ func NewReader(invoker Invoker) *ContractReader { return &ContractReader{invoker} } - // Get invokes `+"`get`"+` method of contract. func (c *ContractReader) Get() (*big.Int, error) { return unwrap.BigInt(c.invoker.Call(Hash, "get")) diff --git a/cli/smartcontract/testdata/gas/gas.go b/cli/smartcontract/testdata/gas/gas.go index d3c938c29..d12bbb8ee 100644 --- a/cli/smartcontract/testdata/gas/gas.go +++ b/cli/smartcontract/testdata/gas/gas.go @@ -44,4 +44,3 @@ func New(actor Actor) *Contract { var nep17t = nep17.New(actor, Hash) return &Contract{ContractReader{nep17t.TokenReader, actor}, nep17t.TokenWriter, actor} } - diff --git a/cli/smartcontract/testdata/nameservice/nns.go b/cli/smartcontract/testdata/nameservice/nns.go index efdfcabfe..c279951ea 100644 --- a/cli/smartcontract/testdata/nameservice/nns.go +++ b/cli/smartcontract/testdata/nameservice/nns.go @@ -59,7 +59,6 @@ func New(actor Actor) *Contract { return &Contract{ContractReader{nep11ndt.NonDivisibleReader, actor}, nep11ndt.BaseWriter, actor} } - // Roots invokes `roots` method of contract. func (c *ContractReader) Roots() (uuid.UUID, result.Iterator, error) { return unwrap.SessionIterator(c.invoker.Call(Hash, "roots")) diff --git a/cli/smartcontract/testdata/nex/nex.go b/cli/smartcontract/testdata/nex/nex.go index 4a4a46de4..068fc1987 100644 --- a/cli/smartcontract/testdata/nex/nex.go +++ b/cli/smartcontract/testdata/nex/nex.go @@ -56,7 +56,6 @@ func New(actor Actor) *Contract { return &Contract{ContractReader{nep17t.TokenReader, actor}, nep17t.TokenWriter, actor} } - // Cap invokes `cap` method of contract. func (c *ContractReader) Cap() (*big.Int, error) { return unwrap.BigInt(c.invoker.Call(Hash, "cap")) diff --git a/cli/smartcontract/testdata/nonepiter/iter.go b/cli/smartcontract/testdata/nonepiter/iter.go index e04f6953c..f298ab8de 100644 --- a/cli/smartcontract/testdata/nonepiter/iter.go +++ b/cli/smartcontract/testdata/nonepiter/iter.go @@ -30,7 +30,6 @@ func NewReader(invoker Invoker) *ContractReader { return &ContractReader{invoker} } - // Tokens invokes `tokens` method of contract. func (c *ContractReader) Tokens() (uuid.UUID, result.Iterator, error) { return unwrap.SessionIterator(c.invoker.Call(Hash, "tokens")) diff --git a/cli/smartcontract/testdata/structs/rpcbindings.out b/cli/smartcontract/testdata/structs/rpcbindings.out index 8b165ec20..29af22323 100644 --- a/cli/smartcontract/testdata/structs/rpcbindings.out +++ b/cli/smartcontract/testdata/structs/rpcbindings.out @@ -17,7 +17,6 @@ import ( // Hash contains contract hash. var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0} - // LedgerBlock is a contract-specific ledger.Block type used by its methods. type LedgerBlock struct { Hash util.Uint256 @@ -154,6 +153,7 @@ type StructsInternal struct { Map map[*big.Int]keys.PublicKeys Struct *StructsInternal } + // Invoker is used by ContractReader to call various safe methods. type Invoker interface { Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) @@ -169,7 +169,6 @@ 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))) @@ -309,7 +308,6 @@ func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field TransactionsLength: %w", err) } - return nil } @@ -448,7 +446,6 @@ func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field PrevStateRoot: %w", err) } - return nil } @@ -545,7 +542,6 @@ func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field Script: %w", err) } - return nil } @@ -673,7 +669,6 @@ func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field Rules: %w", err) } - return nil } @@ -714,7 +709,6 @@ func (res *LedgerWitnessCondition) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field Value: %w", err) } - return nil } @@ -755,7 +749,6 @@ func (res *LedgerWitnessRule) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field Condition: %w", err) } - return nil } @@ -822,7 +815,6 @@ func (res *ManagementABI) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field Events: %w", err) } - return nil } @@ -891,7 +883,6 @@ func (res *ManagementContract) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field Manifest: %w", err) } - return nil } @@ -954,7 +945,6 @@ func (res *ManagementEvent) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field Params: %w", err) } - return nil } @@ -1005,7 +995,6 @@ func (res *ManagementGroup) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field Signature: %w", err) } - return nil } @@ -1198,7 +1187,6 @@ func (res *ManagementManifest) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field Extra: %w", err) } - return nil } @@ -1279,7 +1267,6 @@ func (res *ManagementMethod) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field Safe: %w", err) } - return nil } @@ -1329,7 +1316,6 @@ func (res *ManagementParameter) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field Type: %w", err) } - return nil } @@ -1402,7 +1388,6 @@ func (res *ManagementPermission) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field Methods: %w", err) } - return nil } @@ -1635,6 +1620,5 @@ func (res *StructsInternal) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field Struct: %w", err) } - return nil } diff --git a/cli/smartcontract/testdata/types/rpcbindings.out b/cli/smartcontract/testdata/types/rpcbindings.out index 94bf63910..ca4f0ce36 100644 --- a/cli/smartcontract/testdata/types/rpcbindings.out +++ b/cli/smartcontract/testdata/types/rpcbindings.out @@ -31,7 +31,6 @@ func NewReader(invoker Invoker) *ContractReader { return &ContractReader{invoker} } - // AAAStrings invokes `aAAStrings` method of contract. func (c *ContractReader) AAAStrings(s [][][]string) ([][][]string, error) { return func (item stackitem.Item, err error) ([][][]string, error) { diff --git a/cli/smartcontract/testdata/verifyrpc/verify.go b/cli/smartcontract/testdata/verifyrpc/verify.go index 4a9af0156..a614c36cf 100644 --- a/cli/smartcontract/testdata/verifyrpc/verify.go +++ b/cli/smartcontract/testdata/verifyrpc/verify.go @@ -30,7 +30,6 @@ func New(actor Actor) *Contract { return &Contract{actor} } - func scriptForVerify() ([]byte, error) { return smartcontract.CreateCallWithAssertScript(Hash, "verify") } diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index 6443f5c64..58ed76240 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -12,8 +12,12 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest/standard" ) -const srcTmpl = ` -{{- define "SAFEMETHOD" -}} +// The set of constants containing parts of RPC binding template. Each block of code +// including template definition and var/type/method definitions contain new line at the +// start and ends with a new line. On adding new block of code to the template, please, +// ensure that this block has new line at the start and in the end of the block. +const ( + safemethodDefinition = `{{ define "SAFEMETHOD" }} // {{.Name}} {{.Comment}} func (c *ContractReader) {{.Name}}({{range $index, $arg := .Arguments -}} {{- if ne $index 0}}, {{end}} @@ -32,8 +36,7 @@ func (c *ContractReader) {{.Name}}({{range $index, $arg := .Arguments -}} {{- range $arg := .Arguments -}}, {{.Name}}{{end}}) {{- end}} } -{{- if eq .Unwrapper "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 // doesn't expand iterators. It creates a script that will get the specified @@ -42,17 +45,16 @@ func (c *ContractReader) {{.Name}}({{range $index, $arg := .Arguments -}} func (c *ContractReader) {{.Name}}Expanded({{range $index, $arg := .Arguments}}{{.Name}} {{.Type}}, {{end}}_numOfIteratorItems int) ([]stackitem.Item, error) { return unwrap.Array(c.invoker.CallAndExpandIterator(Hash, "{{.NameABI}}", _numOfIteratorItems{{range $arg := .Arguments}}, {{.Name}}{{end}})) } -{{- end -}} -{{- end -}} -{{- define "METHOD" -}} -{{- if eq .ReturnType "bool"}}func scriptFor{{.Name}}({{range $index, $arg := .Arguments -}} +{{ end }}{{ end }}` + methodDefinition = `{{ define "METHOD" }}{{ if eq .ReturnType "bool"}} +func scriptFor{{.Name}}({{range $index, $arg := .Arguments -}} {{- if ne $index 0}}, {{end}} {{- .Name}} {{.Type}} {{- end}}) ([]byte, error) { return smartcontract.CreateCallWithAssertScript(Hash, "{{ .NameABI }}"{{- range $index, $arg := .Arguments -}}, {{.Name}}{{end}}) } - -{{end}}// {{.Name}} {{.Comment}} +{{ end }} +// {{.Name}} {{.Comment}} // 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) {{.Name}}({{range $index, $arg := .Arguments -}} @@ -97,8 +99,9 @@ func (c *Contract) {{.Name}}Unsigned({{range $index, $arg := .Arguments -}} } return c.actor.MakeUnsignedRun(script, nil){{end}} } -{{- end -}} -// Package {{.PackageName}} contains RPC wrappers for {{.ContractName}} contract. +{{end}}` + + bindingDefinition = `// Package {{.PackageName}} contains RPC wrappers for {{.ContractName}} contract. package {{.PackageName}} import ( @@ -107,16 +110,16 @@ import ( // Hash contains contract hash. var Hash = {{ .Hash }} - -{{range $name, $typ := .NamedTypes}} +{{ 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. +{{end}} +{{- if .HasReader}} +// Invoker is used by ContractReader to call various safe methods. type Invoker interface { {{if or .IsNep11D .IsNep11ND}} nep11.Invoker {{else -}} @@ -129,9 +132,9 @@ type Invoker interface { {{end -}} {{end -}} } - {{end -}} -{{if .HasWriter}}// Actor is used by Contract to call state-changing methods. +{{- if .HasWriter}} +// Actor is used by Contract to call state-changing methods. type Actor interface { {{- if .HasReader}} Invoker @@ -150,9 +153,9 @@ type Actor interface { SendRun(script []byte) (util.Uint256, uint32, error) {{end -}} } - {{end -}} -{{if .HasReader}}// ContractReader implements safe contract methods. +{{- if .HasReader}} +// ContractReader implements safe contract methods. type ContractReader struct { {{if .IsNep11D}}nep11.DivisibleReader {{end -}} @@ -162,9 +165,9 @@ type ContractReader struct { {{end -}} invoker Invoker } - {{end -}} -{{if .HasWriter}}// Contract implements all contract methods. +{{- if .HasWriter}} +// Contract implements all contract methods. type Contract struct { {{if .HasReader}}ContractReader {{end -}} @@ -176,9 +179,9 @@ type Contract struct { {{end -}} actor Actor } - {{end -}} -{{if .HasReader}}// NewReader creates an instance of ContractReader using Hash and the given Invoker. +{{- if .HasReader}} +// NewReader creates an instance of ContractReader using Hash and the given Invoker. func NewReader(invoker Invoker) *ContractReader { return &ContractReader{ {{- if .IsNep11D}}*nep11.NewDivisibleReader(invoker, Hash), {{end}} @@ -186,9 +189,9 @@ func NewReader(invoker Invoker) *ContractReader { {{- if .IsNep17}}*nep17.NewReader(invoker, Hash), {{end -}} invoker} } - {{end -}} -{{if .HasWriter}}// New creates an instance of Contract using Hash and the given Actor. +{{- if .HasWriter}} +// New creates an instance of Contract using Hash and the given Actor. func New(actor Actor) *Contract { {{if .IsNep11D}}var nep11dt = nep11.NewDivisible(actor, Hash) {{end -}} @@ -207,15 +210,10 @@ func New(actor Actor) *Contract { {{- if .IsNep17}}nep17t.TokenWriter, {{end -}} actor} } - {{end -}} -{{range $m := .SafeMethods}} -{{template "SAFEMETHOD" $m }} -{{end}} -{{- range $m := .Methods}} -{{template "METHOD" $m }} -{{end}} -{{- range $name, $typ := .NamedTypes}} +{{- range $m := .SafeMethods }}{{template "SAFEMETHOD" $m }}{{ end -}} +{{- range $m := .Methods -}}{{template "METHOD" $m }}{{ end -}} +{{- range $name, $typ := .NamedTypes }} // itemTo{{toTypeName $name}} converts stack item into *{{toTypeName $name}}. func itemTo{{toTypeName $name}}(item stackitem.Item, err error) (*{{toTypeName $name}}, error) { if err != nil { @@ -236,8 +234,8 @@ func (res *{{toTypeName $name}}) FromStackItem(item stackitem.Item) error { if len(arr) != {{len $typ.Fields}} { return errors.New("wrong number of structure elements") } - -{{if len .Fields}} var ( +{{if len .Fields}} + var ( index = -1 err error ) @@ -248,10 +246,15 @@ func (res *{{toTypeName $name}}) FromStackItem(item stackitem.Item) error { return fmt.Errorf("field {{.Field}}: %w", err) } {{end}} -{{end}} +{{- end}} return nil } -{{end}}` +{{end -}}` + + srcTmpl = bindingDefinition + + safemethodDefinition + + methodDefinition +) type ( ContractTmpl struct { From 044ae477ca08ada8879cf438cc53b5c8463ca711 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 24 May 2023 11:52:14 +0300 Subject: [PATCH 07/22] smartconract: generate RPC binding wrappers for events Signed-off-by: Anna Shaleva --- cli/smartcontract/testdata/gas/gas.go | 96 ++++++ cli/smartcontract/testdata/nameservice/nns.go | 280 ++++++++++++++++++ cli/smartcontract/testdata/nex/nex.go | 193 ++++++++++++ .../testdata/verifyrpc/verify.go | 74 +++++ pkg/compiler/codegen.go | 4 +- pkg/compiler/compiler.go | 37 ++- pkg/compiler/debug.go | 19 +- pkg/compiler/inline.go | 62 ++-- pkg/smartcontract/binding/generate.go | 1 + pkg/smartcontract/rpcbinding/binding.go | 174 ++++++++++- 10 files changed, 896 insertions(+), 44 deletions(-) diff --git a/cli/smartcontract/testdata/gas/gas.go b/cli/smartcontract/testdata/gas/gas.go index d12bbb8ee..2b4cf920a 100644 --- a/cli/smartcontract/testdata/gas/gas.go +++ b/cli/smartcontract/testdata/gas/gas.go @@ -2,13 +2,25 @@ package gastoken import ( + "errors" + "fmt" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "math/big" ) // Hash contains contract hash. var Hash = util.Uint160{0xcf, 0x76, 0xe2, 0x8b, 0xd0, 0x6, 0x2c, 0x4a, 0x47, 0x8e, 0xe3, 0x55, 0x61, 0x1, 0x13, 0x19, 0xf3, 0xcf, 0xa4, 0xd2} +// TransferEvent represents "Transfer" event emitted by the contract. +type TransferEvent struct { + From util.Uint160 + To util.Uint160 + Amount *big.Int +} + // Invoker is used by ContractReader to call various safe methods. type Invoker interface { nep17.Invoker @@ -44,3 +56,87 @@ func New(actor Actor) *Contract { var nep17t = nep17.New(actor, Hash) return &Contract{ContractReader{nep17t.TokenReader, actor}, nep17t.TokenWriter, actor} } + +// TransferEventsFromApplicationLog retrieves a set of all emitted events +// with "Transfer" name from the provided ApplicationLog. +func TransferEventsFromApplicationLog(log *result.ApplicationLog) ([]*TransferEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*TransferEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "Transfer" { + continue + } + event := new(TransferEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize TransferEvent from stackitem (execution %d, event %d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided stackitem.Array to TransferEvent and +// returns an error if so. +func (e *TransferEvent) 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) != 3 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.From, 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 From: %w", err) + } + + index++ + e.To, 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 To: %w", err) + } + + index++ + e.Amount, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Amount: %w", err) + } + + return nil +} diff --git a/cli/smartcontract/testdata/nameservice/nns.go b/cli/smartcontract/testdata/nameservice/nns.go index c279951ea..8bb311bb9 100644 --- a/cli/smartcontract/testdata/nameservice/nns.go +++ b/cli/smartcontract/testdata/nameservice/nns.go @@ -2,6 +2,8 @@ package nameservice import ( + "errors" + "fmt" "github.com/google/uuid" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/neorpc/result" @@ -16,6 +18,28 @@ import ( // Hash contains contract hash. var Hash = util.Uint160{0xde, 0x46, 0x5f, 0x5d, 0x50, 0x57, 0xcf, 0x33, 0x28, 0x47, 0x94, 0xc5, 0xcf, 0xc2, 0xc, 0x69, 0x37, 0x1c, 0xac, 0x50} +// TransferEvent represents "Transfer" event emitted by the contract. +type TransferEvent struct { + From util.Uint160 + To util.Uint160 + Amount *big.Int + TokenId []byte +} + +// SetAdminEvent represents "SetAdmin" event emitted by the contract. +type SetAdminEvent struct { + Name string + OldAdmin util.Uint160 + NewAdmin util.Uint160 +} + +// RenewEvent represents "Renew" event emitted by the contract. +type RenewEvent struct { + Name string + OldExpiration *big.Int + NewExpiration *big.Int +} + // Invoker is used by ContractReader to call various safe methods. type Invoker interface { nep11.Invoker @@ -320,3 +344,259 @@ func (c *Contract) DeleteRecordTransaction(name string, typev *big.Int) (*transa func (c *Contract) DeleteRecordUnsigned(name string, typev *big.Int) (*transaction.Transaction, error) { return c.actor.MakeUnsignedCall(Hash, "deleteRecord", nil, name, typev) } + +// TransferEventsFromApplicationLog retrieves a set of all emitted events +// with "Transfer" name from the provided ApplicationLog. +func TransferEventsFromApplicationLog(log *result.ApplicationLog) ([]*TransferEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*TransferEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "Transfer" { + continue + } + event := new(TransferEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize TransferEvent from stackitem (execution %d, event %d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided stackitem.Array to TransferEvent and +// returns an error if so. +func (e *TransferEvent) 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) != 4 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.From, 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 From: %w", err) + } + + index++ + e.To, 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 To: %w", err) + } + + index++ + e.Amount, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Amount: %w", err) + } + + index++ + e.TokenId, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field TokenId: %w", err) + } + + return nil +} + +// SetAdminEventsFromApplicationLog retrieves a set of all emitted events +// with "SetAdmin" name from the provided ApplicationLog. +func SetAdminEventsFromApplicationLog(log *result.ApplicationLog) ([]*SetAdminEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*SetAdminEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "SetAdmin" { + continue + } + event := new(SetAdminEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize SetAdminEvent from stackitem (execution %d, event %d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided stackitem.Array to SetAdminEvent and +// returns an error if so. +func (e *SetAdminEvent) 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) != 3 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.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 fmt.Errorf("field Name: %w", err) + } + + index++ + e.OldAdmin, 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 OldAdmin: %w", err) + } + + index++ + e.NewAdmin, 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 NewAdmin: %w", err) + } + + return nil +} + +// RenewEventsFromApplicationLog retrieves a set of all emitted events +// with "Renew" name from the provided ApplicationLog. +func RenewEventsFromApplicationLog(log *result.ApplicationLog) ([]*RenewEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*RenewEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "Renew" { + continue + } + event := new(RenewEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize RenewEvent from stackitem (execution %d, event %d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided stackitem.Array to RenewEvent and +// returns an error if so. +func (e *RenewEvent) 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) != 3 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.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 fmt.Errorf("field Name: %w", err) + } + + index++ + e.OldExpiration, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field OldExpiration: %w", err) + } + + index++ + e.NewExpiration, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field NewExpiration: %w", err) + } + + return nil +} diff --git a/cli/smartcontract/testdata/nex/nex.go b/cli/smartcontract/testdata/nex/nex.go index 068fc1987..41de92d02 100644 --- a/cli/smartcontract/testdata/nex/nex.go +++ b/cli/smartcontract/testdata/nex/nex.go @@ -2,17 +2,36 @@ package nextoken import ( + "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/rpcclient/nep17" "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" ) // Hash contains contract hash. var Hash = util.Uint160{0xa8, 0x1a, 0xa1, 0xf0, 0x4b, 0xf, 0xdc, 0x4a, 0xa2, 0xce, 0xd5, 0xbf, 0xc6, 0x22, 0xcf, 0xe8, 0x9, 0x7f, 0xa6, 0xa2} +// TransferEvent represents "Transfer" event emitted by the contract. +type TransferEvent struct { + From util.Uint160 + To util.Uint160 + Amount *big.Int +} + +// OnMintEvent represents "OnMint" event emitted by the contract. +type OnMintEvent struct { + From util.Uint160 + To util.Uint160 + Amount *big.Int + SwapId *big.Int +} + // Invoker is used by ContractReader to call various safe methods. type Invoker interface { nep17.Invoker @@ -229,3 +248,177 @@ func (c *Contract) UpdateCapTransaction(newCap *big.Int) (*transaction.Transacti func (c *Contract) UpdateCapUnsigned(newCap *big.Int) (*transaction.Transaction, error) { return c.actor.MakeUnsignedCall(Hash, "updateCap", nil, newCap) } + +// TransferEventsFromApplicationLog retrieves a set of all emitted events +// with "Transfer" name from the provided ApplicationLog. +func TransferEventsFromApplicationLog(log *result.ApplicationLog) ([]*TransferEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*TransferEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "Transfer" { + continue + } + event := new(TransferEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize TransferEvent from stackitem (execution %d, event %d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided stackitem.Array to TransferEvent and +// returns an error if so. +func (e *TransferEvent) 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) != 3 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.From, 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 From: %w", err) + } + + index++ + e.To, 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 To: %w", err) + } + + index++ + e.Amount, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Amount: %w", err) + } + + return nil +} + +// OnMintEventsFromApplicationLog retrieves a set of all emitted events +// with "OnMint" name from the provided ApplicationLog. +func OnMintEventsFromApplicationLog(log *result.ApplicationLog) ([]*OnMintEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*OnMintEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "OnMint" { + continue + } + event := new(OnMintEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize OnMintEvent from stackitem (execution %d, event %d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided stackitem.Array to OnMintEvent and +// returns an error if so. +func (e *OnMintEvent) 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) != 4 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + e.From, 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 From: %w", err) + } + + index++ + e.To, 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 To: %w", err) + } + + index++ + e.Amount, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Amount: %w", err) + } + + index++ + e.SwapId, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field SwapId: %w", err) + } + + return nil +} diff --git a/cli/smartcontract/testdata/verifyrpc/verify.go b/cli/smartcontract/testdata/verifyrpc/verify.go index a614c36cf..6835a7e4e 100644 --- a/cli/smartcontract/testdata/verifyrpc/verify.go +++ b/cli/smartcontract/testdata/verifyrpc/verify.go @@ -2,14 +2,23 @@ package verify import ( + "errors" + "fmt" "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) // 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} +// HelloWorldEvent represents "Hello world!" event emitted by the contract. +type HelloWorldEvent struct { + Args []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) @@ -67,3 +76,68 @@ func (c *Contract) VerifyUnsigned() (*transaction.Transaction, error) { } return c.actor.MakeUnsignedRun(script, nil) } + +// HelloWorldEventsFromApplicationLog retrieves a set of all emitted events +// with "Hello world!" name from the provided ApplicationLog. +func HelloWorldEventsFromApplicationLog(log *result.ApplicationLog) ([]*HelloWorldEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*HelloWorldEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "Hello world!" { + continue + } + event := new(HelloWorldEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize HelloWorldEvent from stackitem (execution %d, event %d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided stackitem.Array to HelloWorldEvent and +// returns an error if so. +func (e *HelloWorldEvent) 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.Args, 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 Args: %w", err) + } + + return nil +} diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 14793c8ad..4ee702626 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -110,7 +110,7 @@ type codegen struct { docIndex map[string]int // emittedEvents contains all events emitted by the contract. - emittedEvents map[string][][]string + emittedEvents map[string][]EmittedEventInfo // invokedContracts contains invoked methods of other contracts. invokedContracts map[util.Uint160][]string @@ -2269,7 +2269,7 @@ func newCodegen(info *buildInfo, pkg *packages.Package) *codegen { initEndOffset: -1, deployEndOffset: -1, - emittedEvents: make(map[string][][]string), + emittedEvents: make(map[string][]EmittedEventInfo), invokedContracts: make(map[util.Uint160][]string), sequencePoints: make(map[string][]DebugSeqPoint), } diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 60a45525c..aec697006 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -309,6 +309,22 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { if len(di.NamedTypes) > 0 { cfg.NamedTypes = di.NamedTypes } + if len(di.EmittedEvents) > 0 { + for eventName, eventUsages := range di.EmittedEvents { + for typeName, extType := range eventUsages[0].ExtTypes { + cfg.NamedTypes[typeName] = extType + } + for _, p := range eventUsages[0].Params { + pname := eventName + "." + p.Name + if p.RealType.TypeName != "" { + cfg.Overrides[pname] = p.RealType + } + if p.ExtendedType != nil { + cfg.Types[pname] = *p.ExtendedType + } + } + } + } data, err := yaml.Marshal(&cfg) if err != nil { return nil, fmt.Errorf("can't marshal bindings configuration: %w", err) @@ -366,24 +382,23 @@ func CreateManifest(di *DebugInfo, o *Options) (*manifest.Manifest, error) { } if !o.NoEventsCheck { for name := range di.EmittedEvents { - ev := m.ABI.GetEvent(name) - if ev == nil { + expected := m.ABI.GetEvent(name) + if expected == nil { return nil, fmt.Errorf("event '%s' is emitted but not specified in manifest", name) } - argsList := di.EmittedEvents[name] - for i := range argsList { - if len(argsList[i]) != len(ev.Parameters) { + for _, emitted := range di.EmittedEvents[name] { + if len(emitted.Params) != len(expected.Parameters) { return nil, fmt.Errorf("event '%s' should have %d parameters but has %d", - name, len(ev.Parameters), len(argsList[i])) + name, len(expected.Parameters), len(emitted.Params)) } - for j := range ev.Parameters { - if ev.Parameters[j].Type == smartcontract.AnyType { + for j := range expected.Parameters { + if expected.Parameters[j].Type == smartcontract.AnyType { continue } - expected := ev.Parameters[j].Type.String() - if argsList[i][j] != expected { + expectedT := expected.Parameters[j].Type + if emitted.Params[j].TypeSC != expectedT { return nil, fmt.Errorf("event '%s' should have '%s' as type of %d parameter, "+ - "got: %s", name, expected, j+1, argsList[i][j]) + "got: %s", name, expectedT, j+1, emitted.Params[j].TypeSC) } } } diff --git a/pkg/compiler/debug.go b/pkg/compiler/debug.go index 82e2e49f7..88fcbb119 100644 --- a/pkg/compiler/debug.go +++ b/pkg/compiler/debug.go @@ -29,9 +29,14 @@ type DebugInfo struct { // 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:"-"` + // Events are the events that contract is allowed to emit and that have to + // be presented in the resulting contract manifest and debug info file. + Events []EventDebugInfo `json:"events"` + // EmittedEvents contains events occurring in code, i.e. events emitted + // via runtime.Notify(...) call in the contract code if they have constant + // names and doesn't have ellipsis arguments. EmittedEvents are not related + // to the debug info and are aimed to serve bindings generation. + EmittedEvents map[string][]EmittedEventInfo `json:"-"` // InvokedContracts contains foreign contract invocations. InvokedContracts map[util.Uint160][]string `json:"-"` // StaticVariables contains a list of static variable names and types. @@ -112,6 +117,14 @@ type DebugParam struct { TypeSC smartcontract.ParamType `json:"-"` } +// EmittedEventInfo describes information about single emitted event got from +// the contract code. It has the map of extended types used as the parameters to +// runtime.Notify(...) call (if any) and the parameters info itself. +type EmittedEventInfo struct { + ExtTypes map[string]binding.ExtendedType + Params []DebugParam +} + func (c *codegen) saveSequencePoint(n ast.Node) { name := "init" if c.scope != nil { diff --git a/pkg/compiler/inline.go b/pkg/compiler/inline.go index 1653d8638..b6cd03d3b 100644 --- a/pkg/compiler/inline.go +++ b/pkg/compiler/inline.go @@ -7,6 +7,7 @@ import ( "go/types" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/binding" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" @@ -172,11 +173,21 @@ func (c *codegen) processNotify(f *funcScope, args []ast.Expr, hasEllipsis bool) return nil } - params := make([]string, 0, len(args[1:])) + params := make([]DebugParam, 0, len(args[1:])) vParams := make([]*stackitem.Type, 0, len(args[1:])) + // extMap holds the extended parameter types used for the given event call. + // It will be unified with the common extMap later during bindings config + // generation. + extMap := make(map[string]binding.ExtendedType) for _, p := range args[1:] { - st, vt, _, _ := c.scAndVMTypeFromExpr(p, nil) - params = append(params, st.String()) + st, vt, over, extT := c.scAndVMTypeFromExpr(p, extMap) + params = append(params, DebugParam{ + Name: "", // Parameter name will be filled in several lines below if the corresponding event exists in the buildinfo.options. + Type: vt.String(), + RealType: over, + ExtendedType: extT, + TypeSC: st, + }) vParams = append(vParams, &vt) } @@ -187,36 +198,43 @@ func (c *codegen) processNotify(f *funcScope, args []ast.Expr, hasEllipsis bool) return nil } var eventFound bool - if c.buildInfo.options != nil && c.buildInfo.options.ContractEvents != nil && !c.buildInfo.options.NoEventsCheck { + if c.buildInfo.options != nil && c.buildInfo.options.ContractEvents != nil { for _, e := range c.buildInfo.options.ContractEvents { if e.Name == name && len(e.Parameters) == len(vParams) { eventFound = true for i, scParam := range e.Parameters { - expectedType := scParam.Type.ConvertToStackitemType() - // No need to cast if the desired type is unknown. - if expectedType == stackitem.AnyT || - // Do not cast if desired type is Interop, the actual type is likely to be Any, leave the resolving to runtime.Notify. - expectedType == stackitem.InteropT || - // No need to cast if actual parameter type matches the desired one. - *vParams[i] == expectedType || - // expectedType doesn't contain Buffer anyway, but if actual variable type is Buffer, - // then runtime.Notify will convert it to ByteArray automatically, thus no need to emit conversion code. - (*vParams[i] == stackitem.BufferT && expectedType == stackitem.ByteArrayT) { - vParams[i] = nil - } else { - // For other cases the conversion code will be emitted using vParams... - vParams[i] = &expectedType - // ...thus, update emitted notification info in advance. - params[i] = scParam.Type.String() + params[i].Name = scParam.Name + if !c.buildInfo.options.NoEventsCheck { + expectedType := scParam.Type.ConvertToStackitemType() + // No need to cast if the desired type is unknown. + if expectedType == stackitem.AnyT || + // Do not cast if desired type is Interop, the actual type is likely to be Any, leave the resolving to runtime.Notify. + expectedType == stackitem.InteropT || + // No need to cast if actual parameter type matches the desired one. + *vParams[i] == expectedType || + // expectedType doesn't contain Buffer anyway, but if actual variable type is Buffer, + // then runtime.Notify will convert it to ByteArray automatically, thus no need to emit conversion code. + (*vParams[i] == stackitem.BufferT && expectedType == stackitem.ByteArrayT) { + vParams[i] = nil + } else { + // For other cases the conversion code will be emitted using vParams... + vParams[i] = &expectedType + // ...thus, update emitted notification info in advance. + params[i].Type = scParam.Type.String() + params[i].TypeSC = scParam.Type + } } } } } } - c.emittedEvents[name] = append(c.emittedEvents[name], params) + c.emittedEvents[name] = append(c.emittedEvents[name], EmittedEventInfo{ + ExtTypes: extMap, + Params: params, + }) // Do not enforce perfect expected/actual events match on this step, the final // check wil be performed after compilation if --no-events option is off. - if eventFound { + if eventFound && !c.buildInfo.options.NoEventsCheck { return vParams } return nil diff --git a/pkg/smartcontract/binding/generate.go b/pkg/smartcontract/binding/generate.go index 3f9bca863..bf3e3d9a1 100644 --- a/pkg/smartcontract/binding/generate.go +++ b/pkg/smartcontract/binding/generate.go @@ -62,6 +62,7 @@ type ( // smartcontract. The map key has one of the following forms: // - `methodName` for method return value; // - `mathodName.paramName` for method's parameter value. + // - `eventName.paramName` for event's parameter value. Types map[string]ExtendedType `yaml:"types,omitempty"` Output io.Writer `yaml:"-"` } diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index 58ed76240..973264c78 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -5,6 +5,7 @@ import ( "sort" "strings" "text/template" + "unicode" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/binding" @@ -17,6 +18,15 @@ import ( // start and ends with a new line. On adding new block of code to the template, please, // ensure that this block has new line at the start and in the end of the block. const ( + eventDefinition = `{{ define "EVENT" }} +// {{.Name}}Event represents "{{.ManifestName}}" event emitted by the contract. +type {{.Name}}Event struct { + {{- range $index, $arg := .Parameters}} + {{toPascalCase .Name}} {{.Type}} + {{- end}} +} +{{ end }}` + safemethodDefinition = `{{ define "SAFEMETHOD" }} // {{.Name}} {{.Comment}} func (c *ContractReader) {{.Name}}({{range $index, $arg := .Arguments -}} @@ -118,6 +128,7 @@ type {{toTypeName $name}} struct { {{- end}} } {{end}} +{{- range $e := .CustomEvents }}{{template "EVENT" $e }}{{ end -}} {{- if .HasReader}} // Invoker is used by ContractReader to call various safe methods. type Invoker interface { @@ -249,9 +260,65 @@ func (res *{{toTypeName $name}}) FromStackItem(item stackitem.Item) error { {{- end}} return nil } +{{ end -}} +{{- range $e := .CustomEvents }} +// {{$e.Name}}EventsFromApplicationLog retrieves a set of all emitted events +// with "{{$e.ManifestName}}" name from the provided ApplicationLog. +func {{$e.Name}}EventsFromApplicationLog(log *result.ApplicationLog) ([]*{{$e.Name}}Event, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*{{$e.Name}}Event + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "{{$e.ManifestName}}" { + continue + } + event := new({{$e.Name}}Event) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize {{$e.Name}}Event from stackitem (execution %d, event %d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided stackitem.Array to {{$e.Name}}Event and +// returns an error if so. +func (e *{{$e.Name}}Event) 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) != {{len $e.Parameters}} { + return errors.New("wrong number of structure elements") + } + + {{if len $e.Parameters}}var ( + index = -1 + err error + ) + {{- range $p := $e.Parameters}} + index++ + e.{{toPascalCase .Name}}, err = {{etTypeConverter .ExtType "arr[index]"}} + if err != nil { + return fmt.Errorf("field {{toPascalCase .Name}}: %w", err) + } +{{end}} +{{- end}} + return nil +} {{end -}}` srcTmpl = bindingDefinition + + eventDefinition + safemethodDefinition + methodDefinition ) @@ -260,8 +327,9 @@ type ( ContractTmpl struct { binding.ContractTmpl - SafeMethods []SafeMethodTmpl - NamedTypes map[string]binding.ExtendedType + SafeMethods []SafeMethodTmpl + CustomEvents []CustomEventTemplate + NamedTypes map[string]binding.ExtendedType IsNep11D bool IsNep11ND bool @@ -278,6 +346,25 @@ type ( ItemTo string ExtendedReturn binding.ExtendedType } + + CustomEventTemplate struct { + // Name is the event's name that will be used as the event structure name in + // the resulting RPC binding. It is a valid go structure name and may differ + // from ManifestName. + Name string + // ManifestName is the event's name declared in the contract manifest. + // It may contain any UTF8 character. + ManifestName string + Parameters []EventParamTmpl + } + + EventParamTmpl struct { + binding.ParamTmpl + + // ExtType holds the event parameter's type information provided by Manifest, + // i.e. simple types only. + ExtType binding.ExtendedType + } ) // NewConfig initializes and returns a new config instance. @@ -328,7 +415,7 @@ func Generate(cfg binding.Config) error { } ctr.ContractTmpl = binding.TemplateFromManifest(cfg, scTypeToGo) - ctr = scTemplateToRPC(cfg, ctr, imports) + ctr = scTemplateToRPC(cfg, ctr, imports, scTypeToGo) ctr.NamedTypes = cfg.NamedTypes var srcTemplate = template.Must(template.New("generate").Funcs(template.FuncMap{ @@ -338,8 +425,9 @@ func Generate(cfg binding.Config) error { r, _ := extendedTypeToGo(et, ctr.NamedTypes) return r }, - "toTypeName": toTypeName, - "cutPointer": cutPointer, + "toTypeName": toTypeName, + "cutPointer": cutPointer, + "toPascalCase": toPascalCase, }).Parse(srcTmpl)) return srcTemplate.Execute(cfg.Output, ctr) @@ -533,7 +621,7 @@ func scTypeToGo(name string, typ smartcontract.ParamType, cfg *binding.Config) ( return extendedTypeToGo(et, cfg.NamedTypes) } -func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]struct{}) ContractTmpl { +func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]struct{}, scTypeConverter func(string, smartcontract.ParamType, *binding.Config) (string, string)) ContractTmpl { for i := range ctr.Imports { imports[ctr.Imports[i]] = struct{}{} } @@ -564,6 +652,51 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st if len(cfg.NamedTypes) > 0 { imports["errors"] = struct{}{} } + for _, abiEvent := range cfg.Manifest.ABI.Events { + eTmp := CustomEventTemplate{ + // TODO: proper event name is better to be set right into config binding in normal form. + Name: toPascalCase(abiEvent.Name), + ManifestName: abiEvent.Name, + } + var varnames = make(map[string]bool) + for i := range abiEvent.Parameters { + name := abiEvent.Parameters[i].Name + fullPName := abiEvent.Name + "." + name + typeStr, pkg := scTypeConverter(fullPName, abiEvent.Parameters[i].Type, &cfg) + if pkg != "" { + imports[pkg] = struct{}{} + } + for varnames[name] { + name = name + "_" + } + varnames[name] = true + + var ( + extType binding.ExtendedType + ok bool + ) + if extType, ok = cfg.Types[fullPName]; !ok { + extType = binding.ExtendedType{ + Base: abiEvent.Parameters[i].Type, + } + } + eTmp.Parameters = append(eTmp.Parameters, EventParamTmpl{ + ParamTmpl: binding.ParamTmpl{ + Name: name, + Type: typeStr, + }, + ExtType: extType, + }) + } + ctr.CustomEvents = append(ctr.CustomEvents, eTmp) + } + + if len(ctr.CustomEvents) > 0 { + imports["github.com/nspcc-dev/neo-go/pkg/neorpc/result"] = struct{}{} + imports["github.com/nspcc-dev/neo-go/pkg/vm/stackitem"] = struct{}{} + imports["fmt"] = struct{}{} + imports["errors"] = struct{}{} + } for i := range ctr.SafeMethods { switch ctr.SafeMethods[i].ReturnType { @@ -693,3 +826,32 @@ func toTypeName(s string) string { func addIndent(str string, ind string) string { return strings.ReplaceAll(str, "\n", "\n"+ind) } + +// toPascalCase removes all non-unicode characters from the provided string and +// converts it to pascal case using space as delimiter. +func toPascalCase(s string) string { + var res string + ss := strings.Split(s, " ") + for i := range ss { // TODO: use DecodeRuneInString instead. + var word string + for _, ch := range ss[i] { + var ok bool + if len(res) == 0 && len(word) == 0 { + ok = unicode.IsLetter(ch) + } else { + ok = unicode.IsLetter(ch) || unicode.IsDigit(ch) || ch == '_' + } + if ok { + word += string(ch) + } + } + if len(word) > 0 { + res += upperFirst(word) + } + } + return res +} + +func upperFirst(s string) string { + return strings.ToUpper(s[0:1]) + s[1:] +} From 194639bb153f9cab3655c8c92927561133aaaded Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Fri, 5 May 2023 16:57:07 +0300 Subject: [PATCH 08/22] compiler: fix typo in the method description Signed-off-by: Anna Shaleva --- 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 88fcbb119..c720fcbc5 100644 --- a/pkg/compiler/debug.go +++ b/pkg/compiler/debug.go @@ -108,7 +108,7 @@ type DebugRange struct { End uint16 } -// DebugParam represents the variables's name and type. +// DebugParam represents the variable's name and type. type DebugParam struct { Name string `json:"name"` Type string `json:"type"` From e2580187a1447bca3169a8a87fc6249f05f2e6d4 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Mon, 8 May 2023 21:58:28 +0300 Subject: [PATCH 09/22] cli: fetch extended evet types from contract config The user should specify it via parameter's `extendedtype` field and via upper-level `namedtypes` field of the contract configuration YAML. Also, as we have proper event structure source, make the `--guess-eventtype` compilation option and make event types guess optional. Signed-off-by: Anna Shaleva --- cli/smartcontract/smart_contract.go | 25 +++++-- docs/compiler.md | 105 +++++++++++++++++++++++++++- internal/testchain/transaction.go | 1 + pkg/compiler/compiler.go | 77 ++++++++++++++++---- pkg/compiler/compiler_test.go | 22 +++--- pkg/compiler/debug.go | 13 +++- pkg/compiler/interop_test.go | 8 +-- pkg/neotest/compile.go | 1 + 8 files changed, 216 insertions(+), 36 deletions(-) diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index 5a2af4685..38dea5f13 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -24,6 +24,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/management" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/binding" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/util" @@ -125,7 +126,7 @@ func NewCommands() []cli.Command { { Name: "compile", Usage: "compile a smart contract to a .nef file", - UsageText: "neo-go contract compile -i path [-o nef] [-v] [-d] [-m manifest] [-c yaml] [--bindings file] [--no-standards] [--no-events] [--no-permissions]", + UsageText: "neo-go contract compile -i path [-o nef] [-v] [-d] [-m manifest] [-c yaml] [--bindings file] [--no-standards] [--no-events] [--no-permissions] [--guess-eventtypes]", Description: `Compiles given smart contract to a .nef file and emits other associated information (manifest, bindings configuration, debug information files) if asked to. If none of --out, --manifest, --config, --bindings flags are specified, @@ -171,6 +172,10 @@ func NewCommands() []cli.Command { Name: "no-permissions", Usage: "do not check if invoked contracts are allowed in manifest", }, + cli.BoolFlag{ + Name: "guess-eventtypes", + Usage: "guess event types for smart-contract bindings configuration from the code usages", + }, cli.StringFlag{ Name: "bindings", Usage: "output file for smart-contract bindings configuration", @@ -352,13 +357,15 @@ func initSmartContract(ctx *cli.Context) error { SourceURL: "http://example.com/", SupportedStandards: []string{}, SafeMethods: []string{}, - Events: []manifest.Event{ + Events: []compiler.HybridEvent{ { Name: "Hello world!", - Parameters: []manifest.Parameter{ + Parameters: []compiler.HybridParameter{ { - Name: "args", - Type: smartcontract.ArrayType, + Parameter: manifest.Parameter{ + Name: "args", + Type: smartcontract.ArrayType, + }, }, }, }, @@ -447,6 +454,8 @@ func contractCompile(ctx *cli.Context) error { NoStandardCheck: ctx.Bool("no-standards"), NoEventsCheck: ctx.Bool("no-events"), NoPermissionsCheck: ctx.Bool("no-permissions"), + + GuessEventTypes: ctx.Bool("guess-eventtypes"), } if len(confFile) != 0 { @@ -457,6 +466,7 @@ func contractCompile(ctx *cli.Context) error { o.Name = conf.Name o.SourceURL = conf.SourceURL o.ContractEvents = conf.Events + o.DeclaredNamedTypes = conf.NamedTypes o.ContractSupportedStandards = conf.SupportedStandards o.Permissions = make([]manifest.Permission, len(conf.Permissions)) for i := range conf.Permissions { @@ -705,9 +715,10 @@ type ProjectConfig struct { SourceURL string SafeMethods []string SupportedStandards []string - Events []manifest.Event + Events []compiler.HybridEvent Permissions []permission - Overloads map[string]string `yaml:"overloads,omitempty"` + Overloads map[string]string `yaml:"overloads,omitempty"` + NamedTypes map[string]binding.ExtendedType `yaml:"namedtypes,omitempty"` } func inspect(ctx *cli.Context) error { diff --git a/docs/compiler.md b/docs/compiler.md index d4d17efb4..36c37409a 100644 --- a/docs/compiler.md +++ b/docs/compiler.md @@ -472,10 +472,113 @@ 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 +$ ./bin/neo-go contract compile -i contract.go --config contract.yml -o contract.nef --manifest manifest.json --bindings contract.bindings.yml --guess-eventtypes $ ./bin/neo-go contract generate-rpcwrapper --manifest manifest.json --config contract.bindings.yml --out rpcwrapper.go --hash 0x1b4357bff5a01bdf2a6581247cf9ed1e24629176 ``` +Contract-specific RPC-bindings generated by "generate-rpcwrapper" command include +structure wrappers for each event declared in the contract manifest as far as the +set of helpers that allow to retrieve emitted event from the application log or +from stackitem. By default, event wrappers builder use event structure that was +described in the manifest. Since the type data available in the manifest is +limited, in some cases the resulting generated event structure may use generic +go types. Go contracts can make use of additional type data from bindings +configuration file generated during compilation. Like for any other contract +types, this can cover arrays, maps and structures. To reach the maximum +resemblance between the emitted events and the generated event wrappers, we +recommend either to fill in the extended events type information in the contract +configuration file before the compilation or to use `--guess-eventtypes` +compilation option. + +If using `--guess-eventtypes` compilation option, event parameter types will be +guessed from the arguments of `runtime.Notify` calls for each emitted event. If +multiple calls of `runtime.Notify` are found, then argument types will be checked +for matching (guessed types must be the same across the particular event usages). +After that, the extended types binding configuration will be generated according +to the emitted events parameter types. `--guess-eventtypes` compilation option +is able to recognize those events that has a constant name known at a compilation +time and do not include variadic arguments usage. Thus, use this option if your +contract suites these requirements. Otherwise, we recommend to manually specify +extended event parameter types information in the contract configuration file. + +Extended event parameter type information can be provided manually via contract +configuration file under the `events` section. Each event parameter specified in +this section may be supplied with additional parameter type information specified +under `extendedtype` subsection. The extended type information (`ExtendedType`) +has the following structure: + +| Field | Type | Required | Meaning | +|-------------|---------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| +| `base` | Any valid [NEP-14 parameter type](https://github.com/neo-project/proposals/blob/master/nep-14.mediawiki#parametertype) except `Void`. | Always required. | The base type of a parameter, e.g. `Array` for go structures and any nested arrays, `Map` for nested maps, `Hash160` for 160-bits integers, etc. | +| `name` | `string` | Required for structures, omitted for arrays, interfaces and maps. | Name of a structure that will be used in the resulting RPC binding. | +| `interface` | `string` | Required for `InteropInterface`-based types, currently `iterator` only is supported. | Underlying value of the `InteropInterface`. | +| `key` | Any simple [NEP-14 parameter type](https://github.com/neo-project/proposals/blob/master/nep-14.mediawiki#parametertype). | Required for `Map`-based types. | Key type for maps. | +| `value` | `ExtendedType`. | Required for iterators, arrays and maps. | Value type of iterators, arrays and maps. | +| `fields` | Array of `FieldExtendedType`. | Required for structures. | Ordered type data for structure fields. | + +The structure's field extended information (`FieldExtendedType`) has the following structure: + +| Field | Type | Required | Meaning | +|------------------------|----------------|------------------|-----------------------------------------------------------------------------| +| `field` | `string` | Always required. | Name of the structure field that will be used in the resulting RPC binding. | +| Inlined `ExtendedType` | `ExtendedType` | Always required. | The extended type information about structure field. | + + +Any named structures used in the `ExtendedType` description must be manually +specified in the contract configuration file under top-level `namedtypes` section +in the form of `map[string]ExtendedType`, where the map key is a name of the +described named structure that matches the one provided in the `name` field of +the event parameter's extended type. + +Here's the example of manually-created contract configuration file that uses +extended types for event parameters description: + +``` +name: "HelloWorld contract" +supportedstandards: [] +events: + - name: Some simple notification + parameters: + - name: intP + type: Integer + - name: boolP + type: Boolean + - name: stringP + type: String + - name: Structure notification + parameters: + - name: structure parameter + type: Array + extendedtype: + base: Array + name: transferData + - name: Map of structures notification + parameters: + - name: map parameter + type: Map + extendedtype: + base: Map + key: Integer + value: + base: Array + name: transferData + - name: Iterator notification + parameters: + - name: data + type: InteropInterface + extendedtype: + base: InteropInterface + interface: iterator +namedtypes: + transferData: + base: Array + fields: + - field: IntField + base: Integer + - field: BoolField + base: Boolean +``` + ## Smart contract examples Some examples are provided in the [examples directory](../examples). For more diff --git a/internal/testchain/transaction.go b/internal/testchain/transaction.go index 630902ac8..cce8dd49c 100644 --- a/internal/testchain/transaction.go +++ b/internal/testchain/transaction.go @@ -76,6 +76,7 @@ func NewDeployTx(bc Ledger, name string, sender util.Uint160, r gio.Reader, conf o.Name = conf.Name o.SourceURL = conf.SourceURL o.ContractEvents = conf.Events + o.DeclaredNamedTypes = conf.NamedTypes o.ContractSupportedStandards = conf.SupportedStandards o.Permissions = make([]manifest.Permission, len(conf.Permissions)) for i := range conf.Permissions { diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index aec697006..e44aead0a 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -52,14 +52,26 @@ type Options struct { // This setting has effect only if manifest is emitted. NoPermissionsCheck bool + // GuessEventTypes specifies if types of runtime notifications need to be guessed + // from the usage context. These types are used for RPC binding generation only and + // can be defined for events with name known at the compilation time and without + // variadic args usages. If some type is specified via config file, then the config's + // one is preferable. Currently, event's parameter type is defined from the first + // occurrence of event call. + GuessEventTypes bool + // Name is a contract's name to be written to manifest. Name string // SourceURL is a contract's source URL to be written to manifest. SourceURL string - // Runtime notifications. - ContractEvents []manifest.Event + // Runtime notifications declared in the contract configuration file. + ContractEvents []HybridEvent + + // DeclaredNamedTypes is the set of named types that were declared in the + // contract configuration type and are the part of manifest events. + DeclaredNamedTypes map[string]binding.ExtendedType // The list of standards supported by the contract. ContractSupportedStandards []string @@ -78,6 +90,23 @@ type Options struct { BindingsFile string } +// HybridEvent represents the description of event emitted by the contract squashed +// with extended event's parameters description. We have it as a separate type for +// the user's convenience. It is applied for the smart contract configuration file +// only. +type HybridEvent struct { + Name string `json:"name"` + Parameters []HybridParameter `json:"parameters"` +} + +// HybridParameter contains the manifest's event parameter description united with +// the extended type description for this parameter. It is applied for the smart +// contract configuration file only. +type HybridParameter struct { + manifest.Parameter `yaml:",inline"` + ExtendedType *binding.ExtendedType `yaml:"extendedtype,omitempty"` +} + type buildInfo struct { config *packages.Config program []*packages.Package @@ -309,18 +338,42 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { if len(di.NamedTypes) > 0 { cfg.NamedTypes = di.NamedTypes } - if len(di.EmittedEvents) > 0 { - for eventName, eventUsages := range di.EmittedEvents { - for typeName, extType := range eventUsages[0].ExtTypes { - cfg.NamedTypes[typeName] = extType + for name, et := range o.DeclaredNamedTypes { + // TODO: handle name conflict + cfg.NamedTypes[name] = et + } + for _, e := range o.ContractEvents { + for _, p := range e.Parameters { + // TODO: proper imports handling during bindings generation (see utf8 example). + if p.ExtendedType != nil { + pName := e.Name + "." + p.Name + cfg.Types[pName] = *p.ExtendedType } - for _, p := range eventUsages[0].Params { - pname := eventName + "." + p.Name - if p.RealType.TypeName != "" { - cfg.Overrides[pname] = p.RealType + } + } + if o.GuessEventTypes { + if len(di.EmittedEvents) > 0 { + for eventName, eventUsages := range di.EmittedEvents { + // Take into account the first usage only. + // TODO: extend it to the rest of invocations. + for typeName, extType := range eventUsages[0].ExtTypes { + if _, ok := cfg.NamedTypes[typeName]; !ok { + cfg.NamedTypes[typeName] = extType + } } - if p.ExtendedType != nil { - cfg.Types[pname] = *p.ExtendedType + for _, p := range eventUsages[0].Params { + // TODO: prettify notification name in-place. + pname := eventName + "." + p.Name + if p.RealType.TypeName != "" { + if _, ok := cfg.Overrides[pname]; !ok { + cfg.Overrides[pname] = p.RealType + } + } + if p.ExtendedType != nil { + if _, ok := cfg.Types[pname]; !ok { + cfg.Types[pname] = *p.ExtendedType + } + } } } } diff --git a/pkg/compiler/compiler_test.go b/pkg/compiler/compiler_test.go index 8a9ab207a..288aaf3c7 100644 --- a/pkg/compiler/compiler_test.go +++ b/pkg/compiler/compiler_test.go @@ -159,16 +159,16 @@ func TestEventWarnings(t *testing.T) { }) t.Run("wrong parameter number", func(t *testing.T) { _, err = compiler.CreateManifest(di, &compiler.Options{ - ContractEvents: []manifest.Event{{Name: "Event"}}, + ContractEvents: []compiler.HybridEvent{{Name: "Event"}}, Name: "payable", }) require.Error(t, err) }) t.Run("wrong parameter type", func(t *testing.T) { _, err = compiler.CreateManifest(di, &compiler.Options{ - ContractEvents: []manifest.Event{{ + ContractEvents: []compiler.HybridEvent{{ Name: "Event", - Parameters: []manifest.Parameter{manifest.NewParameter("number", smartcontract.StringType)}, + Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.StringType)}}, }}, Name: "payable", }) @@ -176,9 +176,9 @@ func TestEventWarnings(t *testing.T) { }) t.Run("any parameter type", func(t *testing.T) { _, err = compiler.CreateManifest(di, &compiler.Options{ - ContractEvents: []manifest.Event{{ + ContractEvents: []compiler.HybridEvent{{ Name: "Event", - Parameters: []manifest.Parameter{manifest.NewParameter("number", smartcontract.AnyType)}, + Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.AnyType)}}, }}, Name: "payable", }) @@ -186,9 +186,9 @@ func TestEventWarnings(t *testing.T) { }) t.Run("good", func(t *testing.T) { _, err = compiler.CreateManifest(di, &compiler.Options{ - ContractEvents: []manifest.Event{{ + ContractEvents: []compiler.HybridEvent{{ Name: "Event", - Parameters: []manifest.Parameter{manifest.NewParameter("number", smartcontract.IntegerType)}, + Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.IntegerType)}}, }}, Name: "payable", }) @@ -224,7 +224,7 @@ func TestEventWarnings(t *testing.T) { require.Error(t, err) _, err = compiler.CreateManifest(di, &compiler.Options{ - ContractEvents: []manifest.Event{{Name: "Event"}}, + ContractEvents: []compiler.HybridEvent{{Name: "Event"}}, Name: "eventTest", }) require.NoError(t, err) @@ -242,9 +242,9 @@ func TestEventWarnings(t *testing.T) { _, err = compiler.CreateManifest(di, &compiler.Options{ Name: "eventTest", - ContractEvents: []manifest.Event{{ + ContractEvents: []compiler.HybridEvent{{ Name: "Event", - Parameters: []manifest.Parameter{manifest.NewParameter("number", smartcontract.IntegerType)}, + Parameters: []compiler.HybridParameter{{Parameter: manifest.NewParameter("number", smartcontract.IntegerType)}}, }}, }) require.NoError(t, err) @@ -260,7 +260,7 @@ func TestNotifyInVerify(t *testing.T) { t.Run(name, func(t *testing.T) { src := fmt.Sprintf(srcTmpl, name) _, _, err := compiler.CompileWithOptions("eventTest.go", strings.NewReader(src), - &compiler.Options{ContractEvents: []manifest.Event{{Name: "Event"}}}) + &compiler.Options{ContractEvents: []compiler.HybridEvent{{Name: "Event"}}}) require.Error(t, err) t.Run("suppress", func(t *testing.T) { diff --git a/pkg/compiler/debug.go b/pkg/compiler/debug.go index c720fcbc5..0eb18bb5a 100644 --- a/pkg/compiler/debug.go +++ b/pkg/compiler/debug.go @@ -593,9 +593,20 @@ func (di *DebugInfo) ConvertToManifest(o *Options) (*manifest.Manifest, error) { if o.ContractSupportedStandards != nil { result.SupportedStandards = o.ContractSupportedStandards } + events := make([]manifest.Event, len(o.ContractEvents)) + for i, e := range o.ContractEvents { + params := make([]manifest.Parameter, len(e.Parameters)) + for j, p := range e.Parameters { + params[j] = p.Parameter + } + events[i] = manifest.Event{ + Name: o.ContractEvents[i].Name, + Parameters: params, + } + } result.ABI = manifest.ABI{ Methods: methods, - Events: o.ContractEvents, + Events: events, } if result.ABI.Events == nil { result.ABI.Events = make([]manifest.Event, 0) diff --git a/pkg/compiler/interop_test.go b/pkg/compiler/interop_test.go index 1cd1c88f3..c8f77ce24 100644 --- a/pkg/compiler/interop_test.go +++ b/pkg/compiler/interop_test.go @@ -542,13 +542,13 @@ func TestForcedNotifyArgumentsConversion(t *testing.T) { if count != len(expectedVMParamTypes) { t.Fatalf("parameters count mismatch: %d vs %d", count, len(expectedVMParamTypes)) } - scParams := make([]manifest.Parameter, len(targetSCParamTypes)) + scParams := make([]compiler.HybridParameter, len(targetSCParamTypes)) vmParams := make([]stackitem.Item, len(expectedVMParamTypes)) for i := range scParams { - scParams[i] = manifest.Parameter{ + scParams[i] = compiler.HybridParameter{Parameter: manifest.Parameter{ Name: strconv.Itoa(i), Type: targetSCParamTypes[i], - } + }} defaultValue := stackitem.NewBigInteger(big.NewInt(int64(i))) var ( val stackitem.Item @@ -564,7 +564,7 @@ func TestForcedNotifyArgumentsConversion(t *testing.T) { } ctr := neotest.CompileSource(t, e.CommitteeHash, strings.NewReader(src), &compiler.Options{ Name: "Helper", - ContractEvents: []manifest.Event{ + ContractEvents: []compiler.HybridEvent{ { Name: methodWithoutEllipsis, Parameters: scParams, diff --git a/pkg/neotest/compile.go b/pkg/neotest/compile.go index fee77c17e..70645c11c 100644 --- a/pkg/neotest/compile.go +++ b/pkg/neotest/compile.go @@ -60,6 +60,7 @@ func CompileFile(t testing.TB, sender util.Uint160, srcPath string, configPath s o := &compiler.Options{} o.Name = conf.Name o.ContractEvents = conf.Events + o.DeclaredNamedTypes = conf.NamedTypes o.ContractSupportedStandards = conf.SupportedStandards o.Permissions = make([]manifest.Permission, len(conf.Permissions)) for i := range conf.Permissions { From 2c36802e23c220bd1aa892523a45bdf1c0f3f163 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 31 May 2023 15:32:53 +0300 Subject: [PATCH 10/22] binding: fix comment to the extended type field Signed-off-by: Anna Shaleva --- pkg/smartcontract/binding/generate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/smartcontract/binding/generate.go b/pkg/smartcontract/binding/generate.go index bf3e3d9a1..33bb21dd2 100644 --- a/pkg/smartcontract/binding/generate.go +++ b/pkg/smartcontract/binding/generate.go @@ -72,7 +72,7 @@ type ( 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. + Value *ExtendedType `yaml:"value,omitempty"` // Value type for iterators, arrays and maps. Fields []FieldExtendedType `yaml:"fields,omitempty"` // Ordered type data for structure fields. } From 41938ffa78f50938d72040bf58db45ae8405100a Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Sun, 21 May 2023 15:08:29 +0300 Subject: [PATCH 11/22] smartcontract: drop standard events before RPC binging generation Signed-off-by: Anna Shaleva --- cli/smartcontract/testdata/gas/gas.go | 96 ------------------ cli/smartcontract/testdata/nameservice/nns.go | 98 ------------------- cli/smartcontract/testdata/nex/nex.go | 91 ----------------- pkg/rpcclient/nep11/base.go | 69 +++++++++++++ pkg/rpcclient/nep17/nep17.go | 66 +++++++++++++ pkg/smartcontract/rpcbinding/binding.go | 22 +++++ 6 files changed, 157 insertions(+), 285 deletions(-) diff --git a/cli/smartcontract/testdata/gas/gas.go b/cli/smartcontract/testdata/gas/gas.go index 2b4cf920a..d12bbb8ee 100644 --- a/cli/smartcontract/testdata/gas/gas.go +++ b/cli/smartcontract/testdata/gas/gas.go @@ -2,25 +2,13 @@ package gastoken import ( - "errors" - "fmt" - "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" - "math/big" ) // Hash contains contract hash. var Hash = util.Uint160{0xcf, 0x76, 0xe2, 0x8b, 0xd0, 0x6, 0x2c, 0x4a, 0x47, 0x8e, 0xe3, 0x55, 0x61, 0x1, 0x13, 0x19, 0xf3, 0xcf, 0xa4, 0xd2} -// TransferEvent represents "Transfer" event emitted by the contract. -type TransferEvent struct { - From util.Uint160 - To util.Uint160 - Amount *big.Int -} - // Invoker is used by ContractReader to call various safe methods. type Invoker interface { nep17.Invoker @@ -56,87 +44,3 @@ func New(actor Actor) *Contract { var nep17t = nep17.New(actor, Hash) return &Contract{ContractReader{nep17t.TokenReader, actor}, nep17t.TokenWriter, actor} } - -// TransferEventsFromApplicationLog retrieves a set of all emitted events -// with "Transfer" name from the provided ApplicationLog. -func TransferEventsFromApplicationLog(log *result.ApplicationLog) ([]*TransferEvent, error) { - if log == nil { - return nil, errors.New("nil application log") - } - - var res []*TransferEvent - for i, ex := range log.Executions { - for j, e := range ex.Events { - if e.Name != "Transfer" { - continue - } - event := new(TransferEvent) - err := event.FromStackItem(e.Item) - if err != nil { - return nil, fmt.Errorf("failed to deserialize TransferEvent from stackitem (execution %d, event %d): %w", i, j, err) - } - res = append(res, event) - } - } - - return res, nil -} - -// FromStackItem converts provided stackitem.Array to TransferEvent and -// returns an error if so. -func (e *TransferEvent) 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) != 3 { - return errors.New("wrong number of structure elements") - } - - var ( - index = -1 - err error - ) - index++ - e.From, 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 From: %w", err) - } - - index++ - e.To, 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 To: %w", err) - } - - index++ - e.Amount, err = arr[index].TryInteger() - if err != nil { - return fmt.Errorf("field Amount: %w", err) - } - - return nil -} diff --git a/cli/smartcontract/testdata/nameservice/nns.go b/cli/smartcontract/testdata/nameservice/nns.go index 8bb311bb9..2b19af793 100644 --- a/cli/smartcontract/testdata/nameservice/nns.go +++ b/cli/smartcontract/testdata/nameservice/nns.go @@ -18,14 +18,6 @@ import ( // Hash contains contract hash. var Hash = util.Uint160{0xde, 0x46, 0x5f, 0x5d, 0x50, 0x57, 0xcf, 0x33, 0x28, 0x47, 0x94, 0xc5, 0xcf, 0xc2, 0xc, 0x69, 0x37, 0x1c, 0xac, 0x50} -// TransferEvent represents "Transfer" event emitted by the contract. -type TransferEvent struct { - From util.Uint160 - To util.Uint160 - Amount *big.Int - TokenId []byte -} - // SetAdminEvent represents "SetAdmin" event emitted by the contract. type SetAdminEvent struct { Name string @@ -345,96 +337,6 @@ func (c *Contract) DeleteRecordUnsigned(name string, typev *big.Int) (*transacti return c.actor.MakeUnsignedCall(Hash, "deleteRecord", nil, name, typev) } -// TransferEventsFromApplicationLog retrieves a set of all emitted events -// with "Transfer" name from the provided ApplicationLog. -func TransferEventsFromApplicationLog(log *result.ApplicationLog) ([]*TransferEvent, error) { - if log == nil { - return nil, errors.New("nil application log") - } - - var res []*TransferEvent - for i, ex := range log.Executions { - for j, e := range ex.Events { - if e.Name != "Transfer" { - continue - } - event := new(TransferEvent) - err := event.FromStackItem(e.Item) - if err != nil { - return nil, fmt.Errorf("failed to deserialize TransferEvent from stackitem (execution %d, event %d): %w", i, j, err) - } - res = append(res, event) - } - } - - return res, nil -} - -// FromStackItem converts provided stackitem.Array to TransferEvent and -// returns an error if so. -func (e *TransferEvent) 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) != 4 { - return errors.New("wrong number of structure elements") - } - - var ( - index = -1 - err error - ) - index++ - e.From, 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 From: %w", err) - } - - index++ - e.To, 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 To: %w", err) - } - - index++ - e.Amount, err = arr[index].TryInteger() - if err != nil { - return fmt.Errorf("field Amount: %w", err) - } - - index++ - e.TokenId, err = arr[index].TryBytes() - if err != nil { - return fmt.Errorf("field TokenId: %w", err) - } - - return nil -} - // SetAdminEventsFromApplicationLog retrieves a set of all emitted events // with "SetAdmin" name from the provided ApplicationLog. func SetAdminEventsFromApplicationLog(log *result.ApplicationLog) ([]*SetAdminEvent, error) { diff --git a/cli/smartcontract/testdata/nex/nex.go b/cli/smartcontract/testdata/nex/nex.go index 41de92d02..f558fb513 100644 --- a/cli/smartcontract/testdata/nex/nex.go +++ b/cli/smartcontract/testdata/nex/nex.go @@ -17,13 +17,6 @@ import ( // Hash contains contract hash. var Hash = util.Uint160{0xa8, 0x1a, 0xa1, 0xf0, 0x4b, 0xf, 0xdc, 0x4a, 0xa2, 0xce, 0xd5, 0xbf, 0xc6, 0x22, 0xcf, 0xe8, 0x9, 0x7f, 0xa6, 0xa2} -// TransferEvent represents "Transfer" event emitted by the contract. -type TransferEvent struct { - From util.Uint160 - To util.Uint160 - Amount *big.Int -} - // OnMintEvent represents "OnMint" event emitted by the contract. type OnMintEvent struct { From util.Uint160 @@ -249,90 +242,6 @@ func (c *Contract) UpdateCapUnsigned(newCap *big.Int) (*transaction.Transaction, return c.actor.MakeUnsignedCall(Hash, "updateCap", nil, newCap) } -// TransferEventsFromApplicationLog retrieves a set of all emitted events -// with "Transfer" name from the provided ApplicationLog. -func TransferEventsFromApplicationLog(log *result.ApplicationLog) ([]*TransferEvent, error) { - if log == nil { - return nil, errors.New("nil application log") - } - - var res []*TransferEvent - for i, ex := range log.Executions { - for j, e := range ex.Events { - if e.Name != "Transfer" { - continue - } - event := new(TransferEvent) - err := event.FromStackItem(e.Item) - if err != nil { - return nil, fmt.Errorf("failed to deserialize TransferEvent from stackitem (execution %d, event %d): %w", i, j, err) - } - res = append(res, event) - } - } - - return res, nil -} - -// FromStackItem converts provided stackitem.Array to TransferEvent and -// returns an error if so. -func (e *TransferEvent) 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) != 3 { - return errors.New("wrong number of structure elements") - } - - var ( - index = -1 - err error - ) - index++ - e.From, 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 From: %w", err) - } - - index++ - e.To, 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 To: %w", err) - } - - index++ - e.Amount, err = arr[index].TryInteger() - if err != nil { - return fmt.Errorf("field Amount: %w", err) - } - - return nil -} - // OnMintEventsFromApplicationLog retrieves a set of all emitted events // with "OnMint" name from the provided ApplicationLog. func OnMintEventsFromApplicationLog(log *result.ApplicationLog) ([]*OnMintEvent, error) { diff --git a/pkg/rpcclient/nep11/base.go b/pkg/rpcclient/nep11/base.go index ed30df2af..db906cb3d 100644 --- a/pkg/rpcclient/nep11/base.go +++ b/pkg/rpcclient/nep11/base.go @@ -10,6 +10,7 @@ purposes, otherwise more specific types are recommended. package nep11 import ( + "errors" "fmt" "math/big" "unicode/utf8" @@ -246,3 +247,71 @@ func UnwrapKnownProperties(m *stackitem.Map, err error) (map[string]string, erro } return res, nil } + +// TransferEventsFromApplicationLog retrieves all emitted TransferEvents from the +// provided [result.ApplicationLog]. +func TransferEventsFromApplicationLog(log *result.ApplicationLog) ([]*TransferEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + var res []*TransferEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "Transfer" { + continue + } + event := new(TransferEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to decode event from stackitem (event #%d, execution #%d): %w", j, i, err) + } + res = append(res, event) + } + } + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to TransferEvent or returns an +// error if it's not possible to do to so. +func (e *TransferEvent) 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) != 4 { + return errors.New("wrong number of event parameters") + } + + b, err := arr[0].TryBytes() + if err != nil { + return fmt.Errorf("invalid From: %w", err) + } + e.From, err = util.Uint160DecodeBytesBE(b) + if err != nil { + return fmt.Errorf("failed to decode From: %w", err) + } + + b, err = arr[1].TryBytes() + if err != nil { + return fmt.Errorf("invalid To: %w", err) + } + e.To, err = util.Uint160DecodeBytesBE(b) + if err != nil { + return fmt.Errorf("failed to decode To: %w", err) + } + + e.Amount, err = arr[2].TryInteger() + if err != nil { + return fmt.Errorf("field to decode Avount: %w", err) + } + + e.ID, err = arr[3].TryBytes() + if err != nil { + return fmt.Errorf("failed to decode ID: %w", err) + } + + return nil +} diff --git a/pkg/rpcclient/nep17/nep17.go b/pkg/rpcclient/nep17/nep17.go index f91488e82..dec570a22 100644 --- a/pkg/rpcclient/nep17/nep17.go +++ b/pkg/rpcclient/nep17/nep17.go @@ -8,12 +8,15 @@ package nep17 import ( "errors" + "fmt" "math/big" "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/rpcclient/neptoken" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) // Invoker is used by TokenReader to call various safe methods. @@ -147,3 +150,66 @@ func (t *TokenWriter) MultiTransferUnsigned(params []TransferParameters) (*trans } return t.actor.MakeUnsignedRun(script, nil) } + +// TransferEventsFromApplicationLog retrieves all emitted TransferEvents from the +// provided [result.ApplicationLog]. +func TransferEventsFromApplicationLog(log *result.ApplicationLog) ([]*TransferEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + var res []*TransferEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "Transfer" { + continue + } + event := new(TransferEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to decode event from stackitem (event #%d, execution #%d): %w", j, i, err) + } + res = append(res, event) + } + } + return res, nil +} + +// FromStackItem converts provided [stackitem.Array] to TransferEvent or returns an +// error if it's not possible to do to so. +func (e *TransferEvent) 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) != 3 { + return errors.New("wrong number of event parameters") + } + + b, err := arr[0].TryBytes() + if err != nil { + return fmt.Errorf("invalid From: %w", err) + } + e.From, err = util.Uint160DecodeBytesBE(b) + if err != nil { + return fmt.Errorf("failed to decode From: %w", err) + } + + b, err = arr[1].TryBytes() + if err != nil { + return fmt.Errorf("invalid To: %w", err) + } + e.To, err = util.Uint160DecodeBytesBE(b) + if err != nil { + return fmt.Errorf("failed to decode To: %w", err) + } + + e.Amount, err = arr[2].TryInteger() + if err != nil { + return fmt.Errorf("field to decode Avount: %w", err) + } + + return nil +} diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index 973264c78..ba65361f1 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -396,12 +396,14 @@ func Generate(cfg binding.Config) error { mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep11NonDivisible) ctr.IsNep11ND = true } + mfst.ABI.Events = dropStdEvents(mfst.ABI.Events, standard.Nep11Base) break // Can't be NEP-17 at the same time. } if std == manifest.NEP17StandardName && standard.ComplyABI(cfg.Manifest, standard.Nep17) == nil { mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep17) imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"] = struct{}{} ctr.IsNep17 = true + mfst.ABI.Events = dropStdEvents(mfst.ABI.Events, standard.Nep17) break // Can't be NEP-11 at the same time. } } @@ -445,6 +447,18 @@ func dropManifestMethods(meths []manifest.Method, manifested []manifest.Method) return meths } +func dropManifestEvents(events []manifest.Event, manifested []manifest.Event) []manifest.Event { + for _, e := range manifested { + for i := 0; i < len(events); i++ { + if events[i].Name == e.Name && len(events[i].Parameters) == len(e.Parameters) { + events = append(events[:i], events[i+1:]...) + i-- + } + } + } + return events +} + func dropStdMethods(meths []manifest.Method, std *standard.Standard) []manifest.Method { meths = dropManifestMethods(meths, std.Manifest.ABI.Methods) if std.Optional != nil { @@ -456,6 +470,14 @@ func dropStdMethods(meths []manifest.Method, std *standard.Standard) []manifest. return meths } +func dropStdEvents(events []manifest.Event, std *standard.Standard) []manifest.Event { + events = dropManifestEvents(events, std.Manifest.ABI.Events) + if std.Base != nil { + return dropStdEvents(events, std.Base) + } + return events +} + func extendedTypeToGo(et binding.ExtendedType, named map[string]binding.ExtendedType) (string, string) { switch et.Base { case smartcontract.AnyType: From 19cc6c63696839888c0348cdd8b4d81ada4f21ab Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 23 May 2023 14:17:39 +0300 Subject: [PATCH 12/22] compiler: store ready-to-use notification names in bindings config Notification and its parameters may have any UTF8-compatible name which is inappropriate for bindings configuration and for the resulting RPC bindings file. This commit stores the prettified version of notification's name and parameters that are ready to be used in the resulting RPC binding without any changes. Signed-off-by: Anna Shaleva --- cli/smartcontract/generate_test.go | 1 + .../testdata/notifications/config.yml | 7 + .../testdata/notifications/notifications.go | 7 + .../testdata/notifications/rpcbindings.out | 748 ++++++++++++++++++ pkg/compiler/compiler.go | 14 +- pkg/smartcontract/rpcbinding/binding.go | 58 +- 6 files changed, 805 insertions(+), 30 deletions(-) create mode 100644 cli/smartcontract/testdata/notifications/config.yml create mode 100644 cli/smartcontract/testdata/notifications/notifications.go create mode 100644 cli/smartcontract/testdata/notifications/rpcbindings.out diff --git a/cli/smartcontract/generate_test.go b/cli/smartcontract/generate_test.go index b274e99f3..67a46fc12 100644 --- a/cli/smartcontract/generate_test.go +++ b/cli/smartcontract/generate_test.go @@ -416,6 +416,7 @@ func TestAssistedRPCBindings(t *testing.T) { checkBinding(filepath.Join("testdata", "types")) checkBinding(filepath.Join("testdata", "structs")) + checkBinding(filepath.Join("testdata", "notifications")) require.False(t, rewriteExpectedOutputs) } diff --git a/cli/smartcontract/testdata/notifications/config.yml b/cli/smartcontract/testdata/notifications/config.yml new file mode 100644 index 000000000..5bb9b8554 --- /dev/null +++ b/cli/smartcontract/testdata/notifications/config.yml @@ -0,0 +1,7 @@ +name: "Notifications" +sourceurl: https://github.com/nspcc-dev/neo-go/ +events: + - name: "! complicated name %$#" + parameters: + - name: ! complicated param @#$% + type: String diff --git a/cli/smartcontract/testdata/notifications/notifications.go b/cli/smartcontract/testdata/notifications/notifications.go new file mode 100644 index 000000000..369d1cdb6 --- /dev/null +++ b/cli/smartcontract/testdata/notifications/notifications.go @@ -0,0 +1,7 @@ +package structs + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +func Main() { + runtime.Notify("! complicated name %$#", "str1") +} diff --git a/cli/smartcontract/testdata/notifications/rpcbindings.out b/cli/smartcontract/testdata/notifications/rpcbindings.out new file mode 100644 index 000000000..aeeb3ccd7 --- /dev/null +++ b/cli/smartcontract/testdata/notifications/rpcbindings.out @@ -0,0 +1,748 @@ +// 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" +) + +// 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 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 +} + +// 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} +} + +// 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) +} + +// 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 +} diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index e44aead0a..6a711921a 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -18,6 +18,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest/standard" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding" "github.com/nspcc-dev/neo-go/pkg/util" "golang.org/x/tools/go/packages" "gopkg.in/yaml.v3" @@ -339,14 +340,17 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { cfg.NamedTypes = di.NamedTypes } for name, et := range o.DeclaredNamedTypes { - // TODO: handle name conflict + // TODO: handle name conflict (it can happen due to invalid user input e.g.) cfg.NamedTypes[name] = et } for _, e := range o.ContractEvents { + eStructName := rpcbinding.ToEventBindingName(e.Name) for _, p := range e.Parameters { + pStructName := rpcbinding.ToParameterBindingName(p.Name) // TODO: proper imports handling during bindings generation (see utf8 example). + // Probably, we should always add p type to the list of types. if p.ExtendedType != nil { - pName := e.Name + "." + p.Name + pName := eStructName + "." + pStructName cfg.Types[pName] = *p.ExtendedType } } @@ -354,6 +358,7 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { if o.GuessEventTypes { if len(di.EmittedEvents) > 0 { for eventName, eventUsages := range di.EmittedEvents { + eBindingName := rpcbinding.ToEventBindingName(eventName) // Take into account the first usage only. // TODO: extend it to the rest of invocations. for typeName, extType := range eventUsages[0].ExtTypes { @@ -361,9 +366,10 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { cfg.NamedTypes[typeName] = extType } } + for _, p := range eventUsages[0].Params { - // TODO: prettify notification name in-place. - pname := eventName + "." + p.Name + pBindingName := rpcbinding.ToParameterBindingName(p.Name) + pname := eBindingName + "." + pBindingName if p.RealType.TypeName != "" { if _, ok := cfg.Overrides[pname]; !ok { cfg.Overrides[pname] = p.RealType diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index ba65361f1..074b41c49 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -19,10 +19,10 @@ import ( // ensure that this block has new line at the start and in the end of the block. const ( eventDefinition = `{{ define "EVENT" }} -// {{.Name}}Event represents "{{.ManifestName}}" event emitted by the contract. -type {{.Name}}Event struct { +// {{.Name}} represents "{{.ManifestName}}" event emitted by the contract. +type {{.Name}} struct { {{- range $index, $arg := .Parameters}} - {{toPascalCase .Name}} {{.Type}} + {{.Name}} {{.Type}} {{- end}} } {{ end }}` @@ -262,23 +262,23 @@ func (res *{{toTypeName $name}}) FromStackItem(item stackitem.Item) error { } {{ end -}} {{- range $e := .CustomEvents }} -// {{$e.Name}}EventsFromApplicationLog retrieves a set of all emitted events +// {{$e.Name}}sFromApplicationLog retrieves a set of all emitted events // with "{{$e.ManifestName}}" name from the provided ApplicationLog. -func {{$e.Name}}EventsFromApplicationLog(log *result.ApplicationLog) ([]*{{$e.Name}}Event, error) { +func {{$e.Name}}sFromApplicationLog(log *result.ApplicationLog) ([]*{{$e.Name}}, error) { if log == nil { return nil, errors.New("nil application log") } - var res []*{{$e.Name}}Event + var res []*{{$e.Name}} for i, ex := range log.Executions { for j, e := range ex.Events { if e.Name != "{{$e.ManifestName}}" { continue } - event := new({{$e.Name}}Event) + event := new({{$e.Name}}) err := event.FromStackItem(e.Item) if err != nil { - return nil, fmt.Errorf("failed to deserialize {{$e.Name}}Event from stackitem (execution %d, event %d): %w", i, j, err) + return nil, fmt.Errorf("failed to deserialize {{$e.Name}} from stackitem (execution %d, event %d): %w", i, j, err) } res = append(res, event) } @@ -287,9 +287,9 @@ func {{$e.Name}}EventsFromApplicationLog(log *result.ApplicationLog) ([]*{{$e.Na return res, nil } -// FromStackItem converts provided stackitem.Array to {{$e.Name}}Event and +// FromStackItem converts provided stackitem.Array to {{$e.Name}} and // returns an error if so. -func (e *{{$e.Name}}Event) FromStackItem(item *stackitem.Array) error { +func (e *{{$e.Name}}) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") } @@ -307,9 +307,9 @@ func (e *{{$e.Name}}Event) FromStackItem(item *stackitem.Array) error { ) {{- range $p := $e.Parameters}} index++ - e.{{toPascalCase .Name}}, err = {{etTypeConverter .ExtType "arr[index]"}} + e.{{.Name}}, err = {{etTypeConverter .ExtType "arr[index]"}} if err != nil { - return fmt.Errorf("field {{toPascalCase .Name}}: %w", err) + return fmt.Errorf("field {{.Name}}: %w", err) } {{end}} {{- end}} @@ -427,9 +427,8 @@ func Generate(cfg binding.Config) error { r, _ := extendedTypeToGo(et, ctr.NamedTypes) return r }, - "toTypeName": toTypeName, - "cutPointer": cutPointer, - "toPascalCase": toPascalCase, + "toTypeName": toTypeName, + "cutPointer": cutPointer, }).Parse(srcTmpl)) return srcTemplate.Execute(cfg.Output, ctr) @@ -675,23 +674,18 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st imports["errors"] = struct{}{} } for _, abiEvent := range cfg.Manifest.ABI.Events { + eBindingName := ToEventBindingName(abiEvent.Name) eTmp := CustomEventTemplate{ - // TODO: proper event name is better to be set right into config binding in normal form. - Name: toPascalCase(abiEvent.Name), + Name: eBindingName, ManifestName: abiEvent.Name, } - var varnames = make(map[string]bool) for i := range abiEvent.Parameters { - name := abiEvent.Parameters[i].Name - fullPName := abiEvent.Name + "." + name + pBindingName := ToParameterBindingName(abiEvent.Parameters[i].Name) + fullPName := eBindingName + "." + pBindingName typeStr, pkg := scTypeConverter(fullPName, abiEvent.Parameters[i].Type, &cfg) if pkg != "" { imports[pkg] = struct{}{} } - for varnames[name] { - name = name + "_" - } - varnames[name] = true var ( extType binding.ExtendedType @@ -699,12 +693,12 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st ) if extType, ok = cfg.Types[fullPName]; !ok { extType = binding.ExtendedType{ - Base: abiEvent.Parameters[i].Type, + Base: abiEvent.Parameters[i].Type, // TODO: properly handle imports for this case (see utf8 example) } } eTmp.Parameters = append(eTmp.Parameters, EventParamTmpl{ ParamTmpl: binding.ParamTmpl{ - Name: name, + Name: pBindingName, Type: typeStr, }, ExtType: extType, @@ -849,6 +843,18 @@ func addIndent(str string, ind string) string { return strings.ReplaceAll(str, "\n", "\n"+ind) } +// ToEventBindingName converts event name specified in the contract manifest to +// a valid go exported event structure name. +func ToEventBindingName(eventName string) string { + return toPascalCase(eventName) + "Event" +} + +// ToParameterBindingName converts parameter name specified in the contract +// manifest to a valid go structure's exported field name. +func ToParameterBindingName(paramName string) string { + return toPascalCase(paramName) +} + // toPascalCase removes all non-unicode characters from the provided string and // converts it to pascal case using space as delimiter. func toPascalCase(s string) string { From 37af2031bb9fa299b5fc99834d7486f121fce586 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 23 May 2023 18:20:51 +0300 Subject: [PATCH 13/22] rpcbinding: properly add imports for simple types of event parameters There are two ways of doing this: first one is to emit all notifications parameter data into rpcbindings configuration on compile time (event if the parameter has a simple type), and the second one is to fetch parameter type from the manifest on rpcbinding file generation if needed (we always have manifest at this stage, thus it's not a problem to retrieve necessary information). The latter case is chosen to reduce the bindings configuration file size. Signed-off-by: Anna Shaleva --- cli/smartcontract/testdata/nameservice/nns.go | 1 + cli/smartcontract/testdata/notifications/rpcbindings.out | 1 + pkg/compiler/compiler.go | 2 -- pkg/smartcontract/rpcbinding/binding.go | 3 ++- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cli/smartcontract/testdata/nameservice/nns.go b/cli/smartcontract/testdata/nameservice/nns.go index 2b19af793..4c3a76d6f 100644 --- a/cli/smartcontract/testdata/nameservice/nns.go +++ b/cli/smartcontract/testdata/nameservice/nns.go @@ -13,6 +13,7 @@ import ( "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. diff --git a/cli/smartcontract/testdata/notifications/rpcbindings.out b/cli/smartcontract/testdata/notifications/rpcbindings.out index aeeb3ccd7..a3717edd8 100644 --- a/cli/smartcontract/testdata/notifications/rpcbindings.out +++ b/cli/smartcontract/testdata/notifications/rpcbindings.out @@ -11,6 +11,7 @@ import ( "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. diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 6a711921a..eac2bdced 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -347,8 +347,6 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { eStructName := rpcbinding.ToEventBindingName(e.Name) for _, p := range e.Parameters { pStructName := rpcbinding.ToParameterBindingName(p.Name) - // TODO: proper imports handling during bindings generation (see utf8 example). - // Probably, we should always add p type to the list of types. if p.ExtendedType != nil { pName := eStructName + "." + pStructName cfg.Types[pName] = *p.ExtendedType diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index 074b41c49..a3eab9576 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -693,8 +693,9 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st ) if extType, ok = cfg.Types[fullPName]; !ok { extType = binding.ExtendedType{ - Base: abiEvent.Parameters[i].Type, // TODO: properly handle imports for this case (see utf8 example) + Base: abiEvent.Parameters[i].Type, } + addETImports(extType, ctr.NamedTypes, imports) } eTmp.Parameters = append(eTmp.Parameters, EventParamTmpl{ ParamTmpl: binding.ParamTmpl{ From a0d991a500203539cb3455d5b9c2980cc488bdb2 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 3 May 2023 19:13:05 +0300 Subject: [PATCH 14/22] rpcbinding: support map[any]any conversion for extended types Unfortunately, without pre-set user extended types configuration for events and without --guess-eventtypes flag set we are allowed to rely only on manifest information about types. Manifest can't give us a lot of information, but we still need to be able to generate RPC binding. Arrays and structs are correctly handled by the current code, but maps always rely on the fact that map's value type is set. It's not true in the described case, so make the maps type convertor handle this situation in a similar way how arrays are handled. Without this commit the following panic occurs on attempt to generate RPC binding: ``` --- FAIL: TestAssistedRPCBindings/testdata/notifications (0.01s) panic: runtime error: invalid memory address or nil pointer dereference [recovered] panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x7f7c0e] goroutine 190 [running]: testing.tRunner.func1.2({0x109cb40, 0x1d58760}) /usr/local/go/src/testing/testing.go:1396 +0x24e testing.tRunner.func1() /usr/local/go/src/testing/testing.go:1399 +0x39f panic({0x109cb40, 0x1d58760}) /usr/local/go/src/runtime/panic.go:884 +0x212 github.com/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding.extendedTypeToGo({0x22, {0x0, 0x0}, {0x0, 0x0}, 0x0, 0x0, {0x0, 0x0, 0x0}}, ...) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding/binding.go:515 +0x36e github.com/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding.scTypeToGo({0xc000206d92?, 0xc000206d80?}, 0x22, 0xc0005d70e0) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding/binding.go:643 +0x138 github.com/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding.scTemplateToRPC({{0xc00049bb07, 0x7}, 0xc0004c89c0, {0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, ...}, ...}, ...) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding/binding.go:686 +0xbc4 github.com/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding.Generate({{0xc00049bb07, 0x7}, 0xc0004c89c0, {0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, ...}, ...}) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding/binding.go:421 +0x387 github.com/nspcc-dev/neo-go/cli/smartcontract.contractGenerateSomething(0xc00043e2c0, 0x137cd00) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/cli/smartcontract/generate.go:99 +0x855 github.com/nspcc-dev/neo-go/cli/smartcontract.contractGenerateRPCWrapper(0xc00043e2c0?) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/cli/smartcontract/generate.go:60 +0x25 github.com/urfave/cli.HandleAction({0x1048380?, 0x137c660?}, 0x13?) /home/anna/go/pkg/mod/github.com/urfave/cli@v1.22.5/app.go:524 +0x50 github.com/urfave/cli.Command.Run({{0x123539d, 0x13}, {0x0, 0x0}, {0x0, 0x0, 0x0}, {0x12577ad, 0x2a}, {0x127ad35, ...}, ...}, ...) /home/anna/go/pkg/mod/github.com/urfave/cli@v1.22.5/command.go:173 +0x65b github.com/urfave/cli.(*App).RunAsSubcommand(0xc0001f4000, 0xc00043e000) /home/anna/go/pkg/mod/github.com/urfave/cli@v1.22.5/app.go:405 +0x91b github.com/urfave/cli.Command.startApp({{0x12281e1, 0x8}, {0x0, 0x0}, {0x0, 0x0, 0x0}, {0x1254d8a, 0x28}, {0x0, ...}, ...}, ...) /home/anna/go/pkg/mod/github.com/urfave/cli@v1.22.5/command.go:372 +0x6e7 github.com/urfave/cli.Command.Run({{0x12281e1, 0x8}, {0x0, 0x0}, {0x0, 0x0, 0x0}, {0x1254d8a, 0x28}, {0x0, ...}, ...}, ...) /home/anna/go/pkg/mod/github.com/urfave/cli@v1.22.5/command.go:102 +0x825 github.com/urfave/cli.(*App).Run(0xc00024e000, {0xc0004f6420, 0xb, 0xb}) /home/anna/go/pkg/mod/github.com/urfave/cli@v1.22.5/app.go:277 +0x8a7 github.com/nspcc-dev/neo-go/cli/smartcontract.TestAssistedRPCBindings.func1.1(0x9f8829?) /home/anna/Documents/GitProjects/nspcc-dev/neo-go/cli/smartcontract/generate_test.go:395 +0x5fc testing.tRunner(0xc0006824e0, 0xc0004a3680) /usr/local/go/src/testing/testing.go:1446 +0x10b created by testing.(*T).Run /usr/local/go/src/testing/testing.go:1493 +0x35f ``` Signed-off-by: Anna Shaleva --- .../testdata/notifications/config.yml | 8 + .../testdata/notifications/notifications.go | 16 +- .../testdata/notifications/rpcbindings.out | 189 ++++++++++++++++++ cli/smartcontract/testdata/types/config.yml | 2 +- .../testdata/types/rpcbindings.out | 28 +++ cli/smartcontract/testdata/types/types.go | 4 + examples/events/events.go | 6 + examples/events/events.yml | 4 + pkg/smartcontract/rpcbinding/binding.go | 20 +- 9 files changed, 272 insertions(+), 5 deletions(-) diff --git a/cli/smartcontract/testdata/notifications/config.yml b/cli/smartcontract/testdata/notifications/config.yml index 5bb9b8554..5fd018d53 100644 --- a/cli/smartcontract/testdata/notifications/config.yml +++ b/cli/smartcontract/testdata/notifications/config.yml @@ -5,3 +5,11 @@ events: parameters: - name: ! complicated param @#$% type: String + - name: "SomeMap" + parameters: + - name: m + type: Map + - name: "SomeStruct" + parameters: + - name: s + type: Struct \ No newline at end of file diff --git a/cli/smartcontract/testdata/notifications/notifications.go b/cli/smartcontract/testdata/notifications/notifications.go index 369d1cdb6..f679901af 100644 --- a/cli/smartcontract/testdata/notifications/notifications.go +++ b/cli/smartcontract/testdata/notifications/notifications.go @@ -1,7 +1,21 @@ package structs -import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/runtime" +) func Main() { runtime.Notify("! complicated name %$#", "str1") } + +func CrazyMap() { + runtime.Notify("SomeMap", map[int][]map[string][]interop.Hash160{}) +} + +func Struct() { + runtime.Notify("SomeStruct", struct { + I int + B bool + }{I: 123, B: true}) +} diff --git a/cli/smartcontract/testdata/notifications/rpcbindings.out b/cli/smartcontract/testdata/notifications/rpcbindings.out index a3717edd8..ae0724975 100644 --- a/cli/smartcontract/testdata/notifications/rpcbindings.out +++ b/cli/smartcontract/testdata/notifications/rpcbindings.out @@ -82,6 +82,16 @@ type ComplicatedNameEvent struct { ComplicatedParam string } +// SomeMapEvent represents "SomeMap" event emitted by the contract. +type SomeMapEvent struct { + M map[any]any +} + +// SomeStructEvent represents "SomeStruct" event emitted by the contract. +type SomeStructEvent struct { + S []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) @@ -102,6 +112,28 @@ func New(actor Actor) *Contract { return &Contract{actor} } +// 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. @@ -124,6 +156,28 @@ 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) +} + // itemToLedgerBlock converts stack item into *LedgerBlock. func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { if err != nil { @@ -747,3 +801,138 @@ func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error { 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[any]any, error) { + m, ok := item.Value().([]stackitem.MapElement) + if !ok { + return nil, fmt.Errorf("%s is not a map", item.Type().String()) + } + res := make(map[any]any) + for i := range m { + k, err := m[i].Key.Value(), error(nil) + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := m[i].Value.Value(), error(nil) + 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 = 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 S: %w", err) + } + + return nil +} diff --git a/cli/smartcontract/testdata/types/config.yml b/cli/smartcontract/testdata/types/config.yml index b97936c40..98cb40d18 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", "any", "hash160", "hash256", "publicKey", "signature", "bools", "ints", "bytess", "strings", "hash160s", "hash256s", "publicKeys", "signatures", "aAAStrings", "maps", "crazyMaps"] +safemethods: ["bool", "int", "bytes", "string", "any", "hash160", "hash256", "publicKey", "signature", "bools", "ints", "bytess", "strings", "hash160s", "hash256s", "publicKeys", "signatures", "aAAStrings", "maps", "crazyMaps", "anyMaps"] diff --git a/cli/smartcontract/testdata/types/rpcbindings.out b/cli/smartcontract/testdata/types/rpcbindings.out index ca4f0ce36..49f6ac7e5 100644 --- a/cli/smartcontract/testdata/types/rpcbindings.out +++ b/cli/smartcontract/testdata/types/rpcbindings.out @@ -99,6 +99,34 @@ func (c *ContractReader) Any(a any) (any, error) { } (unwrap.Item(c.invoker.Call(Hash, "any", a))) } +// AnyMaps invokes `anyMaps` method of contract. +func (c *ContractReader) AnyMaps(m map[*big.Int]any) (map[*big.Int]any, error) { + return func (item stackitem.Item, err error) (map[*big.Int]any, error) { + if err != nil { + return nil, err + } + return func (item stackitem.Item) (map[*big.Int]any, 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]any) + for i := range m { + k, err := m[i].Key.TryInteger() + if err != nil { + return nil, fmt.Errorf("key %d: %w", i, err) + } + v, err := m[i].Value.Value(), error(nil) + if err != nil { + return nil, fmt.Errorf("value %d: %w", i, err) + } + res[k] = v + } + return res, nil + } (item) + } (unwrap.Item(c.invoker.Call(Hash, "anyMaps", m))) +} + // Bool invokes `bool` method of contract. func (c *ContractReader) Bool(b bool) (bool, error) { return unwrap.Bool(c.invoker.Call(Hash, "bool", b)) diff --git a/cli/smartcontract/testdata/types/types.go b/cli/smartcontract/testdata/types/types.go index c8d819dd5..fcf91a0be 100644 --- a/cli/smartcontract/testdata/types/types.go +++ b/cli/smartcontract/testdata/types/types.go @@ -83,3 +83,7 @@ func Maps(m map[string]string) map[string]string { func CrazyMaps(m map[int][]map[string][]interop.Hash160) map[int][]map[string][]interop.Hash160 { return m } + +func AnyMaps(m map[int]any) map[int]any { + return m +} diff --git a/examples/events/events.go b/examples/events/events.go index 28254ee4a..cd45ff15f 100644 --- a/examples/events/events.go +++ b/examples/events/events.go @@ -1,6 +1,7 @@ package events import ( + "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" ) @@ -24,6 +25,11 @@ func NotifySomeMap(arg map[string]int) { runtime.Notify("SomeMap", arg) } +// NotifySomeCrazyMap emits notification with complicated Map. +func NotifySomeCrazyMap(arg map[int][]map[string][]interop.Hash160) { + runtime.Notify("SomeCrazyMap", arg) +} + // NotifySomeArray emits notification with Array. func NotifySomeArray(arg []int) { runtime.Notify("SomeArray", arg) diff --git a/examples/events/events.yml b/examples/events/events.yml index f16bc347e..7b3c05a40 100644 --- a/examples/events/events.yml +++ b/examples/events/events.yml @@ -18,6 +18,10 @@ events: parameters: - name: m type: Map + - name: SomeCrazyMap + parameters: + - name: m + type: Map - name: SomeArray parameters: - name: a diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index a3eab9576..eb38be084 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -511,7 +511,12 @@ func extendedTypeToGo(et binding.ExtendedType, named map[string]binding.Extended case smartcontract.MapType: kt, _ := extendedTypeToGo(binding.ExtendedType{Base: et.Key}, named) - vt, _ := extendedTypeToGo(*et.Value, named) + var vt string + if et.Value != nil { + vt, _ = extendedTypeToGo(*et.Value, named) + } else { + vt = "any" + } return "map[" + kt + "]" + vt, "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" case smartcontract.InteropInterfaceType: return "any", "" @@ -606,8 +611,9 @@ func etTypeConverter(et binding.ExtendedType, v string) string { }, v) case smartcontract.MapType: - at, _ := extendedTypeToGo(et, nil) - return `func (item stackitem.Item) (` + at + `, error) { + if et.Value != nil { + 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()) @@ -626,6 +632,14 @@ func etTypeConverter(et binding.ExtendedType, v string) string { } return res, nil } (` + v + `)` + } + return etTypeConverter(binding.ExtendedType{ + Base: smartcontract.MapType, + Key: et.Key, + Value: &binding.ExtendedType{ + Base: smartcontract.AnyType, + }, + }, v) case smartcontract.InteropInterfaceType: return "item.Value(), nil" case smartcontract.VoidType: From 3e2755e66d95a9aa381b3b8156f3b0b3fed1fc7f Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Thu, 25 May 2023 18:03:05 +0300 Subject: [PATCH 15/22] 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 +} From 0195aae8248d0b565994933aa73095fcf14c58e9 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Thu, 25 May 2023 18:39:27 +0300 Subject: [PATCH 16/22] cli: add test for --guessed-eventtypes RPC binding generator flag Currently we have problems with unnamed structs used as a parameter for notifications, because there's no one-to-one correspondence between notification parameter and the list of extended types used by the whole contract. Otherwise eventtypes guessing works OK. Signed-off-by: Anna Shaleva --- cli/smartcontract/generate_test.go | 19 +- .../testdata/notifications/config_guessed.yml | 19 + .../notifications/rpcbindings_guessed.out | 1152 +++++++++++++++++ 3 files changed, 1183 insertions(+), 7 deletions(-) create mode 100644 cli/smartcontract/testdata/notifications/config_guessed.yml create mode 100755 cli/smartcontract/testdata/notifications/rpcbindings_guessed.out diff --git a/cli/smartcontract/generate_test.go b/cli/smartcontract/generate_test.go index a9dfaca67..6b4d5aa2f 100644 --- a/cli/smartcontract/generate_test.go +++ b/cli/smartcontract/generate_test.go @@ -379,7 +379,7 @@ func TestAssistedRPCBindings(t *testing.T) { app := cli.NewApp() app.Commands = NewCommands() - var checkBinding = func(source string, suffix ...string) { + var checkBinding = func(source string, guessEventTypes bool, suffix ...string) { testName := source if len(suffix) != 0 { testName += suffix[0] @@ -394,13 +394,17 @@ func TestAssistedRPCBindings(t *testing.T) { 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", + cmd := []string{"", "contract", "compile", "--in", source, "--config", configFile, "--manifest", manifestF, "--bindings", bindingF, "--out", nefF, - })) + } + if guessEventTypes { + cmd = append(cmd, "--guess-eventtypes") + } + require.NoError(t, app.Run(cmd)) outFile := filepath.Join(tmpDir, "out.go") require.NoError(t, app.Run([]string{"", "contract", "generate-rpcwrapper", "--config", bindingF, @@ -423,10 +427,11 @@ 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") + checkBinding(filepath.Join("testdata", "types"), false) + checkBinding(filepath.Join("testdata", "structs"), false) + checkBinding(filepath.Join("testdata", "notifications"), false) + checkBinding(filepath.Join("testdata", "notifications"), false, "_extended") + checkBinding(filepath.Join("testdata", "notifications"), true, "_guessed") require.False(t, rewriteExpectedOutputs) } diff --git a/cli/smartcontract/testdata/notifications/config_guessed.yml b/cli/smartcontract/testdata/notifications/config_guessed.yml new file mode 100644 index 000000000..ef2866318 --- /dev/null +++ b/cli/smartcontract/testdata/notifications/config_guessed.yml @@ -0,0 +1,19 @@ +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 + - name: "SomeStruct" + parameters: + - name: s + type: Struct + - name: "SomeArray" + parameters: + - name: a + type: Array \ No newline at end of file diff --git a/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out b/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out new file mode 100755 index 000000000..09bb03117 --- /dev/null +++ b/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out @@ -0,0 +1,1152 @@ +// 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} + +// 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 +} + +// Unnamed is a contract-specific unnamed type used by its methods. +type Unnamed struct { + I *big.Int + B bool +} + +// 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 []any +} + +// 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) +} + +// 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 +} + +// itemToUnnamed converts stack item into *Unnamed. +func itemToUnnamed(item stackitem.Item, err error) (*Unnamed, error) { + if err != nil { + return nil, err + } + var res = new(Unnamed) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of Unnamed from the given stack item +// and returns an error if so. +func (res *Unnamed) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 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 +} + +// 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) { + 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, 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 + } (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 + } (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 = 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 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 +} From 6379bcc15ac444cdc15bc968e23b235934a5f7e6 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Thu, 25 May 2023 18:45:59 +0300 Subject: [PATCH 17/22] compiler: properly set extended type name for unnamed structs After the struct was registered as "unnamed", it has the own unique name. Signed-off-by: Anna Shaleva --- .../notifications/rpcbindings_guessed.out | 17 ++--------------- pkg/compiler/debug.go | 7 ++++++- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out b/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out index 09bb03117..c9c20ae5b 100755 --- a/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out +++ b/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out @@ -95,7 +95,7 @@ type SomeMapEvent struct { // SomeStructEvent represents "SomeStruct" event emitted by the contract. type SomeStructEvent struct { - S []any + S *Unnamed } // SomeArrayEvent represents "SomeArray" event emitted by the contract. @@ -1052,20 +1052,7 @@ func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error { err error ) index++ - e.S, 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]) + e.S, err = itemToUnnamed(arr[index], nil) if err != nil { return fmt.Errorf("field S: %w", err) } diff --git a/pkg/compiler/debug.go b/pkg/compiler/debug.go index 0eb18bb5a..a583fc3bd 100644 --- a/pkg/compiler/debug.go +++ b/pkg/compiler/debug.go @@ -386,10 +386,12 @@ func (c *codegen) scAndVMTypeFromType(t types.Type, exts map[string]binding.Exte over.TypeName = "map[" + t.Key().String() + "]" + over.TypeName return smartcontract.MapType, stackitem.MapT, over, et case *types.Struct: + var extName string if isNamed { over.Package = named.Obj().Pkg().Path() over.TypeName = named.Obj().Pkg().Name() + "." + named.Obj().Name() _ = c.genStructExtended(t, over.TypeName, exts) + extName = over.TypeName } else { name := "unnamed" if exts != nil { @@ -398,11 +400,14 @@ func (c *codegen) scAndVMTypeFromType(t types.Type, exts map[string]binding.Exte } _ = c.genStructExtended(t, name, exts) } + // For bindings configurator this structure becomes named in fact. Its name + // is "unnamed[X...X]". + extName = name } return smartcontract.ArrayType, stackitem.StructT, over, &binding.ExtendedType{ // Value-less, refer to exts. Base: smartcontract.ArrayType, - Name: over.TypeName, + Name: extName, } case *types.Slice: From 865bd6c9cc78be20587be7d64aacb815589ff8fe Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Thu, 25 May 2023 19:17:49 +0300 Subject: [PATCH 18/22] compiler: compare emitted event params if --guess-eventtypes enabled In this case emitted event parameters should match from invocation to invocation. It's an error otherwise (and if the type is not Any). Signed-off-by: Anna Shaleva --- cli/smartcontract/generate_test.go | 49 ++++ .../testdata/invalid5/invalid.go | 7 + .../testdata/invalid5/invalid.yml | 1 + .../testdata/invalid6/invalid.go | 7 + .../testdata/invalid6/invalid.yml | 6 + .../testdata/invalid7/invalid.go | 7 + .../testdata/invalid7/invalid.yml | 8 + .../testdata/invalid8/invalid.go | 17 ++ .../testdata/invalid8/invalid.yml | 6 + pkg/compiler/compiler.go | 35 ++- pkg/smartcontract/binding/generate.go | 31 +++ pkg/smartcontract/binding/generate_test.go | 229 ++++++++++++++++++ 12 files changed, 399 insertions(+), 4 deletions(-) create mode 100644 cli/smartcontract/testdata/invalid5/invalid.go create mode 100644 cli/smartcontract/testdata/invalid5/invalid.yml create mode 100644 cli/smartcontract/testdata/invalid6/invalid.go create mode 100644 cli/smartcontract/testdata/invalid6/invalid.yml create mode 100644 cli/smartcontract/testdata/invalid7/invalid.go create mode 100644 cli/smartcontract/testdata/invalid7/invalid.yml create mode 100644 cli/smartcontract/testdata/invalid8/invalid.go create mode 100644 cli/smartcontract/testdata/invalid8/invalid.yml create mode 100644 pkg/smartcontract/binding/generate_test.go diff --git a/cli/smartcontract/generate_test.go b/cli/smartcontract/generate_test.go index 6b4d5aa2f..e2a31d913 100644 --- a/cli/smartcontract/generate_test.go +++ b/cli/smartcontract/generate_test.go @@ -499,3 +499,52 @@ callflags: "--config", cfgPath, "--out", "zzz") }) } + +func TestCompile_GuessEventTypes(t *testing.T) { + app := cli.NewApp() + app.Commands = NewCommands() + app.ExitErrHandler = func(*cli.Context, error) {} + + checkError := func(t *testing.T, msg string, args ...string) { + // cli.ExitError doesn't implement wraping properly, so we check for an error message. + err := app.Run(args) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), msg), "got: %v", err) + } + check := func(t *testing.T, source string, expectedErrText string) { + tmpDir := t.TempDir() + configFile := filepath.Join(source, "invalid.yml") + manifestF := filepath.Join(tmpDir, "invalid.manifest.json") + bindingF := filepath.Join(tmpDir, "invalid.binding.yml") + nefF := filepath.Join(tmpDir, "invalid.out.nef") + cmd := []string{"", "contract", "compile", + "--in", source, + "--config", configFile, + "--manifest", manifestF, + "--bindings", bindingF, + "--out", nefF, + "--guess-eventtypes", + } + checkError(t, expectedErrText, cmd...) + } + + t.Run("not declared in manifest", func(t *testing.T) { + check(t, filepath.Join("testdata", "invalid5"), "inconsistent usages of event `Non declared event`: not declared in the contract config") + }) + t.Run("invalid number of params", func(t *testing.T) { + check(t, filepath.Join("testdata", "invalid6"), "inconsistent usages of event `SomeEvent` against config: number of params mismatch: 2 vs 1") + }) + /* + // TODO: this on is a controversial one. If event information is provided in the config file, then conversion code + // will be emitted by the compiler according to the parameter type provided via config. Thus, we can be sure that + // either event parameter has the type specified in the config file or the execution of the contract will fail. + // Thus, this testcase is always failing (no compilation error occures). + // Question: do we want to compare `RealType` of the emitted parameter with the one expected in the manifest? + t.Run("SC parameter type mismatch", func(t *testing.T) { + check(t, filepath.Join("testdata", "invalid7"), "inconsistent usages of event `SomeEvent` against config: number of params mismatch: 2 vs 1") + }) + */ + t.Run("extended types mismatch", func(t *testing.T) { + check(t, filepath.Join("testdata", "invalid8"), "inconsistent usages of event `SomeEvent`: extended type of param #0 mismatch") + }) +} diff --git a/cli/smartcontract/testdata/invalid5/invalid.go b/cli/smartcontract/testdata/invalid5/invalid.go new file mode 100644 index 000000000..0cae2ed71 --- /dev/null +++ b/cli/smartcontract/testdata/invalid5/invalid.go @@ -0,0 +1,7 @@ +package invalid5 + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +func Main() { + runtime.Notify("Non declared event") +} diff --git a/cli/smartcontract/testdata/invalid5/invalid.yml b/cli/smartcontract/testdata/invalid5/invalid.yml new file mode 100644 index 000000000..eda948267 --- /dev/null +++ b/cli/smartcontract/testdata/invalid5/invalid.yml @@ -0,0 +1 @@ +name: Test undeclared event \ No newline at end of file diff --git a/cli/smartcontract/testdata/invalid6/invalid.go b/cli/smartcontract/testdata/invalid6/invalid.go new file mode 100644 index 000000000..dd3a3ecdd --- /dev/null +++ b/cli/smartcontract/testdata/invalid6/invalid.go @@ -0,0 +1,7 @@ +package invalid6 + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +func Main() { + runtime.Notify("SomeEvent", "p1", "p2") +} diff --git a/cli/smartcontract/testdata/invalid6/invalid.yml b/cli/smartcontract/testdata/invalid6/invalid.yml new file mode 100644 index 000000000..13933803e --- /dev/null +++ b/cli/smartcontract/testdata/invalid6/invalid.yml @@ -0,0 +1,6 @@ +name: Test undeclared event +events: + - name: SomeEvent + parameters: + - name: p1 + type: String \ No newline at end of file diff --git a/cli/smartcontract/testdata/invalid7/invalid.go b/cli/smartcontract/testdata/invalid7/invalid.go new file mode 100644 index 000000000..41bc20c47 --- /dev/null +++ b/cli/smartcontract/testdata/invalid7/invalid.go @@ -0,0 +1,7 @@ +package invalid7 + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +func Main() { + runtime.Notify("SomeEvent", "p1", 5) +} diff --git a/cli/smartcontract/testdata/invalid7/invalid.yml b/cli/smartcontract/testdata/invalid7/invalid.yml new file mode 100644 index 000000000..a217deefd --- /dev/null +++ b/cli/smartcontract/testdata/invalid7/invalid.yml @@ -0,0 +1,8 @@ +name: Test undeclared event +events: + - name: SomeEvent + parameters: + - name: p1 + type: String + - name: p2 + type: String \ No newline at end of file diff --git a/cli/smartcontract/testdata/invalid8/invalid.go b/cli/smartcontract/testdata/invalid8/invalid.go new file mode 100644 index 000000000..dba9173ca --- /dev/null +++ b/cli/smartcontract/testdata/invalid8/invalid.go @@ -0,0 +1,17 @@ +package invalid8 + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +type SomeStruct1 struct { + Field1 int +} + +type SomeStruct2 struct { + Field2 string +} + +func Main() { + // Inconsistent event params usages (different named types throughout the usages). + runtime.Notify("SomeEvent", SomeStruct1{Field1: 123}) + runtime.Notify("SomeEvent", SomeStruct2{Field2: "str"}) +} diff --git a/cli/smartcontract/testdata/invalid8/invalid.yml b/cli/smartcontract/testdata/invalid8/invalid.yml new file mode 100644 index 000000000..6c0c34748 --- /dev/null +++ b/cli/smartcontract/testdata/invalid8/invalid.yml @@ -0,0 +1,6 @@ +name: Test undeclared event +events: + - name: SomeEvent + parameters: + - name: p1 + type: Array \ No newline at end of file diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index eac2bdced..2b90ac07e 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -356,16 +356,43 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { if o.GuessEventTypes { if len(di.EmittedEvents) > 0 { for eventName, eventUsages := range di.EmittedEvents { + var manifestEvent HybridEvent + for _, e := range o.ContractEvents { + if e.Name == eventName { + manifestEvent = e + break + } + } + if len(manifestEvent.Name) == 0 { + return nil, fmt.Errorf("inconsistent usages of event `%s`: not declared in the contract config", eventName) + } + exampleUsage := eventUsages[0] + for _, usage := range eventUsages { + if len(usage.Params) != len(manifestEvent.Parameters) { + return nil, fmt.Errorf("inconsistent usages of event `%s` against config: number of params mismatch: %d vs %d", eventName, len(exampleUsage.Params), len(manifestEvent.Parameters)) + } + for i, actual := range usage.Params { + mParam := manifestEvent.Parameters[i] + // TODO: see the TestCompile_GuessEventTypes, "SC parameter type mismatch" section, + // do we want to compare with actual.RealType? The conversion code is emitted by the + // compiler for it, so we expect the parameter to be of the proper type. + if !(mParam.Type == smartcontract.AnyType || actual.TypeSC == mParam.Type) { + return nil, fmt.Errorf("inconsistent usages of event `%s` against config: SC type of param #%d mismatch: %s vs %s", eventName, i, actual.TypeSC, mParam.Type) + } + expected := exampleUsage.Params[i] + if !actual.ExtendedType.Equals(expected.ExtendedType) { + return nil, fmt.Errorf("inconsistent usages of event `%s`: extended type of param #%d mismatch", eventName, i) + } + } + } eBindingName := rpcbinding.ToEventBindingName(eventName) - // Take into account the first usage only. - // TODO: extend it to the rest of invocations. - for typeName, extType := range eventUsages[0].ExtTypes { + for typeName, extType := range exampleUsage.ExtTypes { if _, ok := cfg.NamedTypes[typeName]; !ok { cfg.NamedTypes[typeName] = extType } } - for _, p := range eventUsages[0].Params { + for _, p := range exampleUsage.Params { pBindingName := rpcbinding.ToParameterBindingName(p.Name) pname := eBindingName + "." + pBindingName if p.RealType.TypeName != "" { diff --git a/pkg/smartcontract/binding/generate.go b/pkg/smartcontract/binding/generate.go index 33bb21dd2..bd7d42cbc 100644 --- a/pkg/smartcontract/binding/generate.go +++ b/pkg/smartcontract/binding/generate.go @@ -265,3 +265,34 @@ func TemplateFromManifest(cfg Config, scTypeConverter func(string, smartcontract func upperFirst(s string) string { return strings.ToUpper(s[0:1]) + s[1:] } + +// Equals compares two extended types field-by-field and returns true if they are +// equal. +func (e *ExtendedType) Equals(other *ExtendedType) bool { + if e == nil && other == nil { + return true + } + if e != nil && other == nil || + e == nil && other != nil { + return false + } + if !((e.Base == other.Base || (e.Base == smartcontract.ByteArrayType || e.Base == smartcontract.StringType) && + (other.Base == smartcontract.ByteArrayType || other.Base == smartcontract.StringType)) && + e.Name == other.Name && + e.Interface == other.Interface && + e.Key == other.Key) { + return false + } + if len(e.Fields) != len(other.Fields) { + return false + } + for i := range e.Fields { + if e.Fields[i].Field != other.Fields[i].Field { + return false + } + if !e.Fields[i].ExtendedType.Equals(&other.Fields[i].ExtendedType) { + return false + } + } + return (e.Value == nil && other.Value == nil) || (e.Value != nil && other.Value != nil && e.Value.Equals(other.Value)) +} diff --git a/pkg/smartcontract/binding/generate_test.go b/pkg/smartcontract/binding/generate_test.go new file mode 100644 index 000000000..64522d28c --- /dev/null +++ b/pkg/smartcontract/binding/generate_test.go @@ -0,0 +1,229 @@ +package binding + +import ( + "testing" + + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/stretchr/testify/assert" +) + +func TestExtendedType_Equals(t *testing.T) { + crazyT := ExtendedType{ + Base: smartcontract.StringType, + Name: "qwertyu", + Interface: "qwerty", + Key: smartcontract.BoolType, + Value: &ExtendedType{ + Base: smartcontract.IntegerType, + }, + Fields: []FieldExtendedType{ + { + Field: "qwe", + ExtendedType: ExtendedType{ + Base: smartcontract.IntegerType, + Name: "qwer", + Interface: "qw", + Key: smartcontract.ArrayType, + Fields: []FieldExtendedType{ + { + Field: "as", + }, + }, + }, + }, + { + Field: "asf", + ExtendedType: ExtendedType{ + Base: smartcontract.BoolType, + }, + }, + { + Field: "sffg", + ExtendedType: ExtendedType{ + Base: smartcontract.AnyType, + }, + }, + }, + } + tcs := map[string]struct { + a *ExtendedType + b *ExtendedType + expectedRes bool + }{ + "both nil": { + a: nil, + b: nil, + expectedRes: true, + }, + "a is nil": { + a: nil, + b: &ExtendedType{}, + expectedRes: false, + }, + "b is nil": { + a: &ExtendedType{}, + b: nil, + expectedRes: false, + }, + "base mismatch": { + a: &ExtendedType{ + Base: smartcontract.StringType, + }, + b: &ExtendedType{ + Base: smartcontract.IntegerType, + }, + expectedRes: false, + }, + "name mismatch": { + a: &ExtendedType{ + Base: smartcontract.ArrayType, + Name: "q", + }, + b: &ExtendedType{ + Base: smartcontract.ArrayType, + Name: "w", + }, + expectedRes: false, + }, + "number of fields mismatch": { + a: &ExtendedType{ + Base: smartcontract.ArrayType, + Name: "q", + Fields: []FieldExtendedType{ + { + Field: "IntField", + ExtendedType: ExtendedType{Base: smartcontract.IntegerType}, + }, + }, + }, + b: &ExtendedType{ + Base: smartcontract.ArrayType, + Name: "w", + Fields: []FieldExtendedType{ + { + Field: "IntField", + ExtendedType: ExtendedType{Base: smartcontract.IntegerType}, + }, + { + Field: "BoolField", + ExtendedType: ExtendedType{Base: smartcontract.BoolType}, + }, + }, + }, + expectedRes: false, + }, + "field names mismatch": { + a: &ExtendedType{ + Base: smartcontract.ArrayType, + Fields: []FieldExtendedType{ + { + Field: "IntField", + ExtendedType: ExtendedType{Base: smartcontract.IntegerType}, + }, + }, + }, + b: &ExtendedType{ + Base: smartcontract.ArrayType, + Fields: []FieldExtendedType{ + { + Field: "BoolField", + ExtendedType: ExtendedType{Base: smartcontract.BoolType}, + }, + }, + }, + expectedRes: false, + }, + "field types mismatch": { + a: &ExtendedType{ + Base: smartcontract.ArrayType, + Fields: []FieldExtendedType{ + { + Field: "Field", + ExtendedType: ExtendedType{Base: smartcontract.IntegerType}, + }, + }, + }, + b: &ExtendedType{ + Base: smartcontract.ArrayType, + Fields: []FieldExtendedType{ + { + Field: "Field", + ExtendedType: ExtendedType{Base: smartcontract.BoolType}, + }, + }, + }, + expectedRes: false, + }, + "interface mismatch": { + a: &ExtendedType{Interface: "iterator"}, + b: &ExtendedType{Interface: "unknown"}, + expectedRes: false, + }, + "value is nil": { + a: &ExtendedType{ + Base: smartcontract.StringType, + }, + b: &ExtendedType{ + Base: smartcontract.StringType, + }, + expectedRes: true, + }, + "a value is not nil": { + a: &ExtendedType{ + Base: smartcontract.ArrayType, + Value: &ExtendedType{}, + }, + b: &ExtendedType{ + Base: smartcontract.ArrayType, + }, + expectedRes: false, + }, + "b value is not nil": { + a: &ExtendedType{ + Base: smartcontract.ArrayType, + }, + b: &ExtendedType{ + Base: smartcontract.ArrayType, + Value: &ExtendedType{}, + }, + expectedRes: false, + }, + "byte array tolerance for a": { + a: &ExtendedType{ + Base: smartcontract.StringType, + }, + b: &ExtendedType{ + Base: smartcontract.ByteArrayType, + }, + expectedRes: true, + }, + "byte array tolerance for b": { + a: &ExtendedType{ + Base: smartcontract.ByteArrayType, + }, + b: &ExtendedType{ + Base: smartcontract.StringType, + }, + expectedRes: true, + }, + "key mismatch": { + a: &ExtendedType{ + Key: smartcontract.StringType, + }, + b: &ExtendedType{ + Key: smartcontract.IntegerType, + }, + expectedRes: false, + }, + "good nested": { + a: &crazyT, + b: &crazyT, + expectedRes: true, + }, + } + for name, tc := range tcs { + t.Run(name, func(t *testing.T) { + assert.Equal(t, tc.expectedRes, tc.a.Equals(tc.b)) + }) + } +} From 44dfe8342dea58c3f652907511a8d805204737cf Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Mon, 29 May 2023 18:50:36 +0300 Subject: [PATCH 19/22] compiler: disallow named types redeclaration via contract config Signed-off-by: Anna Shaleva --- cli/smartcontract/generate_test.go | 3 +++ cli/smartcontract/testdata/invalid9/invalid.go | 12 ++++++++++++ cli/smartcontract/testdata/invalid9/invalid.yml | 16 ++++++++++++++++ pkg/compiler/compiler.go | 4 +++- 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 cli/smartcontract/testdata/invalid9/invalid.go create mode 100644 cli/smartcontract/testdata/invalid9/invalid.yml diff --git a/cli/smartcontract/generate_test.go b/cli/smartcontract/generate_test.go index e2a31d913..6c6a63f90 100644 --- a/cli/smartcontract/generate_test.go +++ b/cli/smartcontract/generate_test.go @@ -547,4 +547,7 @@ func TestCompile_GuessEventTypes(t *testing.T) { t.Run("extended types mismatch", func(t *testing.T) { check(t, filepath.Join("testdata", "invalid8"), "inconsistent usages of event `SomeEvent`: extended type of param #0 mismatch") }) + t.Run("named types redeclare", func(t *testing.T) { + check(t, filepath.Join("testdata", "invalid9"), "configured declared named type intersects with the contract's one: `invalid9.NamedStruct`") + }) } diff --git a/cli/smartcontract/testdata/invalid9/invalid.go b/cli/smartcontract/testdata/invalid9/invalid.go new file mode 100644 index 000000000..5036bca9f --- /dev/null +++ b/cli/smartcontract/testdata/invalid9/invalid.go @@ -0,0 +1,12 @@ +package invalid9 + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +type NamedStruct struct { + SomeInt int +} + +func Main() NamedStruct { + runtime.Notify("SomeEvent", []interface{}{123}) + return NamedStruct{SomeInt: 123} +} diff --git a/cli/smartcontract/testdata/invalid9/invalid.yml b/cli/smartcontract/testdata/invalid9/invalid.yml new file mode 100644 index 000000000..40bbf66db --- /dev/null +++ b/cli/smartcontract/testdata/invalid9/invalid.yml @@ -0,0 +1,16 @@ +name: Test undeclared event +events: + - name: SomeEvent + parameters: + - name: p1 + type: Array + extendedtype: + base: Array + name: invalid9.NamedStruct +namedtypes: + invalid9.NamedStruct: + base: Array + name: invalid9.NamedStruct + fields: + - field: SomeInt + base: Integer diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 2b90ac07e..a7826a552 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -340,7 +340,9 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { cfg.NamedTypes = di.NamedTypes } for name, et := range o.DeclaredNamedTypes { - // TODO: handle name conflict (it can happen due to invalid user input e.g.) + if _, ok := cfg.NamedTypes[name]; ok { + return nil, fmt.Errorf("configured declared named type intersects with the contract's one: `%s`", name) + } cfg.NamedTypes[name] = et } for _, e := range o.ContractEvents { From 2ce1454ef51b733e2732f1895db11863296db259 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 30 May 2023 11:59:16 +0300 Subject: [PATCH 20/22] cli: improve error message for binding template Signed-off-by: Anna Shaleva --- cli/smartcontract/testdata/nameservice/nns.go | 4 ++-- cli/smartcontract/testdata/nex/nex.go | 2 +- cli/smartcontract/testdata/notifications/rpcbindings.out | 8 ++++---- .../testdata/notifications/rpcbindings_extended.out | 8 ++++---- .../testdata/notifications/rpcbindings_guessed.out | 8 ++++---- cli/smartcontract/testdata/verifyrpc/verify.go | 2 +- pkg/smartcontract/rpcbinding/binding.go | 2 +- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cli/smartcontract/testdata/nameservice/nns.go b/cli/smartcontract/testdata/nameservice/nns.go index 4c3a76d6f..076aaa438 100644 --- a/cli/smartcontract/testdata/nameservice/nns.go +++ b/cli/smartcontract/testdata/nameservice/nns.go @@ -354,7 +354,7 @@ func SetAdminEventsFromApplicationLog(log *result.ApplicationLog) ([]*SetAdminEv event := new(SetAdminEvent) err := event.FromStackItem(e.Item) if err != nil { - return nil, fmt.Errorf("failed to deserialize SetAdminEvent from stackitem (execution %d, event %d): %w", i, j, err) + return nil, fmt.Errorf("failed to deserialize SetAdminEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } @@ -447,7 +447,7 @@ func RenewEventsFromApplicationLog(log *result.ApplicationLog) ([]*RenewEvent, e event := new(RenewEvent) err := event.FromStackItem(e.Item) if err != nil { - return nil, fmt.Errorf("failed to deserialize RenewEvent from stackitem (execution %d, event %d): %w", i, j, err) + return nil, fmt.Errorf("failed to deserialize RenewEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } diff --git a/cli/smartcontract/testdata/nex/nex.go b/cli/smartcontract/testdata/nex/nex.go index f558fb513..ac530f79c 100644 --- a/cli/smartcontract/testdata/nex/nex.go +++ b/cli/smartcontract/testdata/nex/nex.go @@ -258,7 +258,7 @@ func OnMintEventsFromApplicationLog(log *result.ApplicationLog) ([]*OnMintEvent, event := new(OnMintEvent) err := event.FromStackItem(e.Item) if err != nil { - return nil, fmt.Errorf("failed to deserialize OnMintEvent from stackitem (execution %d, event %d): %w", i, j, err) + return nil, fmt.Errorf("failed to deserialize OnMintEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } diff --git a/cli/smartcontract/testdata/notifications/rpcbindings.out b/cli/smartcontract/testdata/notifications/rpcbindings.out index ea40f64ea..4b95b5a65 100644 --- a/cli/smartcontract/testdata/notifications/rpcbindings.out +++ b/cli/smartcontract/testdata/notifications/rpcbindings.out @@ -784,7 +784,7 @@ func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*Com 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) + return nil, fmt.Errorf("failed to deserialize ComplicatedNameEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } @@ -845,7 +845,7 @@ func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEven 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) + return nil, fmt.Errorf("failed to deserialize SomeMapEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } @@ -915,7 +915,7 @@ func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStru 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) + return nil, fmt.Errorf("failed to deserialize SomeStructEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } @@ -980,7 +980,7 @@ func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArray 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) + return nil, fmt.Errorf("failed to deserialize SomeArrayEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } diff --git a/cli/smartcontract/testdata/notifications/rpcbindings_extended.out b/cli/smartcontract/testdata/notifications/rpcbindings_extended.out index 0c64320a8..9b269e2eb 100755 --- a/cli/smartcontract/testdata/notifications/rpcbindings_extended.out +++ b/cli/smartcontract/testdata/notifications/rpcbindings_extended.out @@ -830,7 +830,7 @@ func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*Com 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) + return nil, fmt.Errorf("failed to deserialize ComplicatedNameEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } @@ -891,7 +891,7 @@ func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEven 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) + return nil, fmt.Errorf("failed to deserialize SomeMapEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } @@ -1011,7 +1011,7 @@ func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStru 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) + return nil, fmt.Errorf("failed to deserialize SomeStructEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } @@ -1063,7 +1063,7 @@ func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArray 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) + return nil, fmt.Errorf("failed to deserialize SomeArrayEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } diff --git a/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out b/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out index c9c20ae5b..0fba2708c 100755 --- a/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out +++ b/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out @@ -830,7 +830,7 @@ func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*Com 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) + return nil, fmt.Errorf("failed to deserialize ComplicatedNameEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } @@ -891,7 +891,7 @@ func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEven 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) + return nil, fmt.Errorf("failed to deserialize SomeMapEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } @@ -1024,7 +1024,7 @@ func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStru 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) + return nil, fmt.Errorf("failed to deserialize SomeStructEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } @@ -1076,7 +1076,7 @@ func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArray 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) + return nil, fmt.Errorf("failed to deserialize SomeArrayEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } diff --git a/cli/smartcontract/testdata/verifyrpc/verify.go b/cli/smartcontract/testdata/verifyrpc/verify.go index 6835a7e4e..dd3539901 100644 --- a/cli/smartcontract/testdata/verifyrpc/verify.go +++ b/cli/smartcontract/testdata/verifyrpc/verify.go @@ -93,7 +93,7 @@ func HelloWorldEventsFromApplicationLog(log *result.ApplicationLog) ([]*HelloWor event := new(HelloWorldEvent) err := event.FromStackItem(e.Item) if err != nil { - return nil, fmt.Errorf("failed to deserialize HelloWorldEvent from stackitem (execution %d, event %d): %w", i, j, err) + return nil, fmt.Errorf("failed to deserialize HelloWorldEvent from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index eb38be084..746dcb34b 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -278,7 +278,7 @@ func {{$e.Name}}sFromApplicationLog(log *result.ApplicationLog) ([]*{{$e.Name}}, event := new({{$e.Name}}) err := event.FromStackItem(e.Item) if err != nil { - return nil, fmt.Errorf("failed to deserialize {{$e.Name}} from stackitem (execution %d, event %d): %w", i, j, err) + return nil, fmt.Errorf("failed to deserialize {{$e.Name}} from stackitem (execution #%d, event #%d): %w", i, j, err) } res = append(res, event) } From 8ae4a1e957a17087a3d28b7270561971d4566263 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 30 May 2023 17:34:51 +0300 Subject: [PATCH 21/22] rpcbinding: adjust comment of `FromStackItem` template methods Signed-off-by: Anna Shaleva --- cli/smartcontract/testdata/nameservice/nns.go | 8 ++--- cli/smartcontract/testdata/nex/nex.go | 4 +-- .../testdata/notifications/rpcbindings.out | 28 ++++++++--------- .../notifications/rpcbindings_extended.out | 30 +++++++++---------- .../notifications/rpcbindings_guessed.out | 30 +++++++++---------- .../testdata/structs/rpcbindings.out | 30 +++++++++---------- .../testdata/verifyrpc/verify.go | 4 +-- pkg/smartcontract/rpcbinding/binding.go | 6 ++-- 8 files changed, 70 insertions(+), 70 deletions(-) diff --git a/cli/smartcontract/testdata/nameservice/nns.go b/cli/smartcontract/testdata/nameservice/nns.go index 076aaa438..82cc8374d 100644 --- a/cli/smartcontract/testdata/nameservice/nns.go +++ b/cli/smartcontract/testdata/nameservice/nns.go @@ -363,8 +363,8 @@ func SetAdminEventsFromApplicationLog(log *result.ApplicationLog) ([]*SetAdminEv return res, nil } -// FromStackItem converts provided stackitem.Array to SetAdminEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to SetAdminEvent or +// returns an error if it's not possible to do to so. func (e *SetAdminEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") @@ -456,8 +456,8 @@ func RenewEventsFromApplicationLog(log *result.ApplicationLog) ([]*RenewEvent, e return res, nil } -// FromStackItem converts provided stackitem.Array to RenewEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to RenewEvent or +// returns an error if it's not possible to do to so. func (e *RenewEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") diff --git a/cli/smartcontract/testdata/nex/nex.go b/cli/smartcontract/testdata/nex/nex.go index ac530f79c..0d716adde 100644 --- a/cli/smartcontract/testdata/nex/nex.go +++ b/cli/smartcontract/testdata/nex/nex.go @@ -267,8 +267,8 @@ func OnMintEventsFromApplicationLog(log *result.ApplicationLog) ([]*OnMintEvent, return res, nil } -// FromStackItem converts provided stackitem.Array to OnMintEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to OnMintEvent or +// returns an error if it's not possible to do to so. func (e *OnMintEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") diff --git a/cli/smartcontract/testdata/notifications/rpcbindings.out b/cli/smartcontract/testdata/notifications/rpcbindings.out index 4b95b5a65..1e873d611 100644 --- a/cli/smartcontract/testdata/notifications/rpcbindings.out +++ b/cli/smartcontract/testdata/notifications/rpcbindings.out @@ -216,7 +216,7 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { } // FromStackItem retrieves fields of LedgerBlock from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -338,7 +338,7 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) } // FromStackItem retrieves fields of LedgerBlockSR from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -476,7 +476,7 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction } // FromStackItem retrieves fields of LedgerTransaction from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -572,7 +572,7 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans } // FromStackItem retrieves fields of LedgerTransactionSigner from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -699,7 +699,7 @@ func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnes } // FromStackItem retrieves fields of LedgerWitnessCondition from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerWitnessCondition) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -739,7 +739,7 @@ func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule } // FromStackItem retrieves fields of LedgerWitnessRule from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerWitnessRule) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -793,8 +793,8 @@ func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*Com return res, nil } -// FromStackItem converts provided stackitem.Array to ComplicatedNameEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to ComplicatedNameEvent or +// returns an error if it's not possible to do to so. func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") @@ -854,8 +854,8 @@ func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEven return res, nil } -// FromStackItem converts provided stackitem.Array to SomeMapEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to SomeMapEvent or +// returns an error if it's not possible to do to so. func (e *SomeMapEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") @@ -924,8 +924,8 @@ func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStru return res, nil } -// FromStackItem converts provided stackitem.Array to SomeStructEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to SomeStructEvent or +// returns an error if it's not possible to do to so. func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") @@ -989,8 +989,8 @@ func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArray return res, nil } -// FromStackItem converts provided stackitem.Array to SomeArrayEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to SomeArrayEvent or +// returns an error if it's not possible to do to so. func (e *SomeArrayEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") diff --git a/cli/smartcontract/testdata/notifications/rpcbindings_extended.out b/cli/smartcontract/testdata/notifications/rpcbindings_extended.out index 9b269e2eb..3b2ab5c07 100755 --- a/cli/smartcontract/testdata/notifications/rpcbindings_extended.out +++ b/cli/smartcontract/testdata/notifications/rpcbindings_extended.out @@ -222,7 +222,7 @@ func itemToCrazyStruct(item stackitem.Item, err error) (*CrazyStruct, error) { } // FromStackItem retrieves fields of CrazyStruct from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *CrazyStruct) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -262,7 +262,7 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { } // FromStackItem retrieves fields of LedgerBlock from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -384,7 +384,7 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) } // FromStackItem retrieves fields of LedgerBlockSR from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -522,7 +522,7 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction } // FromStackItem retrieves fields of LedgerTransaction from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -618,7 +618,7 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans } // FromStackItem retrieves fields of LedgerTransactionSigner from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -745,7 +745,7 @@ func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnes } // FromStackItem retrieves fields of LedgerWitnessCondition from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerWitnessCondition) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -785,7 +785,7 @@ func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule } // FromStackItem retrieves fields of LedgerWitnessRule from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerWitnessRule) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -839,8 +839,8 @@ func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*Com return res, nil } -// FromStackItem converts provided stackitem.Array to ComplicatedNameEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to ComplicatedNameEvent or +// returns an error if it's not possible to do to so. func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") @@ -900,8 +900,8 @@ func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEven return res, nil } -// FromStackItem converts provided stackitem.Array to SomeMapEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to SomeMapEvent or +// returns an error if it's not possible to do to so. func (e *SomeMapEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") @@ -1020,8 +1020,8 @@ func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStru return res, nil } -// FromStackItem converts provided stackitem.Array to SomeStructEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to SomeStructEvent or +// returns an error if it's not possible to do to so. func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") @@ -1072,8 +1072,8 @@ func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArray return res, nil } -// FromStackItem converts provided stackitem.Array to SomeArrayEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to SomeArrayEvent or +// returns an error if it's not possible to do to so. func (e *SomeArrayEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") diff --git a/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out b/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out index 0fba2708c..5b4d429c1 100755 --- a/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out +++ b/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out @@ -222,7 +222,7 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { } // FromStackItem retrieves fields of LedgerBlock from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -344,7 +344,7 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) } // FromStackItem retrieves fields of LedgerBlockSR from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -482,7 +482,7 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction } // FromStackItem retrieves fields of LedgerTransaction from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -578,7 +578,7 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans } // FromStackItem retrieves fields of LedgerTransactionSigner from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -705,7 +705,7 @@ func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnes } // FromStackItem retrieves fields of LedgerWitnessCondition from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerWitnessCondition) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -745,7 +745,7 @@ func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule } // FromStackItem retrieves fields of LedgerWitnessRule from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerWitnessRule) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -785,7 +785,7 @@ func itemToUnnamed(item stackitem.Item, err error) (*Unnamed, error) { } // FromStackItem retrieves fields of Unnamed from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *Unnamed) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -839,8 +839,8 @@ func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*Com return res, nil } -// FromStackItem converts provided stackitem.Array to ComplicatedNameEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to ComplicatedNameEvent or +// returns an error if it's not possible to do to so. func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") @@ -900,8 +900,8 @@ func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEven return res, nil } -// FromStackItem converts provided stackitem.Array to SomeMapEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to SomeMapEvent or +// returns an error if it's not possible to do to so. func (e *SomeMapEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") @@ -1033,8 +1033,8 @@ func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStru return res, nil } -// FromStackItem converts provided stackitem.Array to SomeStructEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to SomeStructEvent or +// returns an error if it's not possible to do to so. func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") @@ -1085,8 +1085,8 @@ func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArray return res, nil } -// FromStackItem converts provided stackitem.Array to SomeArrayEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to SomeArrayEvent or +// returns an error if it's not possible to do to so. func (e *SomeArrayEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") diff --git a/cli/smartcontract/testdata/structs/rpcbindings.out b/cli/smartcontract/testdata/structs/rpcbindings.out index 29af22323..5ac5bdcd1 100644 --- a/cli/smartcontract/testdata/structs/rpcbindings.out +++ b/cli/smartcontract/testdata/structs/rpcbindings.out @@ -200,7 +200,7 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { } // FromStackItem retrieves fields of LedgerBlock from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -322,7 +322,7 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) } // FromStackItem retrieves fields of LedgerBlockSR from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -460,7 +460,7 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction } // FromStackItem retrieves fields of LedgerTransaction from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -556,7 +556,7 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans } // FromStackItem retrieves fields of LedgerTransactionSigner from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -683,7 +683,7 @@ func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnes } // FromStackItem retrieves fields of LedgerWitnessCondition from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerWitnessCondition) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -723,7 +723,7 @@ func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule } // FromStackItem retrieves fields of LedgerWitnessRule from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *LedgerWitnessRule) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -763,7 +763,7 @@ func itemToManagementABI(item stackitem.Item, err error) (*ManagementABI, error) } // FromStackItem retrieves fields of ManagementABI from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *ManagementABI) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -829,7 +829,7 @@ func itemToManagementContract(item stackitem.Item, err error) (*ManagementContra } // FromStackItem retrieves fields of ManagementContract from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *ManagementContract) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -897,7 +897,7 @@ func itemToManagementEvent(item stackitem.Item, err error) (*ManagementEvent, er } // FromStackItem retrieves fields of ManagementEvent from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *ManagementEvent) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -959,7 +959,7 @@ func itemToManagementGroup(item stackitem.Item, err error) (*ManagementGroup, er } // FromStackItem retrieves fields of ManagementGroup from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *ManagementGroup) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -1009,7 +1009,7 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife } // FromStackItem retrieves fields of ManagementManifest from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *ManagementManifest) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -1201,7 +1201,7 @@ func itemToManagementMethod(item stackitem.Item, err error) (*ManagementMethod, } // FromStackItem retrieves fields of ManagementMethod from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *ManagementMethod) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -1281,7 +1281,7 @@ func itemToManagementParameter(item stackitem.Item, err error) (*ManagementParam } // FromStackItem retrieves fields of ManagementParameter from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *ManagementParameter) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -1330,7 +1330,7 @@ func itemToManagementPermission(item stackitem.Item, err error) (*ManagementPerm } // FromStackItem retrieves fields of ManagementPermission from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *ManagementPermission) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -1402,7 +1402,7 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er } // FromStackItem retrieves fields of StructsInternal from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *StructsInternal) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { diff --git a/cli/smartcontract/testdata/verifyrpc/verify.go b/cli/smartcontract/testdata/verifyrpc/verify.go index dd3539901..be1f5c671 100644 --- a/cli/smartcontract/testdata/verifyrpc/verify.go +++ b/cli/smartcontract/testdata/verifyrpc/verify.go @@ -102,8 +102,8 @@ func HelloWorldEventsFromApplicationLog(log *result.ApplicationLog) ([]*HelloWor return res, nil } -// FromStackItem converts provided stackitem.Array to HelloWorldEvent and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to HelloWorldEvent or +// returns an error if it's not possible to do to so. func (e *HelloWorldEvent) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index 746dcb34b..5f1495af3 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -236,7 +236,7 @@ func itemTo{{toTypeName $name}}(item stackitem.Item, err error) (*{{toTypeName $ } // FromStackItem retrieves fields of {{toTypeName $name}} from the given stack item -// and returns an error if so. +// or returns an error if it's not possible to do to so. func (res *{{toTypeName $name}}) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -287,8 +287,8 @@ func {{$e.Name}}sFromApplicationLog(log *result.ApplicationLog) ([]*{{$e.Name}}, return res, nil } -// FromStackItem converts provided stackitem.Array to {{$e.Name}} and -// returns an error if so. +// FromStackItem converts provided stackitem.Array to {{$e.Name}} or +// returns an error if it's not possible to do to so. func (e *{{$e.Name}}) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") From 9f9cec53bd681287a625d8359753d6404c3e8d05 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 31 May 2023 15:47:10 +0300 Subject: [PATCH 22/22] rpcbinding: provide links to types in comments Signed-off-by: Anna Shaleva --- cli/smartcontract/testdata/nameservice/nns.go | 8 +-- cli/smartcontract/testdata/nex/nex.go | 4 +- .../testdata/notifications/rpcbindings.out | 40 ++++++------- .../notifications/rpcbindings_extended.out | 44 +++++++------- .../notifications/rpcbindings_guessed.out | 44 +++++++------- .../testdata/structs/rpcbindings.out | 60 +++++++++---------- .../testdata/verifyrpc/verify.go | 4 +- pkg/smartcontract/rpcbinding/binding.go | 8 +-- 8 files changed, 106 insertions(+), 106 deletions(-) diff --git a/cli/smartcontract/testdata/nameservice/nns.go b/cli/smartcontract/testdata/nameservice/nns.go index 82cc8374d..3636fd333 100644 --- a/cli/smartcontract/testdata/nameservice/nns.go +++ b/cli/smartcontract/testdata/nameservice/nns.go @@ -339,7 +339,7 @@ func (c *Contract) DeleteRecordUnsigned(name string, typev *big.Int) (*transacti } // SetAdminEventsFromApplicationLog retrieves a set of all emitted events -// with "SetAdmin" name from the provided ApplicationLog. +// with "SetAdmin" name from the provided [result.ApplicationLog]. func SetAdminEventsFromApplicationLog(log *result.ApplicationLog) ([]*SetAdminEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -363,7 +363,7 @@ func SetAdminEventsFromApplicationLog(log *result.ApplicationLog) ([]*SetAdminEv return res, nil } -// FromStackItem converts provided stackitem.Array to SetAdminEvent or +// FromStackItem converts provided [stackitem.Array] to SetAdminEvent or // returns an error if it's not possible to do to so. func (e *SetAdminEvent) FromStackItem(item *stackitem.Array) error { if item == nil { @@ -432,7 +432,7 @@ func (e *SetAdminEvent) FromStackItem(item *stackitem.Array) error { } // RenewEventsFromApplicationLog retrieves a set of all emitted events -// with "Renew" name from the provided ApplicationLog. +// with "Renew" name from the provided [result.ApplicationLog]. func RenewEventsFromApplicationLog(log *result.ApplicationLog) ([]*RenewEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -456,7 +456,7 @@ func RenewEventsFromApplicationLog(log *result.ApplicationLog) ([]*RenewEvent, e return res, nil } -// FromStackItem converts provided stackitem.Array to RenewEvent or +// FromStackItem converts provided [stackitem.Array] to RenewEvent or // returns an error if it's not possible to do to so. func (e *RenewEvent) FromStackItem(item *stackitem.Array) error { if item == nil { diff --git a/cli/smartcontract/testdata/nex/nex.go b/cli/smartcontract/testdata/nex/nex.go index 0d716adde..32e7526c8 100644 --- a/cli/smartcontract/testdata/nex/nex.go +++ b/cli/smartcontract/testdata/nex/nex.go @@ -243,7 +243,7 @@ func (c *Contract) UpdateCapUnsigned(newCap *big.Int) (*transaction.Transaction, } // OnMintEventsFromApplicationLog retrieves a set of all emitted events -// with "OnMint" name from the provided ApplicationLog. +// with "OnMint" name from the provided [result.ApplicationLog]. func OnMintEventsFromApplicationLog(log *result.ApplicationLog) ([]*OnMintEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -267,7 +267,7 @@ func OnMintEventsFromApplicationLog(log *result.ApplicationLog) ([]*OnMintEvent, return res, nil } -// FromStackItem converts provided stackitem.Array to OnMintEvent or +// FromStackItem converts provided [stackitem.Array] to OnMintEvent or // returns an error if it's not possible to do to so. func (e *OnMintEvent) FromStackItem(item *stackitem.Array) error { if item == nil { diff --git a/cli/smartcontract/testdata/notifications/rpcbindings.out b/cli/smartcontract/testdata/notifications/rpcbindings.out index 1e873d611..a95cc1ea0 100644 --- a/cli/smartcontract/testdata/notifications/rpcbindings.out +++ b/cli/smartcontract/testdata/notifications/rpcbindings.out @@ -215,8 +215,8 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return res, err } -// FromStackItem retrieves fields of LedgerBlock from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerBlock from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -337,8 +337,8 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return res, err } -// FromStackItem retrieves fields of LedgerBlockSR from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerBlockSR from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -475,8 +475,8 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction return res, err } -// FromStackItem retrieves fields of LedgerTransaction from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerTransaction from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -571,8 +571,8 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans return res, err } -// FromStackItem retrieves fields of LedgerTransactionSigner from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerTransactionSigner from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -698,8 +698,8 @@ func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnes return res, err } -// FromStackItem retrieves fields of LedgerWitnessCondition from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerWitnessCondition from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerWitnessCondition) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -738,8 +738,8 @@ func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule return res, err } -// FromStackItem retrieves fields of LedgerWitnessRule from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerWitnessRule from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerWitnessRule) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -769,7 +769,7 @@ func (res *LedgerWitnessRule) FromStackItem(item stackitem.Item) error { } // ComplicatedNameEventsFromApplicationLog retrieves a set of all emitted events -// with "! complicated name %$#" name from the provided ApplicationLog. +// with "! complicated name %$#" name from the provided [result.ApplicationLog]. func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*ComplicatedNameEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -793,7 +793,7 @@ func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*Com return res, nil } -// FromStackItem converts provided stackitem.Array to ComplicatedNameEvent or +// FromStackItem converts provided [stackitem.Array] to ComplicatedNameEvent or // returns an error if it's not possible to do to so. func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error { if item == nil { @@ -830,7 +830,7 @@ func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error { } // SomeMapEventsFromApplicationLog retrieves a set of all emitted events -// with "SomeMap" name from the provided ApplicationLog. +// with "SomeMap" name from the provided [result.ApplicationLog]. func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -854,7 +854,7 @@ func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEven return res, nil } -// FromStackItem converts provided stackitem.Array to SomeMapEvent or +// FromStackItem converts provided [stackitem.Array] to SomeMapEvent or // returns an error if it's not possible to do to so. func (e *SomeMapEvent) FromStackItem(item *stackitem.Array) error { if item == nil { @@ -900,7 +900,7 @@ func (e *SomeMapEvent) FromStackItem(item *stackitem.Array) error { } // SomeStructEventsFromApplicationLog retrieves a set of all emitted events -// with "SomeStruct" name from the provided ApplicationLog. +// with "SomeStruct" name from the provided [result.ApplicationLog]. func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStructEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -924,7 +924,7 @@ func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStru return res, nil } -// FromStackItem converts provided stackitem.Array to SomeStructEvent or +// FromStackItem converts provided [stackitem.Array] to SomeStructEvent or // returns an error if it's not possible to do to so. func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error { if item == nil { @@ -965,7 +965,7 @@ func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error { } // SomeArrayEventsFromApplicationLog retrieves a set of all emitted events -// with "SomeArray" name from the provided ApplicationLog. +// with "SomeArray" name from the provided [result.ApplicationLog]. func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArrayEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -989,7 +989,7 @@ func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArray return res, nil } -// FromStackItem converts provided stackitem.Array to SomeArrayEvent or +// FromStackItem converts provided [stackitem.Array] to SomeArrayEvent or // returns an error if it's not possible to do to so. func (e *SomeArrayEvent) FromStackItem(item *stackitem.Array) error { if item == nil { diff --git a/cli/smartcontract/testdata/notifications/rpcbindings_extended.out b/cli/smartcontract/testdata/notifications/rpcbindings_extended.out index 3b2ab5c07..818c91993 100755 --- a/cli/smartcontract/testdata/notifications/rpcbindings_extended.out +++ b/cli/smartcontract/testdata/notifications/rpcbindings_extended.out @@ -221,8 +221,8 @@ func itemToCrazyStruct(item stackitem.Item, err error) (*CrazyStruct, error) { return res, err } -// FromStackItem retrieves fields of CrazyStruct from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of CrazyStruct from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *CrazyStruct) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -261,8 +261,8 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return res, err } -// FromStackItem retrieves fields of LedgerBlock from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerBlock from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -383,8 +383,8 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return res, err } -// FromStackItem retrieves fields of LedgerBlockSR from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerBlockSR from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -521,8 +521,8 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction return res, err } -// FromStackItem retrieves fields of LedgerTransaction from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerTransaction from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -617,8 +617,8 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans return res, err } -// FromStackItem retrieves fields of LedgerTransactionSigner from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerTransactionSigner from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -744,8 +744,8 @@ func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnes return res, err } -// FromStackItem retrieves fields of LedgerWitnessCondition from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerWitnessCondition from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerWitnessCondition) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -784,8 +784,8 @@ func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule return res, err } -// FromStackItem retrieves fields of LedgerWitnessRule from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerWitnessRule from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerWitnessRule) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -815,7 +815,7 @@ func (res *LedgerWitnessRule) FromStackItem(item stackitem.Item) error { } // ComplicatedNameEventsFromApplicationLog retrieves a set of all emitted events -// with "! complicated name %$#" name from the provided ApplicationLog. +// with "! complicated name %$#" name from the provided [result.ApplicationLog]. func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*ComplicatedNameEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -839,7 +839,7 @@ func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*Com return res, nil } -// FromStackItem converts provided stackitem.Array to ComplicatedNameEvent or +// FromStackItem converts provided [stackitem.Array] to ComplicatedNameEvent or // returns an error if it's not possible to do to so. func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error { if item == nil { @@ -876,7 +876,7 @@ func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error { } // SomeMapEventsFromApplicationLog retrieves a set of all emitted events -// with "SomeMap" name from the provided ApplicationLog. +// with "SomeMap" name from the provided [result.ApplicationLog]. func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -900,7 +900,7 @@ func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEven return res, nil } -// FromStackItem converts provided stackitem.Array to SomeMapEvent or +// FromStackItem converts provided [stackitem.Array] to SomeMapEvent or // returns an error if it's not possible to do to so. func (e *SomeMapEvent) FromStackItem(item *stackitem.Array) error { if item == nil { @@ -996,7 +996,7 @@ func (e *SomeMapEvent) FromStackItem(item *stackitem.Array) error { } // SomeStructEventsFromApplicationLog retrieves a set of all emitted events -// with "SomeStruct" name from the provided ApplicationLog. +// with "SomeStruct" name from the provided [result.ApplicationLog]. func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStructEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -1020,7 +1020,7 @@ func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStru return res, nil } -// FromStackItem converts provided stackitem.Array to SomeStructEvent or +// FromStackItem converts provided [stackitem.Array] to SomeStructEvent or // returns an error if it's not possible to do to so. func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error { if item == nil { @@ -1048,7 +1048,7 @@ func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error { } // SomeArrayEventsFromApplicationLog retrieves a set of all emitted events -// with "SomeArray" name from the provided ApplicationLog. +// with "SomeArray" name from the provided [result.ApplicationLog]. func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArrayEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -1072,7 +1072,7 @@ func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArray return res, nil } -// FromStackItem converts provided stackitem.Array to SomeArrayEvent or +// FromStackItem converts provided [stackitem.Array] to SomeArrayEvent or // returns an error if it's not possible to do to so. func (e *SomeArrayEvent) FromStackItem(item *stackitem.Array) error { if item == nil { diff --git a/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out b/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out index 5b4d429c1..883283d09 100755 --- a/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out +++ b/cli/smartcontract/testdata/notifications/rpcbindings_guessed.out @@ -221,8 +221,8 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return res, err } -// FromStackItem retrieves fields of LedgerBlock from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerBlock from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -343,8 +343,8 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return res, err } -// FromStackItem retrieves fields of LedgerBlockSR from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerBlockSR from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -481,8 +481,8 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction return res, err } -// FromStackItem retrieves fields of LedgerTransaction from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerTransaction from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -577,8 +577,8 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans return res, err } -// FromStackItem retrieves fields of LedgerTransactionSigner from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerTransactionSigner from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -704,8 +704,8 @@ func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnes return res, err } -// FromStackItem retrieves fields of LedgerWitnessCondition from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerWitnessCondition from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerWitnessCondition) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -744,8 +744,8 @@ func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule return res, err } -// FromStackItem retrieves fields of LedgerWitnessRule from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerWitnessRule from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerWitnessRule) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -784,8 +784,8 @@ func itemToUnnamed(item stackitem.Item, err error) (*Unnamed, error) { return res, err } -// FromStackItem retrieves fields of Unnamed from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of Unnamed from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *Unnamed) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -815,7 +815,7 @@ func (res *Unnamed) FromStackItem(item stackitem.Item) error { } // ComplicatedNameEventsFromApplicationLog retrieves a set of all emitted events -// with "! complicated name %$#" name from the provided ApplicationLog. +// with "! complicated name %$#" name from the provided [result.ApplicationLog]. func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*ComplicatedNameEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -839,7 +839,7 @@ func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*Com return res, nil } -// FromStackItem converts provided stackitem.Array to ComplicatedNameEvent or +// FromStackItem converts provided [stackitem.Array] to ComplicatedNameEvent or // returns an error if it's not possible to do to so. func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error { if item == nil { @@ -876,7 +876,7 @@ func (e *ComplicatedNameEvent) FromStackItem(item *stackitem.Array) error { } // SomeMapEventsFromApplicationLog retrieves a set of all emitted events -// with "SomeMap" name from the provided ApplicationLog. +// with "SomeMap" name from the provided [result.ApplicationLog]. func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -900,7 +900,7 @@ func SomeMapEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeMapEven return res, nil } -// FromStackItem converts provided stackitem.Array to SomeMapEvent or +// FromStackItem converts provided [stackitem.Array] to SomeMapEvent or // returns an error if it's not possible to do to so. func (e *SomeMapEvent) FromStackItem(item *stackitem.Array) error { if item == nil { @@ -1009,7 +1009,7 @@ func (e *SomeMapEvent) FromStackItem(item *stackitem.Array) error { } // SomeStructEventsFromApplicationLog retrieves a set of all emitted events -// with "SomeStruct" name from the provided ApplicationLog. +// with "SomeStruct" name from the provided [result.ApplicationLog]. func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStructEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -1033,7 +1033,7 @@ func SomeStructEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeStru return res, nil } -// FromStackItem converts provided stackitem.Array to SomeStructEvent or +// FromStackItem converts provided [stackitem.Array] to SomeStructEvent or // returns an error if it's not possible to do to so. func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error { if item == nil { @@ -1061,7 +1061,7 @@ func (e *SomeStructEvent) FromStackItem(item *stackitem.Array) error { } // SomeArrayEventsFromApplicationLog retrieves a set of all emitted events -// with "SomeArray" name from the provided ApplicationLog. +// with "SomeArray" name from the provided [result.ApplicationLog]. func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArrayEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -1085,7 +1085,7 @@ func SomeArrayEventsFromApplicationLog(log *result.ApplicationLog) ([]*SomeArray return res, nil } -// FromStackItem converts provided stackitem.Array to SomeArrayEvent or +// FromStackItem converts provided [stackitem.Array] to SomeArrayEvent or // returns an error if it's not possible to do to so. func (e *SomeArrayEvent) FromStackItem(item *stackitem.Array) error { if item == nil { diff --git a/cli/smartcontract/testdata/structs/rpcbindings.out b/cli/smartcontract/testdata/structs/rpcbindings.out index 5ac5bdcd1..ba503be59 100644 --- a/cli/smartcontract/testdata/structs/rpcbindings.out +++ b/cli/smartcontract/testdata/structs/rpcbindings.out @@ -199,8 +199,8 @@ func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { return res, err } -// FromStackItem retrieves fields of LedgerBlock from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerBlock from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -321,8 +321,8 @@ func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) return res, err } -// FromStackItem retrieves fields of LedgerBlockSR from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerBlockSR from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -459,8 +459,8 @@ func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction return res, err } -// FromStackItem retrieves fields of LedgerTransaction from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerTransaction from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -555,8 +555,8 @@ func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTrans return res, err } -// FromStackItem retrieves fields of LedgerTransactionSigner from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerTransactionSigner from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -682,8 +682,8 @@ func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnes return res, err } -// FromStackItem retrieves fields of LedgerWitnessCondition from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerWitnessCondition from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerWitnessCondition) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -722,8 +722,8 @@ func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule return res, err } -// FromStackItem retrieves fields of LedgerWitnessRule from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of LedgerWitnessRule from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *LedgerWitnessRule) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -762,8 +762,8 @@ func itemToManagementABI(item stackitem.Item, err error) (*ManagementABI, error) return res, err } -// FromStackItem retrieves fields of ManagementABI from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of ManagementABI from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *ManagementABI) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -828,8 +828,8 @@ func itemToManagementContract(item stackitem.Item, err error) (*ManagementContra return res, err } -// FromStackItem retrieves fields of ManagementContract from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of ManagementContract from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *ManagementContract) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -896,8 +896,8 @@ func itemToManagementEvent(item stackitem.Item, err error) (*ManagementEvent, er return res, err } -// FromStackItem retrieves fields of ManagementEvent from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of ManagementEvent from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *ManagementEvent) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -958,8 +958,8 @@ func itemToManagementGroup(item stackitem.Item, err error) (*ManagementGroup, er return res, err } -// FromStackItem retrieves fields of ManagementGroup from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of ManagementGroup from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *ManagementGroup) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -1008,8 +1008,8 @@ func itemToManagementManifest(item stackitem.Item, err error) (*ManagementManife return res, err } -// FromStackItem retrieves fields of ManagementManifest from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of ManagementManifest from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *ManagementManifest) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -1200,8 +1200,8 @@ func itemToManagementMethod(item stackitem.Item, err error) (*ManagementMethod, return res, err } -// FromStackItem retrieves fields of ManagementMethod from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of ManagementMethod from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *ManagementMethod) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -1280,8 +1280,8 @@ func itemToManagementParameter(item stackitem.Item, err error) (*ManagementParam return res, err } -// FromStackItem retrieves fields of ManagementParameter from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of ManagementParameter from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *ManagementParameter) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -1329,8 +1329,8 @@ func itemToManagementPermission(item stackitem.Item, err error) (*ManagementPerm return res, err } -// FromStackItem retrieves fields of ManagementPermission from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of ManagementPermission from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *ManagementPermission) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -1401,8 +1401,8 @@ func itemToStructsInternal(item stackitem.Item, err error) (*StructsInternal, er return res, err } -// FromStackItem retrieves fields of StructsInternal from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of StructsInternal from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *StructsInternal) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { diff --git a/cli/smartcontract/testdata/verifyrpc/verify.go b/cli/smartcontract/testdata/verifyrpc/verify.go index be1f5c671..3a01b990c 100644 --- a/cli/smartcontract/testdata/verifyrpc/verify.go +++ b/cli/smartcontract/testdata/verifyrpc/verify.go @@ -78,7 +78,7 @@ func (c *Contract) VerifyUnsigned() (*transaction.Transaction, error) { } // HelloWorldEventsFromApplicationLog retrieves a set of all emitted events -// with "Hello world!" name from the provided ApplicationLog. +// with "Hello world!" name from the provided [result.ApplicationLog]. func HelloWorldEventsFromApplicationLog(log *result.ApplicationLog) ([]*HelloWorldEvent, error) { if log == nil { return nil, errors.New("nil application log") @@ -102,7 +102,7 @@ func HelloWorldEventsFromApplicationLog(log *result.ApplicationLog) ([]*HelloWor return res, nil } -// FromStackItem converts provided stackitem.Array to HelloWorldEvent or +// FromStackItem converts provided [stackitem.Array] to HelloWorldEvent or // returns an error if it's not possible to do to so. func (e *HelloWorldEvent) FromStackItem(item *stackitem.Array) error { if item == nil { diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index 5f1495af3..caf6f4397 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -235,8 +235,8 @@ func itemTo{{toTypeName $name}}(item stackitem.Item, err error) (*{{toTypeName $ return res, err } -// FromStackItem retrieves fields of {{toTypeName $name}} from the given stack item -// or returns an error if it's not possible to do to so. +// FromStackItem retrieves fields of {{toTypeName $name}} from the given +// [stackitem.Item] or returns an error if it's not possible to do to so. func (res *{{toTypeName $name}}) FromStackItem(item stackitem.Item) error { arr, ok := item.Value().([]stackitem.Item) if !ok { @@ -263,7 +263,7 @@ func (res *{{toTypeName $name}}) FromStackItem(item stackitem.Item) error { {{ end -}} {{- range $e := .CustomEvents }} // {{$e.Name}}sFromApplicationLog retrieves a set of all emitted events -// with "{{$e.ManifestName}}" name from the provided ApplicationLog. +// with "{{$e.ManifestName}}" name from the provided [result.ApplicationLog]. func {{$e.Name}}sFromApplicationLog(log *result.ApplicationLog) ([]*{{$e.Name}}, error) { if log == nil { return nil, errors.New("nil application log") @@ -287,7 +287,7 @@ func {{$e.Name}}sFromApplicationLog(log *result.ApplicationLog) ([]*{{$e.Name}}, return res, nil } -// FromStackItem converts provided stackitem.Array to {{$e.Name}} or +// FromStackItem converts provided [stackitem.Array] to {{$e.Name}} or // returns an error if it's not possible to do to so. func (e *{{$e.Name}}) FromStackItem(item *stackitem.Array) error { if item == nil {