github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/state/notification_event_test.go (about)

     1  package state
     2  
     3  import (
     4  	"encoding/json"
     5  	"testing"
     6  
     7  	"github.com/nspcc-dev/neo-go/internal/random"
     8  	"github.com/nspcc-dev/neo-go/internal/testserdes"
     9  	"github.com/nspcc-dev/neo-go/pkg/io"
    10  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
    11  	"github.com/nspcc-dev/neo-go/pkg/util"
    12  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    13  	"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func BenchmarkAppExecResult_EncodeBinary(b *testing.B) {
    18  	aer := &AppExecResult{
    19  		Container: random.Uint256(),
    20  		Execution: Execution{
    21  			Trigger:     trigger.Application,
    22  			VMState:     vmstate.Halt,
    23  			GasConsumed: 12345,
    24  			Stack:       []stackitem.Item{},
    25  			Events: []NotificationEvent{{
    26  				ScriptHash: random.Uint160(),
    27  				Name:       "Event",
    28  				Item:       stackitem.NewArray([]stackitem.Item{stackitem.NewBool(true)}),
    29  			}},
    30  		},
    31  	}
    32  
    33  	w := io.NewBufBinWriter()
    34  	b.ReportAllocs()
    35  	b.ResetTimer()
    36  	for i := 0; i < b.N; i++ {
    37  		w.Reset()
    38  		aer.EncodeBinary(w.BinWriter)
    39  	}
    40  }
    41  
    42  func TestEncodeDecodeNotificationEvent(t *testing.T) {
    43  	event := &NotificationEvent{
    44  		ScriptHash: random.Uint160(),
    45  		Name:       "Event",
    46  		Item:       stackitem.NewArray([]stackitem.Item{stackitem.NewBool(true)}),
    47  	}
    48  
    49  	testserdes.EncodeDecodeBinary(t, event, new(NotificationEvent))
    50  }
    51  
    52  func TestEncodeDecodeAppExecResult(t *testing.T) {
    53  	newAer := func() *AppExecResult {
    54  		return &AppExecResult{
    55  			Container: random.Uint256(),
    56  			Execution: Execution{
    57  				Trigger:     1,
    58  				VMState:     vmstate.Halt,
    59  				GasConsumed: 10,
    60  				Stack:       []stackitem.Item{stackitem.NewBool(true)},
    61  				Events:      []NotificationEvent{},
    62  			},
    63  		}
    64  	}
    65  	t.Run("halt", func(t *testing.T) {
    66  		appExecResult := newAer()
    67  		appExecResult.VMState = vmstate.Halt
    68  		testserdes.EncodeDecodeBinary(t, appExecResult, new(AppExecResult))
    69  	})
    70  	t.Run("fault", func(t *testing.T) {
    71  		appExecResult := newAer()
    72  		appExecResult.VMState = vmstate.Fault
    73  		testserdes.EncodeDecodeBinary(t, appExecResult, new(AppExecResult))
    74  	})
    75  	t.Run("with interop", func(t *testing.T) {
    76  		appExecResult := newAer()
    77  		appExecResult.Stack = []stackitem.Item{stackitem.NewInterop(nil)}
    78  		testserdes.EncodeDecodeBinary(t, appExecResult, new(AppExecResult))
    79  	})
    80  	t.Run("recursive reference", func(t *testing.T) {
    81  		var arr = stackitem.NewArray(nil)
    82  		arr.Append(arr)
    83  		appExecResult := newAer()
    84  		appExecResult.Stack = []stackitem.Item{arr, stackitem.NewBool(true), stackitem.NewInterop(123)}
    85  
    86  		bs, err := testserdes.EncodeBinary(appExecResult)
    87  		require.NoError(t, err)
    88  		actual := new(AppExecResult)
    89  		require.NoError(t, testserdes.DecodeBinary(bs, actual))
    90  		require.Equal(t, 3, len(actual.Stack))
    91  		require.Nil(t, actual.Stack[0])
    92  		require.Equal(t, true, actual.Stack[1].Value())
    93  		require.Equal(t, stackitem.InteropT, actual.Stack[2].Type())
    94  
    95  		bs1, err := testserdes.EncodeBinary(actual)
    96  		require.NoError(t, err)
    97  		require.Equal(t, bs, bs1)
    98  	})
    99  	t.Run("invalid item type", func(t *testing.T) {
   100  		aer := newAer()
   101  		w := io.NewBufBinWriter()
   102  		w.WriteBytes(aer.Container[:])
   103  		w.WriteB(byte(aer.Trigger))
   104  		w.WriteB(byte(aer.VMState))
   105  		w.WriteU64LE(uint64(aer.GasConsumed))
   106  		stackitem.EncodeBinary(stackitem.NewBool(true), w.BinWriter)
   107  		require.NoError(t, w.Err)
   108  		require.Error(t, testserdes.DecodeBinary(w.Bytes(), new(AppExecResult)))
   109  	})
   110  }
   111  
   112  func TestMarshalUnmarshalJSONNotificationEvent(t *testing.T) {
   113  	t.Run("positive", func(t *testing.T) {
   114  		ne := &NotificationEvent{
   115  			ScriptHash: random.Uint160(),
   116  			Name:       "my_ne",
   117  			Item: stackitem.NewArray([]stackitem.Item{
   118  				stackitem.NewBool(true),
   119  			}),
   120  		}
   121  		testserdes.MarshalUnmarshalJSON(t, ne, new(NotificationEvent))
   122  	})
   123  
   124  	t.Run("MarshalJSON recursive reference", func(t *testing.T) {
   125  		i := make([]stackitem.Item, 1)
   126  		recursive := stackitem.NewArray(i)
   127  		i[0] = recursive
   128  		ne := &NotificationEvent{
   129  			Item: recursive,
   130  		}
   131  		_, err := json.Marshal(ne)
   132  		require.NoError(t, err)
   133  	})
   134  
   135  	t.Run("UnmarshalJSON error", func(t *testing.T) {
   136  		errorCases := []string{
   137  			`{"contract":"0xBadHash","eventname":"my_ne","state":{"type":"Array","value":[{"type":"Boolean","value":true}]}}`,
   138  			`{"contract":"0xab2f820e2aa7cca1e081283c58a7d7943c33a2f1","eventname":"my_ne","state":{"type":"Array","value":[{"type":"BadType","value":true}]}}`,
   139  			`{"contract":"0xab2f820e2aa7cca1e081283c58a7d7943c33a2f1","eventname":"my_ne","state":{"type":"Boolean", "value":true}}`,
   140  		}
   141  		for _, errCase := range errorCases {
   142  			err := json.Unmarshal([]byte(errCase), new(NotificationEvent))
   143  			require.Error(t, err)
   144  		}
   145  	})
   146  }
   147  
   148  func TestMarshalUnmarshalJSONAppExecResult(t *testing.T) {
   149  	t.Run("positive, transaction", func(t *testing.T) {
   150  		appExecResult := &AppExecResult{
   151  			Container: random.Uint256(),
   152  			Execution: Execution{
   153  				Trigger:     trigger.Application,
   154  				VMState:     vmstate.Halt,
   155  				GasConsumed: 10,
   156  				Stack:       []stackitem.Item{},
   157  				Events:      []NotificationEvent{},
   158  			},
   159  		}
   160  		testserdes.MarshalUnmarshalJSON(t, appExecResult, new(AppExecResult))
   161  	})
   162  
   163  	t.Run("positive, fault state", func(t *testing.T) {
   164  		appExecResult := &AppExecResult{
   165  			Container: random.Uint256(),
   166  			Execution: Execution{
   167  				Trigger:        trigger.Application,
   168  				VMState:        vmstate.Fault,
   169  				GasConsumed:    10,
   170  				Stack:          []stackitem.Item{stackitem.NewBool(true)},
   171  				Events:         []NotificationEvent{},
   172  				FaultException: "unhandled exception",
   173  			},
   174  		}
   175  		testserdes.MarshalUnmarshalJSON(t, appExecResult, new(AppExecResult))
   176  	})
   177  	t.Run("positive, block", func(t *testing.T) {
   178  		appExecResult := &AppExecResult{
   179  			Container: random.Uint256(),
   180  			Execution: Execution{
   181  				Trigger:     trigger.OnPersist,
   182  				VMState:     vmstate.Halt,
   183  				GasConsumed: 10,
   184  				Stack:       []stackitem.Item{},
   185  				Events:      []NotificationEvent{},
   186  			},
   187  		}
   188  		testserdes.MarshalUnmarshalJSON(t, appExecResult, new(AppExecResult))
   189  	})
   190  
   191  	t.Run("MarshalJSON recursive reference", func(t *testing.T) {
   192  		arr := stackitem.NewArray(nil)
   193  		arr.Append(arr)
   194  		errAer := &AppExecResult{
   195  			Execution: Execution{
   196  				Trigger: trigger.Application,
   197  				Stack:   []stackitem.Item{arr, stackitem.NewBool(true), stackitem.NewInterop(123)},
   198  			},
   199  		}
   200  
   201  		bs, err := json.Marshal(errAer)
   202  		require.NoError(t, err)
   203  
   204  		actual := new(AppExecResult)
   205  		require.NoError(t, json.Unmarshal(bs, actual))
   206  		require.Equal(t, 3, len(actual.Stack))
   207  		require.Nil(t, actual.Stack[0])
   208  		require.Equal(t, true, actual.Stack[1].Value())
   209  		require.Equal(t, stackitem.InteropT, actual.Stack[2].Type())
   210  
   211  		bs1, err := json.Marshal(actual)
   212  		require.NoError(t, err)
   213  		require.NotEqual(t, bs, bs1) // recursive ref error vs. unserializable nil
   214  
   215  		actual2 := new(AppExecResult)
   216  		require.NoError(t, json.Unmarshal(bs, actual2))
   217  		bs2, err := json.Marshal(actual2)
   218  		require.NoError(t, err)
   219  		require.Equal(t, bs1, bs2) // unserializable nil in both cases
   220  	})
   221  
   222  	t.Run("UnmarshalJSON error", func(t *testing.T) {
   223  		nilStackCases := []string{
   224  			`{"container":"0x17145a039fca704fcdbeb46e6b210af98a1a9e5b9768e46ffc38f71c79ac2521","trigger":"Application","vmstate":"HALT","gasconsumed":"1","stack":[{"type":"WrongType","value":"1"}],"notifications":[]}`,
   225  		}
   226  		for _, str := range nilStackCases {
   227  			actual := new(AppExecResult)
   228  			err := json.Unmarshal([]byte(str), actual)
   229  			require.NoError(t, err)
   230  			require.Nil(t, actual.Stack)
   231  		}
   232  
   233  		errorCases := []string{
   234  			`{"container":"0xBadHash","trigger":"Application","vmstate":"HALT","gasconsumed":"1","stack":[{"type":"Integer","value":"1"}],"notifications":[]}`,
   235  			`{"container":"0x17145a039fca704fcdbeb46e6b210af98a1a9e5b9768e46ffc38f71c79ac2521","trigger":"Application","vmstate":"BadState","gasconsumed":"1","stack":[{"type":"Integer","value":"1"}],"notifications":[]}`,
   236  			`{"container":"0x17145a039fca704fcdbeb46e6b210af98a1a9e5b9768e46ffc38f71c79ac2521","trigger":"BadTrigger","vmstate":"HALT","gasconsumed":"1","stack":[{"type":"Integer","value":"1"}],"notifications":[]}`,
   237  		}
   238  		for _, str := range errorCases {
   239  			actual := new(AppExecResult)
   240  			err := json.Unmarshal([]byte(str), actual)
   241  			require.Error(t, err)
   242  		}
   243  	})
   244  }
   245  
   246  func TestContainedNotificationEvent_MarshalUnmarshalJSON(t *testing.T) {
   247  	testserdes.MarshalUnmarshalJSON(t, &ContainedNotificationEvent{
   248  		Container: util.Uint256{1, 2, 3},
   249  		NotificationEvent: NotificationEvent{
   250  			ScriptHash: util.Uint160{4, 5, 6},
   251  			Name:       "alarm",
   252  			Item:       stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte("qwerty"))}),
   253  		},
   254  	}, new(ContainedNotificationEvent))
   255  }