Skip to content

Commit e95b413

Browse files
authored
Merge pull request #622 from onflow/mpeter/sort-flow-events-by-event-index
Sort Flow EVM events received from Event Streaming API by their `TransactionIndex` & `EventIndex` fields in ascending order
2 parents 5774ba3 + 3d4533d commit e95b413

File tree

2 files changed

+108
-9
lines changed

2 files changed

+108
-9
lines changed

models/events.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package models
22

33
import (
44
"fmt"
5-
"strings"
5+
"sort"
66

77
"github.com/onflow/cadence"
88
"github.com/onflow/flow-go-sdk"
@@ -12,20 +12,25 @@ import (
1212
errs "github.com/onflow/flow-evm-gateway/models/errors"
1313
)
1414

15+
const (
16+
BlockExecutedQualifiedIdentifier = string(events.EventTypeBlockExecuted)
17+
TransactionExecutedQualifiedIdentifier = string(events.EventTypeTransactionExecuted)
18+
)
19+
1520
// isBlockExecutedEvent checks whether the given event contains block executed data.
1621
func isBlockExecutedEvent(event cadence.Event) bool {
1722
if event.EventType == nil {
1823
return false
1924
}
20-
return strings.Contains(event.EventType.ID(), string(events.EventTypeBlockExecuted))
25+
return event.EventType.QualifiedIdentifier == BlockExecutedQualifiedIdentifier
2126
}
2227

2328
// isTransactionExecutedEvent checks whether the given event contains transaction executed data.
2429
func isTransactionExecutedEvent(event cadence.Event) bool {
2530
if event.EventType == nil {
2631
return false
2732
}
28-
return strings.Contains(event.EventType.ID(), string(events.EventTypeTransactionExecuted))
33+
return event.EventType.QualifiedIdentifier == TransactionExecutedQualifiedIdentifier
2934
}
3035

3136
// CadenceEvents contains Flow emitted events containing one or zero evm block executed event,
@@ -39,6 +44,15 @@ type CadenceEvents struct {
3944

4045
// NewCadenceEvents decodes the events into evm types.
4146
func NewCadenceEvents(events flow.BlockEvents) (*CadenceEvents, error) {
47+
// first we sort all the events in the block, by their TransactionIndex,
48+
// and then we also sort events in the same transaction, by their EventIndex.
49+
sort.Slice(events.Events, func(i, j int) bool {
50+
if events.Events[i].TransactionIndex != events.Events[j].TransactionIndex {
51+
return events.Events[i].TransactionIndex < events.Events[j].TransactionIndex
52+
}
53+
return events.Events[i].EventIndex < events.Events[j].EventIndex
54+
})
55+
4256
e, err := decodeCadenceEvents(events)
4357
if err != nil {
4458
return nil, err

models/events_test.go

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func TestCadenceEvents_Block(t *testing.T) {
6969

7070
// generate txs
7171
for i := 0; i < txCount; i++ {
72-
tx, _, txEvent, err := newTransaction(uint64(i))
72+
tx, _, txEvent, err := newTransaction(uint64(i), uint16(i))
7373
require.NoError(t, err)
7474
hashes[i] = tx.Hash()
7575
events = append(events, txEvent)
@@ -131,7 +131,7 @@ func TestCadenceEvents_Block(t *testing.T) {
131131
})
132132

133133
t.Run("block with more transaction hashes", func(t *testing.T) {
134-
tx, _, _, err := newTransaction(1)
134+
tx, _, _, err := newTransaction(1, 0)
135135
require.NoError(t, err)
136136

137137
// generate single block
@@ -153,6 +153,78 @@ func TestCadenceEvents_Block(t *testing.T) {
153153
"block 1 references missing transaction/s",
154154
)
155155
})
156+
157+
t.Run("EVM events are ordered by Flow TransactionIndex & EventIndex", func(t *testing.T) {
158+
txCount := 3
159+
blockEvents := flow.BlockEvents{
160+
BlockID: flow.Identifier{0x1},
161+
Height: 1,
162+
}
163+
164+
// tx1 and tx2 are EVM transactions executed on a single Flow transaction.
165+
tx1, _, txEvent1, err := newTransaction(0, 0)
166+
require.NoError(t, err)
167+
txEvent1.TransactionIndex = 0
168+
txEvent1.EventIndex = 2
169+
170+
tx2, _, txEvent2, err := newTransaction(1, 1)
171+
require.NoError(t, err)
172+
txEvent2.TransactionIndex = 0
173+
txEvent2.EventIndex = 5
174+
175+
// tx3 is a Flow transaction with a single EVM transaction on EventIndex=1
176+
tx3, _, txEvent3, err := newTransaction(2, 0)
177+
require.NoError(t, err)
178+
txEvent3.TransactionIndex = 2
179+
txEvent3.EventIndex = 1
180+
181+
// needed for computing the `TransactionHashRoot` field on
182+
// EVM.BlockExecuted event payload. the order is sensitive.
183+
hashes = []gethCommon.Hash{
184+
tx1.Hash(),
185+
tx2.Hash(),
186+
tx3.Hash(),
187+
}
188+
189+
// add the tx events in a shuffled order
190+
blockEvents.Events = []flow.Event{
191+
txEvent3,
192+
txEvent1,
193+
txEvent2,
194+
}
195+
196+
// generate single block
197+
_, blockEvent, err := newBlock(1, hashes)
198+
require.NoError(t, err)
199+
blockEvent.TransactionIndex = 4
200+
blockEvent.EventIndex = 0
201+
blockEvents.Events = append(blockEvents.Events, blockEvent)
202+
203+
// parse the EventStreaming API response
204+
cdcEvents, err := NewCadenceEvents(blockEvents)
205+
require.NoError(t, err)
206+
207+
// assert that Flow events are sorted by their TransactionIndex and EventIndex fields
208+
assert.Equal(
209+
t,
210+
[]flow.Event{
211+
txEvent1,
212+
txEvent2,
213+
txEvent3,
214+
blockEvent,
215+
},
216+
cdcEvents.events.Events,
217+
)
218+
219+
// assert that EVM transactions & receipts are sorted by their
220+
// TransactionIndex field
221+
for i := 0; i < txCount; i++ {
222+
tx := cdcEvents.transactions[i]
223+
receipt := cdcEvents.receipts[i]
224+
assert.Equal(t, tx.Hash(), receipt.TxHash)
225+
assert.Equal(t, uint(i), receipt.TransactionIndex)
226+
}
227+
})
156228
}
157229

158230
func Test_EventDecoding(t *testing.T) {
@@ -171,7 +243,7 @@ func Test_EventDecoding(t *testing.T) {
171243
// generate txs
172244
for i := 0; i < txCount; i++ {
173245
var err error
174-
txs[i], results[i], txEvents[i], err = newTransaction(uint64(i))
246+
txs[i], results[i], txEvents[i], err = newTransaction(uint64(i), uint16(i))
175247
require.NoError(t, err)
176248
hashes[i] = txs[i].Hash()
177249
blockEvents.Events = append(blockEvents.Events, txEvents[i])
@@ -224,12 +296,22 @@ func Test_EventDecoding(t *testing.T) {
224296
}
225297
}
226298

227-
func newTransaction(nonce uint64) (Transaction, *types.Result, flow.Event, error) {
228-
tx := gethTypes.NewTransaction(nonce, gethCommon.HexToAddress("0x1"), big.NewInt(10), uint64(100), big.NewInt(123), nil)
299+
func newTransaction(nonce uint64, txIndex uint16) (Transaction, *types.Result, flow.Event, error) {
300+
tx := gethTypes.NewTransaction(
301+
nonce,
302+
gethCommon.HexToAddress("0x1"),
303+
big.NewInt(10),
304+
uint64(100),
305+
big.NewInt(123),
306+
nil,
307+
)
229308
res := &types.Result{
309+
ValidationError: nil,
230310
VMError: nil,
231311
TxType: tx.Type(),
232312
GasConsumed: 1,
313+
CumulativeGasUsed: 1,
314+
GasRefund: 0,
233315
DeployedContractAddress: &types.Address{0x5, 0x6, 0x7},
234316
ReturnedData: []byte{0x55},
235317
Logs: []*gethTypes.Log{{
@@ -239,7 +321,10 @@ func newTransaction(nonce uint64) (Transaction, *types.Result, flow.Event, error
239321
Address: gethCommon.Address{0x3, 0x5},
240322
Topics: []gethCommon.Hash{{0x2, 0x66}, {0x7, 0x1}},
241323
}},
242-
TxHash: tx.Hash(),
324+
TxHash: tx.Hash(),
325+
Index: txIndex,
326+
PrecompiledCalls: []byte{},
327+
StateChangeCommitment: []byte{},
243328
}
244329

245330
txEncoded, err := tx.MarshalBinary()

0 commit comments

Comments
 (0)