diff --git a/cmd/stellar-rpc/internal/db/event.go b/cmd/stellar-rpc/internal/db/event.go index feb287d6..b31d218d 100644 --- a/cmd/stellar-rpc/internal/db/event.go +++ b/cmd/stellar-rpc/internal/db/event.go @@ -30,6 +30,16 @@ type EventWriter interface { InsertEvents(lcm xdr.LedgerCloseMeta) error } +// EventOrder represents the order in which events are returned +type EventOrder string + +const ( + // EventOrderAsc returns events in ascending order (oldest first) + EventOrderAsc EventOrder = "asc" + // EventOrderDesc returns events in descending order (newest first) + EventOrderDesc EventOrder = "desc" +) + // EventReader has all the public methods to fetch events from DB type EventReader interface { GetEvents( @@ -38,6 +48,7 @@ type EventReader interface { contractIDs [][]byte, topics NestedTopicArray, eventTypes []int, + order EventOrder, f ScanFunction, ) error } @@ -292,7 +303,7 @@ func (eventHandler *eventHandler) trimEvents(latestLedgerSeq uint32, retentionWi // GetEvents applies f on all the events occurring in the given range with // specified contract IDs if provided. The events are returned in sorted -// ascending Cursor order. +// order based on the order parameter (ascending or descending). // // If f returns false, the scan terminates early (f will not be applied on // remaining events in the range). @@ -304,16 +315,23 @@ func (eventHandler *eventHandler) GetEvents( contractIDs [][]byte, topics NestedTopicArray, eventTypes []int, + order EventOrder, scanner ScanFunction, ) error { start := time.Now() + // Determine sort order + orderDirection := "ASC" + if order == EventOrderDesc { + orderDirection = "DESC" + } + rowQ := sq. Select("id", "event_data", "transaction_hash", "ledger_close_time"). From(eventTableName). Where(sq.GtOrEq{"id": cursorRange.Start.String()}). Where(sq.Lt{"id": cursorRange.End.String()}). - OrderBy("id ASC") + OrderBy("id " + orderDirection) if len(contractIDs) > 0 { rowQ = rowQ.Where(sq.Eq{"contract_id": contractIDs}) diff --git a/cmd/stellar-rpc/internal/db/event_test.go b/cmd/stellar-rpc/internal/db/event_test.go index 38208918..026da626 100644 --- a/cmd/stellar-rpc/internal/db/event_test.go +++ b/cmd/stellar-rpc/internal/db/event_test.go @@ -201,6 +201,6 @@ func TestInsertEvents(t *testing.T) { end := protocol.Cursor{Ledger: 100} cursorRange := protocol.CursorRange{Start: start, End: end} - err = eventReader.GetEvents(ctx, cursorRange, nil, nil, nil, nil) + err = eventReader.GetEvents(ctx, cursorRange, nil, nil, nil, EventOrderAsc, nil) require.NoError(t, err) } diff --git a/cmd/stellar-rpc/internal/db/transaction_test.go b/cmd/stellar-rpc/internal/db/transaction_test.go index a392bb3d..c2619625 100644 --- a/cmd/stellar-rpc/internal/db/transaction_test.go +++ b/cmd/stellar-rpc/internal/db/transaction_test.go @@ -229,7 +229,7 @@ func TestTransactionFound(t *testing.T) { end := protocol.Cursor{Ledger: 1000} cursorRange := protocol.CursorRange{Start: start, End: end} - err = eventReader.GetEvents(ctx, cursorRange, nil, nil, nil, nil) + err = eventReader.GetEvents(ctx, cursorRange, nil, nil, nil, EventOrderAsc, nil) require.NoError(t, err) // check all 200 cases diff --git a/cmd/stellar-rpc/internal/methods/get_events.go b/cmd/stellar-rpc/internal/methods/get_events.go index c3c9ea79..e88e1e2a 100644 --- a/cmd/stellar-rpc/internal/methods/get_events.go +++ b/cmd/stellar-rpc/internal/methods/get_events.go @@ -117,30 +117,75 @@ func (h eventsRPCHandler) getEvents(ctx context.Context, request protocol.GetEve } } - start := protocol.Cursor{Ledger: request.StartLedger} + order := protocol.EventOrderAsc + if request.Pagination != nil && request.Pagination.Order != "" { + order = request.Pagination.Order + } + isDescending := order == protocol.EventOrderDesc + limit := h.defaultLimit - if request.Pagination != nil { - if request.Pagination.Cursor != nil { + if request.Pagination != nil && request.Pagination.Limit > 0 { + limit = request.Pagination.Limit + } + + // Build cursor range based on order direction + // For ASC: startLedger is lower bound, endLedger is upper bound + // For DESC: startLedger is upper bound, endLedger is lower bound + var cursorRange protocol.CursorRange + var validationLedger uint32 // The ledger to validate against retention window + + if isDescending { + // DESC order: startLedger is upper bound, scan backwards + // Calculate lower bound + lowerBound := uint32(0) + if request.StartLedger > LedgerScanLimit { + lowerBound = request.StartLedger - LedgerScanLimit + } + // lowerBound should not be before ledger retention window + lowerBound = max(ledgerRange.FirstLedger.Sequence, lowerBound) + if request.EndLedger != 0 { + lowerBound = max(request.EndLedger, lowerBound) + } + + // Handle cursor-based pagination for DESC + upperCursor := protocol.Cursor{Ledger: request.StartLedger + 1} // +1 because end is exclusive + if request.Pagination != nil && request.Pagination.Cursor != nil { + upperCursor = *request.Pagination.Cursor + // For descending order, we move backwards from the cursor + if upperCursor.Event > 0 { + upperCursor.Event-- + } else { + upperCursor = decrementCursor(upperCursor) + } + } + + cursorRange = protocol.CursorRange{ + Start: protocol.Cursor{Ledger: lowerBound}, + End: upperCursor, + } + validationLedger = request.StartLedger + } else { + // ASC order: startLedger is lower bound, scan forwards (original behavior) + start := protocol.Cursor{Ledger: request.StartLedger} + if request.Pagination != nil && request.Pagination.Cursor != nil { start = *request.Pagination.Cursor - // increment event index because, when paginating, we start with the - // item right after the cursor start.Event++ } - if request.Pagination.Limit > 0 { - limit = request.Pagination.Limit + + endLedger := start.Ledger + LedgerScanLimit + endLedger = min(ledgerRange.LastLedger.Sequence+1, endLedger) + if request.EndLedger != 0 { + endLedger = min(request.EndLedger, endLedger) } - } - endLedger := start.Ledger + LedgerScanLimit - // endLedger should not exceed ledger retention window - endLedger = min(ledgerRange.LastLedger.Sequence+1, endLedger) - if request.EndLedger != 0 { - endLedger = min(request.EndLedger, endLedger) - } - end := protocol.Cursor{Ledger: endLedger} - cursorRange := protocol.CursorRange{Start: start, End: end} + cursorRange = protocol.CursorRange{ + Start: start, + End: protocol.Cursor{Ledger: endLedger}, + } + validationLedger = request.StartLedger + } - if start.Ledger < ledgerRange.FirstLedger.Sequence || start.Ledger > ledgerRange.LastLedger.Sequence { + if validationLedger < ledgerRange.FirstLedger.Sequence || validationLedger > ledgerRange.LastLedger.Sequence { return protocol.GetEventsResponse{}, &jrpc2.Error{ Code: jrpc2.InvalidRequest, Message: fmt.Sprintf( @@ -179,7 +224,13 @@ func (h eventsRPCHandler) getEvents(ctx context.Context, request protocol.GetEve return uint(len(found)) < limit } - err = h.dbReader.GetEvents(ctx, cursorRange, contractIDs, topics, eventTypes, eventScanFunction) + // Convert order to db.EventOrder + dbOrder := db.EventOrderAsc + if isDescending { + dbOrder = db.EventOrderDesc + } + + err = h.dbReader.GetEvents(ctx, cursorRange, contractIDs, topics, eventTypes, dbOrder, eventScanFunction) if err != nil { return protocol.GetEventsResponse{}, &jrpc2.Error{ Code: jrpc2.InvalidRequest, Message: err.Error(), @@ -207,11 +258,15 @@ func (h eventsRPCHandler) getEvents(ctx context.Context, request protocol.GetEve cursor = lastEvent.ID } else { // cursor represents end of the search window if events does not reach limit - // here endLedger is always exclusive when fetching events - // so search window is max Cursor value with endLedger - 1 - maxCursor := protocol.MaxCursor - maxCursor.Ledger = endLedger - 1 - cursor = maxCursor.String() + if isDescending { + // For descending order, the cursor represents the lower bound of the search window + cursor = cursorRange.Start.String() + } else { + // For ascending order, the cursor represents the upper bound of the search window + maxCursor := protocol.MaxCursor + maxCursor.Ledger = cursorRange.End.Ledger - 1 + cursor = maxCursor.String() + } } return protocol.GetEventsResponse{ @@ -225,6 +280,23 @@ func (h eventsRPCHandler) getEvents(ctx context.Context, request protocol.GetEve }, nil } +// decrementCursor decrements the cursor to the previous position +func decrementCursor(c protocol.Cursor) protocol.Cursor { + // If we're at the minimum cursor for this ledger, we can't go further back + // The cursor will remain at position 0,0,0 for the ledger + if c.Event == 0 && c.Op == 0 && c.Tx == 0 { + return c + } + // Set to the maximum possible cursor value to capture all earlier events + // This effectively means "everything before this cursor in this ledger" + return protocol.Cursor{ + Ledger: c.Ledger, + Tx: c.Tx, + Op: c.Op, + Event: 0, // The DB query will handle the rest with DESC ordering + } +} + func eventInfoForEvent( event xdr.DiagnosticEvent, cursor protocol.Cursor, diff --git a/cmd/stellar-rpc/internal/methods/get_events_test.go b/cmd/stellar-rpc/internal/methods/get_events_test.go index 478b7943..ecf13710 100644 --- a/cmd/stellar-rpc/internal/methods/get_events_test.go +++ b/cmd/stellar-rpc/internal/methods/get_events_test.go @@ -1012,6 +1012,199 @@ func TestGetEvents(t *testing.T) { results, ) }) + + t.Run("with descending order", func(t *testing.T) { + dbx := newTestDB(t) + ctx := context.TODO() + log := log.DefaultLogger + log.SetLevel(logrus.TraceLevel) + + writer := db.NewReadWriter(log, dbx, interfaces.MakeNoOpDeamon(), 10, 10, passphrase) + write, err := writer.NewTx(ctx) + require.NoError(t, err) + + ledgerW, eventW := write.LedgerWriter(), write.EventWriter() + store := db.NewEventReader(log, dbx, passphrase) + + contractID := xdr.ContractId([32]byte{}) + var txMeta []xdr.TransactionMeta + for i := range 5 { + number := xdr.Uint64(i) + txMeta = append(txMeta, transactionMetaWithEvents( + contractEvent( + contractID, + xdr.ScVec{ + xdr.ScVal{Type: xdr.ScValTypeScvSymbol, Sym: &counter}, + }, + xdr.ScVal{Type: xdr.ScValTypeScvU64, U64: &number}, + ), + )) + } + ledgerCloseMeta := ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...) + require.NoError(t, ledgerW.InsertLedger(ledgerCloseMeta), "ingestion failed for ledger") + require.NoError(t, eventW.InsertEvents(ledgerCloseMeta), "ingestion failed for events") + require.NoError(t, write.Commit(ledgerCloseMeta, nil)) + + handler := eventsRPCHandler{ + dbReader: store, + maxLimit: 10000, + defaultLimit: 100, + ledgerReader: db.NewLedgerReader(dbx), + } + + // Test descending order returns events in reverse order + results, err := handler.getEvents(ctx, protocol.GetEventsRequest{ + StartLedger: 1, + Pagination: &protocol.PaginationOptions{ + Order: protocol.EventOrderDesc, + }, + }) + require.NoError(t, err) + require.Len(t, results.Events, 5) + + // Verify events are returned in descending order (tx 5, 4, 3, 2, 1) + for i, event := range results.Events { + expectedTxIndex := uint32(5 - i) + assert.Equal(t, expectedTxIndex, event.TxIndex, + "event %d should have TxIndex %d", i, expectedTxIndex) + } + }) + + t.Run("descending order with limit", func(t *testing.T) { + dbx := newTestDB(t) + ctx := context.TODO() + log := log.DefaultLogger + log.SetLevel(logrus.TraceLevel) + + writer := db.NewReadWriter(log, dbx, interfaces.MakeNoOpDeamon(), 10, 10, passphrase) + write, err := writer.NewTx(ctx) + require.NoError(t, err) + + ledgerW, eventW := write.LedgerWriter(), write.EventWriter() + store := db.NewEventReader(log, dbx, passphrase) + + contractID := xdr.ContractId([32]byte{}) + var txMeta []xdr.TransactionMeta + for i := range 10 { + number := xdr.Uint64(i) + txMeta = append(txMeta, transactionMetaWithEvents( + contractEvent( + contractID, + xdr.ScVec{ + xdr.ScVal{Type: xdr.ScValTypeScvSymbol, Sym: &counter}, + }, + xdr.ScVal{Type: xdr.ScValTypeScvU64, U64: &number}, + ), + )) + } + ledgerCloseMeta := ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...) + require.NoError(t, ledgerW.InsertLedger(ledgerCloseMeta), "ingestion failed for ledger") + require.NoError(t, eventW.InsertEvents(ledgerCloseMeta), "ingestion failed for events") + require.NoError(t, write.Commit(ledgerCloseMeta, nil)) + + handler := eventsRPCHandler{ + dbReader: store, + maxLimit: 10000, + defaultLimit: 100, + ledgerReader: db.NewLedgerReader(dbx), + } + + // Test descending order with limit returns the N newest events + results, err := handler.getEvents(ctx, protocol.GetEventsRequest{ + StartLedger: 1, + Pagination: &protocol.PaginationOptions{ + Limit: 3, + Order: protocol.EventOrderDesc, + }, + }) + require.NoError(t, err) + require.Len(t, results.Events, 3) + + // Should return the 3 newest events (tx 10, 9, 8 in that order) + assert.Equal(t, uint32(10), results.Events[0].TxIndex) + assert.Equal(t, uint32(9), results.Events[1].TxIndex) + assert.Equal(t, uint32(8), results.Events[2].TxIndex) + }) + + t.Run("invalid order parameter", func(t *testing.T) { + dbx := newTestDB(t) + log := log.DefaultLogger + log.SetLevel(logrus.TraceLevel) + store := db.NewEventReader(log, dbx, passphrase) + + handler := eventsRPCHandler{ + dbReader: store, + maxLimit: 10000, + defaultLimit: 100, + ledgerReader: db.NewLedgerReader(dbx), + } + + _, err := handler.getEvents(context.TODO(), protocol.GetEventsRequest{ + StartLedger: 1, + Pagination: &protocol.PaginationOptions{ + Order: "invalid", + }, + }) + require.Error(t, err) + require.Contains(t, err.Error(), "order must be 'asc' or 'desc'") + }) + + t.Run("ascending order explicitly set", func(t *testing.T) { + dbx := newTestDB(t) + ctx := context.TODO() + log := log.DefaultLogger + log.SetLevel(logrus.TraceLevel) + + writer := db.NewReadWriter(log, dbx, interfaces.MakeNoOpDeamon(), 10, 10, passphrase) + write, err := writer.NewTx(ctx) + require.NoError(t, err) + + ledgerW, eventW := write.LedgerWriter(), write.EventWriter() + store := db.NewEventReader(log, dbx, passphrase) + + contractID := xdr.ContractId([32]byte{}) + var txMeta []xdr.TransactionMeta + for i := range 5 { + number := xdr.Uint64(i) + txMeta = append(txMeta, transactionMetaWithEvents( + contractEvent( + contractID, + xdr.ScVec{ + xdr.ScVal{Type: xdr.ScValTypeScvSymbol, Sym: &counter}, + }, + xdr.ScVal{Type: xdr.ScValTypeScvU64, U64: &number}, + ), + )) + } + ledgerCloseMeta := ledgerCloseMetaWithEvents(1, now.Unix(), txMeta...) + require.NoError(t, ledgerW.InsertLedger(ledgerCloseMeta), "ingestion failed for ledger") + require.NoError(t, eventW.InsertEvents(ledgerCloseMeta), "ingestion failed for events") + require.NoError(t, write.Commit(ledgerCloseMeta, nil)) + + handler := eventsRPCHandler{ + dbReader: store, + maxLimit: 10000, + defaultLimit: 100, + ledgerReader: db.NewLedgerReader(dbx), + } + + // Test explicitly set ascending order + results, err := handler.getEvents(ctx, protocol.GetEventsRequest{ + StartLedger: 1, + Pagination: &protocol.PaginationOptions{ + Order: protocol.EventOrderAsc, + }, + }) + require.NoError(t, err) + require.Len(t, results.Events, 5) + + // Verify events are returned in ascending order (tx 1, 2, 3, 4, 5) + for i, event := range results.Events { + expectedTxIndex := uint32(i + 1) + assert.Equal(t, expectedTxIndex, event.TxIndex, + "event %d should have TxIndex %d", i, expectedTxIndex) + } + }) } func BenchmarkGetEvents(b *testing.B) { diff --git a/go.mod b/go.mod index 1e45d072..1a8e2590 100644 --- a/go.mod +++ b/go.mod @@ -129,14 +129,14 @@ require ( go.opentelemetry.io/otel/metric v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.43.0 // indirect + golang.org/x/crypto v0.45.0 // indirect golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect golang.org/x/mod v0.29.0 - golang.org/x/net v0.46.0 // indirect + golang.org/x/net v0.47.0 // indirect golang.org/x/oauth2 v0.32.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/text v0.30.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.14.0 // indirect google.golang.org/api v0.254.0 // indirect google.golang.org/genproto v0.0.0-20251029180050-ab9386a59fda // indirect @@ -150,3 +150,5 @@ require ( gopkg.in/tylerb/graceful.v1 v1.2.15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/stellar/go-stellar-sdk => ../go-stellar-sdk diff --git a/go.sum b/go.sum index 4162620f..284f6298 100644 --- a/go.sum +++ b/go.sum @@ -429,8 +429,6 @@ github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= -github.com/stellar/go-stellar-sdk v0.0.0-20251208182759-7568ee53f4fd h1:90j0mtcO8J6v2v/EVgvyfK+L1WlLW9Fg2Vn4/zAv/0Q= -github.com/stellar/go-stellar-sdk v0.0.0-20251208182759-7568ee53f4fd/go.mod h1:fZPcxQZw1I0zZ+X76uFcVPqmQCaYbWc87lDFW/kQJaY= github.com/stellar/go-xdr v0.0.0-20231122183749-b53fb00bcac2 h1:OzCVd0SV5qE3ZcDeSFCmOWLZfEWZ3Oe8KtmSOYKEVWE= github.com/stellar/go-xdr v0.0.0-20231122183749-b53fb00bcac2/go.mod h1:yoxyU/M8nl9LKeWIoBrbDPQ7Cy+4jxRcWcOayZ4BMps= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -515,8 +513,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -587,8 +585,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -610,8 +608,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -650,11 +648,11 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -663,8 +661,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=