From ea95387a1558dd0ee46991a13ff0132fcef0c715 Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Thu, 1 Feb 2024 17:26:42 +0100 Subject: [PATCH] rpc: Store and serve the event transaction ID (#1185) (cherry picked from commit 0c77361264c08c4d785694a13fca903416b76650) --- cmd/soroban-rpc/internal/events/events.go | 10 ++++++-- .../internal/events/events_test.go | 5 ++-- .../internal/methods/get_events.go | 12 ++++++---- .../internal/methods/get_events_test.go | 24 ++++++++++++++----- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/cmd/soroban-rpc/internal/events/events.go b/cmd/soroban-rpc/internal/events/events.go index e2c9030bf..e854cd773 100644 --- a/cmd/soroban-rpc/internal/events/events.go +++ b/cmd/soroban-rpc/internal/events/events.go @@ -19,6 +19,7 @@ type event struct { diagnosticEventXDR []byte txIndex uint32 eventIndex uint32 + txHash *xdr.Hash // intentionally stored as a pointer to save memory (amortized as soon as there are two events in a transaction) } func (e event) cursor(ledgerSeq uint32) Cursor { @@ -90,13 +91,15 @@ type Range struct { ClampEnd bool } +type ScanFunction func(xdr.DiagnosticEvent, Cursor, int64, *xdr.Hash) bool + // Scan applies f on all the events occurring in the given range. // The events are processed in sorted ascending Cursor order. // If f returns false, the scan terminates early (f will not be applied on // remaining events in the range). Note that a read lock is held for the // entire duration of the Scan function so f should be written in a way // to minimize latency. -func (m *MemoryStore) Scan(eventRange Range, f func(xdr.DiagnosticEvent, Cursor, int64) bool) (uint32, error) { +func (m *MemoryStore) Scan(eventRange Range, f ScanFunction) (uint32, error) { startTime := time.Now() m.lock.RLock() defer m.lock.RUnlock() @@ -126,7 +129,7 @@ func (m *MemoryStore) Scan(eventRange Range, f func(xdr.DiagnosticEvent, Cursor, if err != nil { return 0, err } - if !f(diagnosticEvent, cur, timestamp) { + if !f(diagnosticEvent, cur, timestamp, event.txHash) { return lastLedgerInWindow, nil } } @@ -235,10 +238,12 @@ func readEvents(networkPassphrase string, ledgerCloseMeta xdr.LedgerCloseMeta) ( if !tx.Result.Successful() { continue } + txEvents, err := tx.GetDiagnosticEvents() if err != nil { return nil, err } + txHash := tx.Result.TransactionHash for index, e := range txEvents { diagnosticEventXDR, err := e.MarshalBinary() if err != nil { @@ -248,6 +253,7 @@ func readEvents(networkPassphrase string, ledgerCloseMeta xdr.LedgerCloseMeta) ( diagnosticEventXDR: diagnosticEventXDR, txIndex: tx.Index, eventIndex: uint32(index), + txHash: &txHash, }) } } diff --git a/cmd/soroban-rpc/internal/events/events_test.go b/cmd/soroban-rpc/internal/events/events_test.go index fc4d3c6b9..df9b3385c 100644 --- a/cmd/soroban-rpc/internal/events/events_test.go +++ b/cmd/soroban-rpc/internal/events/events_test.go @@ -83,7 +83,7 @@ func eventsAreEqual(t *testing.T, a, b []event) { func TestScanRangeValidation(t *testing.T) { m := NewMemoryStore(interfaces.MakeNoOpDeamon(), "unit-tests", 4) - assertNoCalls := func(contractEvent xdr.DiagnosticEvent, cursor Cursor, timestamp int64) bool { + assertNoCalls := func(xdr.DiagnosticEvent, Cursor, int64, *xdr.Hash) bool { t.Fatalf("unexpected call") return true } @@ -362,7 +362,7 @@ func TestScan(t *testing.T) { for _, input := range genEquivalentInputs(testCase.input) { var events []event iterateAll := true - f := func(contractEvent xdr.DiagnosticEvent, cursor Cursor, ledgerCloseTimestamp int64) bool { + f := func(contractEvent xdr.DiagnosticEvent, cursor Cursor, ledgerCloseTimestamp int64, hash *xdr.Hash) bool { require.Equal(t, ledgerCloseTime(cursor.Ledger), ledgerCloseTimestamp) diagnosticEventXDR, err := contractEvent.MarshalBinary() require.NoError(t, err) @@ -370,6 +370,7 @@ func TestScan(t *testing.T) { diagnosticEventXDR: diagnosticEventXDR, txIndex: cursor.Tx, eventIndex: cursor.Event, + txHash: hash, }) return iterateAll } diff --git a/cmd/soroban-rpc/internal/methods/get_events.go b/cmd/soroban-rpc/internal/methods/get_events.go index e5bf36281..71cad1e9d 100644 --- a/cmd/soroban-rpc/internal/methods/get_events.go +++ b/cmd/soroban-rpc/internal/methods/get_events.go @@ -76,6 +76,7 @@ type EventInfo struct { Topic []string `json:"topic"` Value string `json:"value"` InSuccessfulContractCall bool `json:"inSuccessfulContractCall"` + TransactionHash string `json:"txHash"` } type GetEventsRequest struct { @@ -299,7 +300,7 @@ type GetEventsResponse struct { } type eventScanner interface { - Scan(eventRange events.Range, f func(xdr.DiagnosticEvent, events.Cursor, int64) bool) (uint32, error) + Scan(eventRange events.Range, f events.ScanFunction) (uint32, error) } type eventsRPCHandler struct { @@ -334,6 +335,7 @@ func (h eventsRPCHandler) getEvents(request GetEventsRequest) (GetEventsResponse cursor events.Cursor ledgerCloseTimestamp int64 event xdr.DiagnosticEvent + txHash *xdr.Hash } var found []entry latestLedger, err := h.scanner.Scan( @@ -343,9 +345,9 @@ func (h eventsRPCHandler) getEvents(request GetEventsRequest) (GetEventsResponse End: events.MaxCursor, ClampEnd: true, }, - func(event xdr.DiagnosticEvent, cursor events.Cursor, ledgerCloseTimestamp int64) bool { + func(event xdr.DiagnosticEvent, cursor events.Cursor, ledgerCloseTimestamp int64, txHash *xdr.Hash) bool { if request.Matches(event) { - found = append(found, entry{cursor, ledgerCloseTimestamp, event}) + found = append(found, entry{cursor, ledgerCloseTimestamp, event, txHash}) } return uint(len(found)) < limit }, @@ -363,6 +365,7 @@ func (h eventsRPCHandler) getEvents(request GetEventsRequest) (GetEventsResponse entry.event, entry.cursor, time.Unix(entry.ledgerCloseTimestamp, 0).UTC().Format(time.RFC3339), + entry.txHash.HexString(), ) if err != nil { return GetEventsResponse{}, errors.Wrap(err, "could not parse event") @@ -375,7 +378,7 @@ func (h eventsRPCHandler) getEvents(request GetEventsRequest) (GetEventsResponse }, nil } -func eventInfoForEvent(event xdr.DiagnosticEvent, cursor events.Cursor, ledgerClosedAt string) (EventInfo, error) { +func eventInfoForEvent(event xdr.DiagnosticEvent, cursor events.Cursor, ledgerClosedAt string, txHash string) (EventInfo, error) { v0, ok := event.Event.Body.GetV0() if !ok { return EventInfo{}, errors.New("unknown event version") @@ -411,6 +414,7 @@ func eventInfoForEvent(event xdr.DiagnosticEvent, cursor events.Cursor, ledgerCl Topic: topic, Value: data, InSuccessfulContractCall: event.InSuccessfulContractCall, + TransactionHash: txHash, } if event.Event.ContractId != nil { info.ContractID = strkey.MustEncode(strkey.VersionByteContract, (*event.Event.ContractId)[:]) diff --git a/cmd/soroban-rpc/internal/methods/get_events_test.go b/cmd/soroban-rpc/internal/methods/get_events_test.go index 4d15e2c0a..5d1b929c6 100644 --- a/cmd/soroban-rpc/internal/methods/get_events_test.go +++ b/cmd/soroban-rpc/internal/methods/get_events_test.go @@ -591,7 +591,8 @@ func TestGetEvents(t *testing.T) { ), )) } - assert.NoError(t, store.IngestEvents(ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...))) + ledgerCloseMeta := ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...) + assert.NoError(t, store.IngestEvents(ledgerCloseMeta)) handler := eventsRPCHandler{ scanner: store, @@ -626,6 +627,7 @@ func TestGetEvents(t *testing.T) { Topic: []string{value}, Value: value, InSuccessfulContractCall: true, + TransactionHash: ledgerCloseMeta.TransactionHash(i).HexString(), }) } assert.Equal(t, GetEventsResponse{expected, 1}, results) @@ -699,7 +701,8 @@ func TestGetEvents(t *testing.T) { ), )) } - assert.NoError(t, store.IngestEvents(ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...))) + ledgerCloseMeta := ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...) + assert.NoError(t, store.IngestEvents(ledgerCloseMeta)) number := xdr.Uint64(4) handler := eventsRPCHandler{ @@ -738,6 +741,7 @@ func TestGetEvents(t *testing.T) { Topic: []string{counterXdr, value}, Value: value, InSuccessfulContractCall: true, + TransactionHash: ledgerCloseMeta.TransactionHash(4).HexString(), }, } assert.Equal(t, GetEventsResponse{expected, 1}, results) @@ -792,7 +796,8 @@ func TestGetEvents(t *testing.T) { ), ), } - assert.NoError(t, store.IngestEvents(ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...))) + ledgerCloseMeta := ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...) + assert.NoError(t, store.IngestEvents(ledgerCloseMeta)) handler := eventsRPCHandler{ scanner: store, @@ -832,6 +837,7 @@ func TestGetEvents(t *testing.T) { Topic: []string{counterXdr, value}, Value: value, InSuccessfulContractCall: true, + TransactionHash: ledgerCloseMeta.TransactionHash(3).HexString(), }, } assert.Equal(t, GetEventsResponse{expected, 1}, results) @@ -865,7 +871,8 @@ func TestGetEvents(t *testing.T) { ), ), } - assert.NoError(t, store.IngestEvents(ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...))) + ledgerCloseMeta := ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...) + assert.NoError(t, store.IngestEvents(ledgerCloseMeta)) handler := eventsRPCHandler{ scanner: store, @@ -892,6 +899,7 @@ func TestGetEvents(t *testing.T) { Topic: []string{counterXdr}, Value: counterXdr, InSuccessfulContractCall: true, + TransactionHash: ledgerCloseMeta.TransactionHash(0).HexString(), }, } assert.Equal(t, GetEventsResponse{expected, 1}, results) @@ -913,7 +921,8 @@ func TestGetEvents(t *testing.T) { ), )) } - assert.NoError(t, store.IngestEvents(ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...))) + ledgerCloseMeta := ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...) + assert.NoError(t, store.IngestEvents(ledgerCloseMeta)) handler := eventsRPCHandler{ scanner: store, @@ -947,6 +956,7 @@ func TestGetEvents(t *testing.T) { Topic: []string{value}, Value: value, InSuccessfulContractCall: true, + TransactionHash: ledgerCloseMeta.TransactionHash(i).HexString(), }) } assert.Equal(t, GetEventsResponse{expected, 1}, results) @@ -996,7 +1006,8 @@ func TestGetEvents(t *testing.T) { ), ), } - assert.NoError(t, store.IngestEvents(ledgerCloseMetaWithEvents(5, now.Unix(), txMeta...))) + ledgerCloseMeta := ledgerCloseMetaWithEvents(5, now.Unix(), txMeta...) + assert.NoError(t, store.IngestEvents(ledgerCloseMeta)) id := &events.Cursor{Ledger: 5, Tx: 1, Op: 0, Event: 0} handler := eventsRPCHandler{ @@ -1031,6 +1042,7 @@ func TestGetEvents(t *testing.T) { Topic: []string{counterXdr}, Value: expectedXdr, InSuccessfulContractCall: true, + TransactionHash: ledgerCloseMeta.TransactionHash(i).HexString(), }) } assert.Equal(t, GetEventsResponse{expected, 5}, results)