github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/integration/rpcevents/execution_events_server_test.go (about) 1 // +build integration 2 3 // Space above here matters 4 // Copyright Monax Industries Limited 5 // SPDX-License-Identifier: Apache-2.0 6 7 package rpcevents 8 9 import ( 10 "context" 11 "io" 12 "strconv" 13 "sync" 14 "testing" 15 "time" 16 17 "github.com/hyperledger/burrow/core" 18 "github.com/hyperledger/burrow/crypto" 19 "github.com/hyperledger/burrow/event" 20 "github.com/hyperledger/burrow/event/query" 21 "github.com/hyperledger/burrow/execution/errors" 22 "github.com/hyperledger/burrow/execution/evm/abi" 23 "github.com/hyperledger/burrow/execution/exec" 24 "github.com/hyperledger/burrow/execution/solidity" 25 "github.com/hyperledger/burrow/integration" 26 "github.com/hyperledger/burrow/integration/rpctest" 27 "github.com/hyperledger/burrow/rpc/rpcevents" 28 "github.com/hyperledger/burrow/rpc/rpctransact" 29 "github.com/hyperledger/burrow/txs/payload" 30 "github.com/stretchr/testify/assert" 31 "github.com/stretchr/testify/require" 32 ) 33 34 func TestExecutionEventsTest(t *testing.T) { 35 kern, shutdown := integration.RunNode(t, rpctest.GenesisDoc, rpctest.PrivateAccounts) 36 defer shutdown() 37 tcli := rpctest.NewTransactClient(t, kern.GRPCListenAddress().String()) 38 ecli := rpctest.NewExecutionEventsClient(t, kern.GRPCListenAddress().String()) 39 inputAddress0 := rpctest.PrivateAccounts[0].GetAddress() 40 inputAddress1 := rpctest.PrivateAccounts[1].GetAddress() 41 42 t.Run("Group", func(t *testing.T) { 43 t.Run("StreamDB", func(t *testing.T) { 44 numSends := 4 45 request := &rpcevents.BlocksRequest{ 46 BlockRange: doSends(t, numSends, tcli, kern, inputAddress1, 2004), 47 } 48 var blocks []*exec.BlockExecution 49 50 stream, err := ecli.Stream(context.Background(), request) 51 require.NoError(t, err) 52 53 err = rpcevents.ConsumeBlockExecutions(stream, func(be *exec.BlockExecution) error { 54 blocks = append(blocks, be) 55 return nil 56 }) 57 require.Equal(t, io.EOF, err) 58 59 require.True(t, len(blocks) > 0, "should see at least one block") 60 var height uint64 61 for _, b := range blocks { 62 if height > 0 { 63 assert.Equal(t, height+1, b.Height) 64 } 65 for range b.TxExecutions { 66 numSends-- 67 } 68 height = b.Height 69 } 70 require.Equal(t, 0, numSends, "all transactions should be observed") 71 require.NoError(t, stream.CloseSend()) 72 }) 73 74 t.Run("Stream_streaming", func(t *testing.T) { 75 request := &rpcevents.BlocksRequest{ 76 BlockRange: rpcevents.NewBlockRange(rpcevents.AbsoluteBound(0), rpcevents.StreamBound()), 77 } 78 stream, err := ecli.Stream(context.Background(), request) 79 require.NoError(t, err) 80 batches := 3 81 sendsPerBatch := 4 82 total := batches * sendsPerBatch 83 doneCh := make(chan []struct{}) 84 go func() { 85 for i := 0; i < batches; i++ { 86 doSends(t, sendsPerBatch, tcli, kern, inputAddress0, 2004) 87 } 88 close(doneCh) 89 }() 90 91 err = rpcevents.ConsumeBlockExecutions(stream, func(be *exec.BlockExecution) error { 92 for range be.TxExecutions { 93 total-- 94 } 95 if total == 0 { 96 return io.EOF 97 } 98 return nil 99 }) 100 require.Equal(t, io.EOF, err) 101 assert.Equal(t, 0, total) 102 require.NoError(t, stream.CloseSend()) 103 <-doneCh 104 }) 105 106 t.Run("StreamContains2", func(t *testing.T) { 107 request := &rpcevents.BlocksRequest{ 108 BlockRange: rpcevents.AbsoluteRange(0, 12), 109 Query: "Height CONTAINS '2'", 110 } 111 stream, err := ecli.Stream(context.Background(), request) 112 require.NoError(t, err) 113 numSends := 4 114 var blocks []*exec.BlockExecution 115 require.NoError(t, err) 116 doSends(t, numSends, tcli, kern, inputAddress1, 1992) 117 require.NoError(t, err) 118 err = rpcevents.ConsumeBlockExecutions(stream, func(be *exec.BlockExecution) error { 119 blocks = append(blocks, be) 120 assert.Contains(t, strconv.FormatUint(be.Height, 10), "2") 121 return nil 122 }, exec.NonConsecutiveBlocks|exec.NonConsecutiveTxs) 123 require.Equal(t, io.EOF, err) 124 require.Len(t, blocks, 2, "should record blocks 2 and 12") 125 assert.Equal(t, uint64(2), blocks[0].Height) 126 assert.Equal(t, uint64(12), blocks[1].Height) 127 128 require.NoError(t, stream.CloseSend()) 129 }) 130 131 t.Run("GetEventsSend", func(t *testing.T) { 132 numSends := 1100 133 request := &rpcevents.BlocksRequest{BlockRange: doSends(t, numSends, tcli, kern, inputAddress0, 2004)} 134 responses, err := getEvents(t, request, ecli) 135 require.NoError(t, err) 136 assert.Equal(t, numSends*2, countEventsAndCheckConsecutive(t, responses), 137 "should receive 1 input, 1 output per send") 138 }) 139 140 t.Run("GetEventsSendContainsAA", func(t *testing.T) { 141 numSends := 1100 142 request := &rpcevents.BlocksRequest{ 143 BlockRange: doSends(t, numSends, tcli, kern, inputAddress1, 2004), 144 Query: "TxHash CONTAINS 'AA'", 145 } 146 responses, err := getEvents(t, request, ecli) 147 require.NoError(t, err) 148 for _, response := range responses { 149 for _, ev := range response.Events { 150 require.Contains(t, ev.Header.TxHash.String(), "AA") 151 } 152 } 153 }) 154 155 t.Run("GetEventsSendFiltered", func(t *testing.T) { 156 numSends := 500 157 request := &rpcevents.BlocksRequest{ 158 BlockRange: doSends(t, numSends, tcli, kern, inputAddress0, 999), 159 Query: query.NewBuilder().AndEquals("Input.Address", inputAddress0.String()). 160 AndEquals(event.EventTypeKey, exec.TypeAccountInput.String()).String(), 161 } 162 responses, err := getEvents(t, request, ecli) 163 require.NoError(t, err) 164 assert.Equal(t, numSends, countEventsAndCheckConsecutive(t, responses), "should receive every single input event per send") 165 }) 166 167 t.Run("Revert", func(t *testing.T) { 168 txe, err := rpctest.CreateEVMContract(tcli, inputAddress0, solidity.Bytecode_Revert, nil) 169 require.NoError(t, err) 170 spec, err := abi.ReadSpec(solidity.Abi_Revert) 171 require.NoError(t, err) 172 data, _, err := spec.Pack("RevertAt", 4) 173 require.NoError(t, err) 174 contractAddress := txe.Receipt.ContractAddress 175 txe, err = rpctest.CallContract(tcli, inputAddress0, contractAddress, data) 176 require.NoError(t, err) 177 assert.Equal(t, errors.Codes.ExecutionReverted, errors.GetCode(txe.Exception)) 178 assert.Contains(t, txe.Exception.Error(), "I have reverted") 179 180 request := &rpcevents.BlocksRequest{ 181 BlockRange: rpcevents.NewBlockRange(rpcevents.AbsoluteBound(0), rpcevents.LatestBound()), 182 Query: query.Must(query.NewBuilder().AndEquals(event.EventIDKey, exec.EventStringLogEvent(contractAddress)). 183 AndEquals(event.TxHashKey, txe.TxHash).Query()).String(), 184 } 185 evs, err := getEvents(t, request, ecli) 186 require.NoError(t, err) 187 n := countEventsAndCheckConsecutive(t, evs) 188 assert.Equal(t, 0, n, "should not see reverted events") 189 }) 190 191 // This test triggered a bug when using 'latest' as the end bound and where the latest block is an empty block 192 // leading to streaming until another block is emitted causing clients to hang around much longer than they 193 // should 194 t.Run("GetEventsWithLatestBlockEmpty", func(t *testing.T) { 195 numSends := 5 196 blockRange := doSends(t, numSends, tcli, kern, inputAddress0, 999) 197 request := &rpcevents.BlocksRequest{ 198 BlockRange: &rpcevents.BlockRange{ 199 Start: blockRange.Start, 200 End: rpcevents.LatestBound(), 201 }, 202 } 203 204 n := 400 205 wait := time.Millisecond 206 before := time.Now() 207 for i := 0; i < n; i++ { 208 time.Sleep(wait) 209 responses, err := getEvents(t, request, ecli) 210 require.NoError(t, err) 211 assert.Equal(t, 2*numSends, countEventsAndCheckConsecutive(t, responses), "should receive every single input event per send") 212 } 213 elapsed := time.Now().Sub(before) 214 // This should complete very quickly unless it accidentally starts streaming in which case it will keep 215 // waiting for an empty block at each iteration after the bug is triggered 216 require.Less(t, elapsed, time.Duration(n)*wait*10) 217 }) 218 }) 219 } 220 221 func getEvents(t *testing.T, request *rpcevents.BlocksRequest, ecli rpcevents.ExecutionEventsClient) ([]*rpcevents.EventsResponse, error) { 222 evs, err := ecli.Events(context.Background(), request) 223 require.NoError(t, err) 224 var responses []*rpcevents.EventsResponse 225 for { 226 resp, err := evs.Recv() 227 if err != nil { 228 if err == io.EOF { 229 break 230 } 231 return nil, err 232 } 233 responses = append(responses, resp) 234 } 235 return responses, nil 236 } 237 238 func doSends(t *testing.T, numSends int, cli rpctransact.TransactClient, kern *core.Kernel, inputAddress crypto.Address, 239 amt uint64) *rpcevents.BlockRange { 240 expecter := rpctest.ExpectTxs(kern.Emitter, "doSends") 241 wg := new(sync.WaitGroup) 242 for i := 0; i < numSends; i++ { 243 wg.Add(1) 244 // Slow us down a bit to ensure spread across blocks 245 time.Sleep(time.Millisecond) 246 receipt, err := cli.SendTxAsync(context.Background(), 247 &payload.SendTx{ 248 Inputs: []*payload.TxInput{{ 249 Address: inputAddress, 250 Amount: amt, 251 }}, 252 Outputs: []*payload.TxOutput{{ 253 Address: rpctest.PrivateAccounts[4].GetAddress(), 254 Amount: amt, 255 }}, 256 }) 257 require.NoError(t, err) 258 expecter.Expect(receipt.TxHash) 259 assert.False(t, receipt.CreatesContract) 260 wg.Done() 261 } 262 wg.Wait() 263 return expecter.AssertCommitted(t) 264 } 265 266 func countEventsAndCheckConsecutive(t *testing.T, responses []*rpcevents.EventsResponse) int { 267 i := 0 268 var height uint64 269 var index uint64 270 txHash := "" 271 for _, resp := range responses { 272 require.True(t, resp.Height > height, "must not receive multiple GetEventsResponses for the same block") 273 height := resp.Height 274 for _, ev := range resp.Events { 275 require.Equal(t, ev.Header.Height, height, "height of event headers much match height of GetEventsResponse") 276 if txHash != ev.Header.TxHash.String() { 277 txHash = ev.Header.TxHash.String() 278 index = 0 279 } 280 if ev.Header.Index > index { 281 require.Equal(t, index+1, ev.Header.Index) 282 index++ 283 } 284 i++ 285 } 286 } 287 return i 288 }