diff --git a/api/doc/thor.yaml b/api/doc/thor.yaml index 44eb3eb09..5570c97c0 100644 --- a/api/doc/thor.yaml +++ b/api/doc/thor.yaml @@ -1009,8 +1009,6 @@ components: enum: - asc - desc - optionalData: - $ref: '#/components/schemas/EventOptionalData' EventLogsResponse: type: array @@ -1022,7 +1020,7 @@ components: - $ref: '#/components/schemas/Event' - properties: meta: - $ref: '#/components/schemas/EventLogMeta' + $ref: '#/components/schemas/LogMeta' TransferLogFilterRequest: type: object @@ -1326,65 +1324,13 @@ components: description: The index of the clause in the transaction, from which the log was generated. example: 0 nullable: false - - EventLogMeta: - title: EventLogMeta - type: object - description: The event or transfer log metadata such as block number, block timestamp, etc. - properties: - blockID: - type: string - format: hex - description: The block identifier in which the log was included. - example: '0x0004f6cc88bb4626a92907718e82f255b8fa511453a78e8797eb8cea3393b215' - nullable: false - pattern: '^0x[0-9a-f]{64}$' - blockNumber: - type: integer - format: uint32 - description: The block number (height) of the block in which the log was included. - example: 325324 - nullable: false - blockTimestamp: - type: integer - format: uint64 - description: The UNIX timestamp of the block in which the log was included. - example: 1533267900 - nullable: false - txID: - type: string - format: hex - description: The transaction identifier, from which the log was generated. - example: '0x284bba50ef777889ff1a367ed0b38d5e5626714477c40de38d71cedd6f9fa477' - nullable: false - pattern: '^0x[0-9a-f]{64}$' - txOrigin: - type: string - description: The account from which the transaction was sent. - example: '0xdb4027477b2a8fe4c83c6dafe7f86678bb1b8a8d' - nullable: false - pattern: '^0x[0-9a-f]{40}$' - clauseIndex: - type: integer - format: uint32 - description: The index of the clause in the transaction, from which the log was generated. - example: 0 - nullable: false - extendedLogMeta: - $ref: '#/components/schemas/ExtendedLogMeta' - - ExtendedLogMeta: - title: ExtendedLogMeta - type: object - nullable: true - properties: txIndex: description: The index of the transaction in the block, from which the log was generated. type: integer nullable: true example: 1 logIndex: - descrption: The index of the log in the receipt's outputs. + description: The index of the log in the receipt's outputs. type: integer nullable: true example: 1 @@ -1918,6 +1864,11 @@ components: The limit of records to be included in the output. Use this parameter for pagination. Default's to all results. + includeIndexes: + type: boolean + example: true + nullable: true + description: Include both transaction and log index in the response. description: | Include these parameters to receive filtered results in a paged format. @@ -1928,7 +1879,8 @@ components: { "options": { "offset": 0, - "limit": 10 + "limit": 10, + "includeIndexes": true } } ``` diff --git a/api/events/events.go b/api/events/events.go index 62bdec355..0001280df 100644 --- a/api/events/events.go +++ b/api/events/events.go @@ -44,7 +44,7 @@ func (e *Events) filter(ctx context.Context, ef *EventFilter) ([]*FilteredEvent, } fes := make([]*FilteredEvent, len(events)) for i, e := range events { - fes[i] = convertEvent(e, ef.OptionalData) + fes[i] = convertEvent(e, ef.Options.IncludeIndexes) } return fes, nil } diff --git a/api/events/events_test.go b/api/events/events_test.go index 745306914..a16a1be84 100644 --- a/api/events/events_test.go +++ b/api/events/events_test.go @@ -59,61 +59,36 @@ func TestEvents(t *testing.T) { testEventWithBlocks(t, blocksToInsert) } -func TestOptionalData(t *testing.T) { +func TestOptionalIndexes(t *testing.T) { db := createDb(t) initEventServer(t, db, defaultLogLimit) defer ts.Close() insertBlocks(t, db, 5) testCases := []struct { - name string - optData *events.EventOptionalData - expected *events.ExtendedLogMeta + name string + includeIndexes bool + expected *uint32 }{ { - name: "empty optional data", - optData: &events.EventOptionalData{}, - expected: nil, + name: "do not include indexes", + includeIndexes: false, + expected: nil, }, { - name: "optional data with txIndex", - optData: &events.EventOptionalData{ - TxIndex: true, - }, - expected: &events.ExtendedLogMeta{ - TxIndex: new(uint32), - }, - }, - { - name: "optional data with logIndex", - optData: &events.EventOptionalData{ - LogIndex: true, - }, - expected: &events.ExtendedLogMeta{ - LogIndex: new(uint32), - }, - }, - { - name: "optional data with txIndex and logIndex", - optData: &events.EventOptionalData{ - TxIndex: true, - LogIndex: true, - }, - expected: &events.ExtendedLogMeta{ - TxIndex: new(uint32), - LogIndex: new(uint32), - }, + name: "include indexes", + includeIndexes: true, + expected: new(uint32), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { filter := events.EventFilter{ - CriteriaSet: make([]*events.EventCriteria, 0), - Range: nil, - Options: &logdb.Options{Limit: 6}, - Order: logdb.DESC, - OptionalData: tc.optData, + CriteriaSet: make([]*events.EventCriteria, 0), + Range: nil, + Options: &logdb.Options{Limit: 6, IncludeIndexes: tc.includeIndexes}, + Order: logdb.DESC, } res, statusCode := httpPost(t, ts.URL+"/events", filter) @@ -126,7 +101,8 @@ func TestOptionalData(t *testing.T) { assert.Equal(t, 5, len(tLogs)) for _, tLog := range tLogs { - assert.Equal(t, tc.expected, tLog.Meta.ExtendedLogMeta) + assert.Equal(t, tc.expected, tLog.Meta.TxIndex) + assert.Equal(t, tc.expected, tLog.Meta.LogIndex) } }) } diff --git a/api/events/types.go b/api/events/types.go index f9c2f612a..278b66f76 100644 --- a/api/events/types.go +++ b/api/events/types.go @@ -17,33 +17,14 @@ import ( ) type LogMeta struct { - BlockID thor.Bytes32 `json:"blockID"` - BlockNumber uint32 `json:"blockNumber"` - BlockTimestamp uint64 `json:"blockTimestamp"` - TxID thor.Bytes32 `json:"txID"` - TxOrigin thor.Address `json:"txOrigin"` - ClauseIndex uint32 `json:"clauseIndex"` - ExtendedLogMeta *ExtendedLogMeta `json:"extendedLogMeta,omitempty"` -} - -type ExtendedLogMeta struct { - TxIndex *uint32 `json:"txIndex,omitempty"` - LogIndex *uint32 `json:"logIndex,omitempty"` -} - -func (opt *ExtendedLogMeta) Empty() bool { - return opt == nil || (opt.TxIndex == nil && opt.LogIndex == nil) -} - -func (opt *ExtendedLogMeta) String() string { - var parts []string - if opt.TxIndex != nil { - parts = append(parts, fmt.Sprintf("txIndex: %v", *opt.TxIndex)) - } - if opt.LogIndex != nil { - parts = append(parts, fmt.Sprintf("logIndex: %v", *opt.LogIndex)) - } - return fmt.Sprintf("%v", parts) + BlockID thor.Bytes32 `json:"blockID"` + BlockNumber uint32 `json:"blockNumber"` + BlockTimestamp uint64 `json:"blockTimestamp"` + TxID thor.Bytes32 `json:"txID"` + TxOrigin thor.Address `json:"txOrigin"` + ClauseIndex uint32 `json:"clauseIndex"` + TxIndex *uint32 `json:"txIndex,omitempty"` + LogIndex *uint32 `json:"logIndex,omitempty"` } type TopicSet struct { @@ -63,7 +44,7 @@ type FilteredEvent struct { } // convert a logdb.Event into a json format Event -func convertEvent(event *logdb.Event, eventOptionalData *EventOptionalData) *FilteredEvent { +func convertEvent(event *logdb.Event, addIndexes bool) *FilteredEvent { fe := &FilteredEvent{ Address: event.Address, Data: hexutil.Encode(event.Data), @@ -76,7 +57,11 @@ func convertEvent(event *logdb.Event, eventOptionalData *EventOptionalData) *Fil ClauseIndex: event.ClauseIndex, }, } - fe = addOptionalData(fe, event, eventOptionalData) + + if addIndexes { + fe.Meta.TxIndex = &event.TxIndex + fe.Meta.LogIndex = &event.Index + } fe.Topics = make([]*thor.Bytes32, 0) for i := 0; i < 5; i++ { @@ -87,24 +72,6 @@ func convertEvent(event *logdb.Event, eventOptionalData *EventOptionalData) *Fil return fe } -func addOptionalData(fe *FilteredEvent, event *logdb.Event, eventOptionalData *EventOptionalData) *FilteredEvent { - if eventOptionalData != nil { - opt := &ExtendedLogMeta{} - - if eventOptionalData.LogIndex { - opt.LogIndex = &event.Index - } - if eventOptionalData.TxIndex { - opt.TxIndex = &event.TxIndex - } - - if !opt.Empty() { - fe.Meta.ExtendedLogMeta = opt - } - } - return fe -} - func (e *FilteredEvent) String() string { return fmt.Sprintf(` Event( @@ -117,7 +84,8 @@ func (e *FilteredEvent) String() string { txID %v, txOrigin %v, clauseIndex %v, - optionalData (%v)) + txIndex: %v, + logIndex: %v) )`, e.Address, e.Topics, @@ -128,7 +96,8 @@ func (e *FilteredEvent) String() string { e.Meta.TxID, e.Meta.TxOrigin, e.Meta.ClauseIndex, - e.Meta.ExtendedLogMeta, + e.Meta.TxIndex, + e.Meta.LogIndex, ) } @@ -138,11 +107,10 @@ type EventCriteria struct { } type EventFilter struct { - CriteriaSet []*EventCriteria `json:"criteriaSet"` - Range *Range `json:"range"` - Options *logdb.Options `json:"options"` - Order logdb.Order `json:"order"` - OptionalData *EventOptionalData `json:"optionalData,omitempty"` + CriteriaSet []*EventCriteria `json:"criteriaSet"` + Range *Range `json:"range"` + Options *logdb.Options `json:"options"` + Order logdb.Order `json:"order"` } type EventOptionalData struct { diff --git a/api/events/types_test.go b/api/events/types_test.go index e6216094d..cefc56768 100644 --- a/api/events/types_test.go +++ b/api/events/types_test.go @@ -145,10 +145,6 @@ func TestConvertEvent(t *testing.T) { nil, }, } - eventOptData := &EventOptionalData{ - LogIndex: true, - TxIndex: true, - } expectedTopics := []*thor.Bytes32{ {0x0B}, @@ -156,7 +152,7 @@ func TestConvertEvent(t *testing.T) { } expectedData := hexutil.Encode(event.Data) - result := convertEvent(event, eventOptData) + result := convertEvent(event, true) assert.Equal(t, event.Address, result.Address) assert.Equal(t, expectedData, result.Data) @@ -164,40 +160,9 @@ func TestConvertEvent(t *testing.T) { assert.Equal(t, event.BlockNumber, result.Meta.BlockNumber) assert.Equal(t, event.BlockTime, result.Meta.BlockTimestamp) assert.Equal(t, event.TxID, result.Meta.TxID) - assert.Equal(t, event.TxIndex, *result.Meta.ExtendedLogMeta.TxIndex) - assert.Equal(t, event.Index, *result.Meta.ExtendedLogMeta.LogIndex) + assert.Equal(t, event.TxIndex, *result.Meta.TxIndex) + assert.Equal(t, event.Index, *result.Meta.LogIndex) assert.Equal(t, event.TxOrigin, result.Meta.TxOrigin) assert.Equal(t, event.ClauseIndex, result.Meta.ClauseIndex) assert.Equal(t, expectedTopics, result.Topics) } - -func TestIsEmpty(t *testing.T) { - // Empty cases - var nilCase *ExtendedLogMeta - assert.True(t, nilCase.Empty()) - - emptyCase := &ExtendedLogMeta{} - assert.True(t, emptyCase.Empty()) - - emptyCase = &ExtendedLogMeta{ - LogIndex: nil, - } - assert.True(t, emptyCase.Empty()) - - emptyCase = &ExtendedLogMeta{ - TxIndex: nil, - } - assert.True(t, emptyCase.Empty()) - - // Not empty cases - val := uint32(1) - notEmptyCase := &ExtendedLogMeta{ - LogIndex: &val, - } - assert.False(t, notEmptyCase.Empty()) - - notEmptyCase = &ExtendedLogMeta{ - TxIndex: &val, - } - assert.False(t, notEmptyCase.Empty()) -} diff --git a/api/transfers/transfers.go b/api/transfers/transfers.go index cad4ee6b3..7d548b29d 100644 --- a/api/transfers/transfers.go +++ b/api/transfers/transfers.go @@ -50,7 +50,7 @@ func (t *Transfers) filter(ctx context.Context, filter *TransferFilter) ([]*Filt } tLogs := make([]*FilteredTransfer, len(transfers)) for i, trans := range transfers { - tLogs[i] = convertTransfer(trans) + tLogs[i] = convertTransfer(trans, filter.Options.IncludeIndexes) } return tLogs, nil } diff --git a/api/transfers/transfers_test.go b/api/transfers/transfers_test.go index 0c7814993..b1bd8d644 100644 --- a/api/transfers/transfers_test.go +++ b/api/transfers/transfers_test.go @@ -3,7 +3,7 @@ // Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying // file LICENSE or -package transfers_test +package transfers import ( "bytes" @@ -16,10 +16,10 @@ import ( "strings" "testing" + "github.com/ethereum/go-ethereum/common/math" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" "github.com/vechain/thor/v2/api/events" - "github.com/vechain/thor/v2/api/transfers" "github.com/vechain/thor/v2/block" "github.com/vechain/thor/v2/chain" "github.com/vechain/thor/v2/genesis" @@ -60,7 +60,7 @@ func TestOption(t *testing.T) { defer ts.Close() insertBlocks(t, db, 5) - filter := transfers.TransferFilter{ + filter := TransferFilter{ CriteriaSet: make([]*logdb.TransferCriteria, 0), Range: nil, Options: &logdb.Options{Limit: 6}, @@ -94,6 +94,87 @@ func TestOption(t *testing.T) { assert.Equal(t, "the number of filtered logs exceeds the maximum allowed value of 5, please use pagination", strings.Trim(string(res), "\n")) } +func TestOptionalData(t *testing.T) { + db := createDb(t) + initTransferServer(t, db, defaultLogLimit) + defer ts.Close() + insertBlocks(t, db, 5) + + testCases := []struct { + name string + includeIndexes bool + expected *uint32 + }{ + { + name: "do not include indexes", + includeIndexes: false, + expected: nil, + }, + { + name: "include indexes", + includeIndexes: true, + expected: new(uint32), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + filter := TransferFilter{ + CriteriaSet: make([]*logdb.TransferCriteria, 0), + Range: nil, + Options: &logdb.Options{Limit: 5, IncludeIndexes: tc.includeIndexes}, + Order: logdb.DESC, + } + + res, statusCode := httpPost(t, ts.URL+"/transfers", filter) + assert.Equal(t, http.StatusOK, statusCode) + var tLogs []*FilteredTransfer + if err := json.Unmarshal(res, &tLogs); err != nil { + t.Fatal(err) + } + assert.Equal(t, http.StatusOK, statusCode) + assert.Equal(t, 5, len(tLogs)) + + for _, tLog := range tLogs { + assert.Equal(t, tc.expected, tLog.Meta.TxIndex) + assert.Equal(t, tc.expected, tLog.Meta.LogIndex) + } + }) + } +} + +func TestConvertTransfer(t *testing.T) { + transfer := &logdb.Transfer{ + Sender: thor.Address{0x01}, + Recipient: thor.Address{0x02}, + Amount: big.NewInt(100), + BlockID: thor.Bytes32{0x03}, + BlockNumber: 4, + BlockTime: 5, + TxID: thor.Bytes32{0x06}, + TxOrigin: thor.Address{0x07}, + ClauseIndex: 8, + TxIndex: 9, + Index: 10, + } + + expectedAmount := math.HexOrDecimal256(*big.NewInt(100)) + + result := convertTransfer(transfer, true) + + assert.Equal(t, transfer.Sender, result.Sender) + assert.Equal(t, transfer.Recipient, result.Recipient) + assert.Equal(t, &expectedAmount, result.Amount) + assert.Equal(t, transfer.BlockID, result.Meta.BlockID) + assert.Equal(t, transfer.BlockNumber, result.Meta.BlockNumber) + assert.Equal(t, transfer.BlockTime, result.Meta.BlockTimestamp) + assert.Equal(t, transfer.TxID, result.Meta.TxID) + assert.Equal(t, transfer.TxOrigin, result.Meta.TxOrigin) + assert.Equal(t, transfer.ClauseIndex, result.Meta.ClauseIndex) + assert.Equal(t, &transfer.TxIndex, result.Meta.TxIndex) + assert.Equal(t, &transfer.Index, result.Meta.LogIndex) +} + // Test functions func testTransferBadRequest(t *testing.T) { badBody := []byte{0x00, 0x01, 0x02} @@ -105,7 +186,7 @@ func testTransferBadRequest(t *testing.T) { } func testTransferWithEmptyDb(t *testing.T) { - emptyFilter := transfers.TransferFilter{ + emptyFilter := TransferFilter{ CriteriaSet: make([]*logdb.TransferCriteria, 0), Range: nil, Options: nil, @@ -113,7 +194,7 @@ func testTransferWithEmptyDb(t *testing.T) { } res, statusCode := httpPost(t, ts.URL+"/transfers", emptyFilter) - var tLogs []*transfers.FilteredTransfer + var tLogs []*FilteredTransfer if err := json.Unmarshal(res, &tLogs); err != nil { t.Fatal(err) } @@ -123,7 +204,7 @@ func testTransferWithEmptyDb(t *testing.T) { } func testTransferWithBlocks(t *testing.T, expectedBlocks int) { - emptyFilter := transfers.TransferFilter{ + emptyFilter := TransferFilter{ CriteriaSet: make([]*logdb.TransferCriteria, 0), Range: nil, Options: nil, @@ -131,7 +212,7 @@ func testTransferWithBlocks(t *testing.T, expectedBlocks int) { } res, statusCode := httpPost(t, ts.URL+"/transfers", emptyFilter) - var tLogs []*transfers.FilteredTransfer + var tLogs []*FilteredTransfer if err := json.Unmarshal(res, &tLogs); err != nil { t.Fatal(err) } @@ -177,7 +258,7 @@ func initTransferServer(t *testing.T, logDb *logdb.LogDB, limit uint64) { repo, _ := chain.NewRepository(muxDb, b) - transfers.New(repo, logDb, limit).Mount(router, "/transfers") + New(repo, logDb, limit).Mount(router, "/transfers") ts = httptest.NewServer(router) } diff --git a/api/transfers/types.go b/api/transfers/types.go index 29ad9b328..440c89c5d 100644 --- a/api/transfers/types.go +++ b/api/transfers/types.go @@ -19,6 +19,8 @@ type LogMeta struct { TxID thor.Bytes32 `json:"txID"` TxOrigin thor.Address `json:"txOrigin"` ClauseIndex uint32 `json:"clauseIndex"` + TxIndex *uint32 `json:"txIndex,omitempty"` + LogIndex *uint32 `json:"logIndex,omitempty"` } type FilteredTransfer struct { @@ -28,9 +30,9 @@ type FilteredTransfer struct { Meta LogMeta `json:"meta"` } -func convertTransfer(transfer *logdb.Transfer) *FilteredTransfer { +func convertTransfer(transfer *logdb.Transfer, addIndexes bool) *FilteredTransfer { v := math.HexOrDecimal256(*transfer.Amount) - return &FilteredTransfer{ + ft := &FilteredTransfer{ Sender: transfer.Sender, Recipient: transfer.Recipient, Amount: &v, @@ -43,6 +45,13 @@ func convertTransfer(transfer *logdb.Transfer) *FilteredTransfer { ClauseIndex: transfer.ClauseIndex, }, } + + if addIndexes { + ft.Meta.TxIndex = &transfer.TxIndex + ft.Meta.LogIndex = &transfer.Index + } + + return ft } type TransferFilter struct { diff --git a/logdb/types.go b/logdb/types.go index 7c05b9c59..8ed84bb1d 100644 --- a/logdb/types.go +++ b/logdb/types.go @@ -34,6 +34,7 @@ type Transfer struct { BlockID thor.Bytes32 BlockTime uint64 TxID thor.Bytes32 + TxIndex uint32 TxOrigin thor.Address ClauseIndex uint32 Sender thor.Address @@ -54,8 +55,9 @@ type Range struct { } type Options struct { - Offset uint64 - Limit uint64 + Offset uint64 + Limit uint64 + IncludeIndexes bool } type EventCriteria struct {