diff --git a/api/api.go b/api/api.go index 7067a329d..b0f346f8f 100644 --- a/api/api.go +++ b/api/api.go @@ -581,11 +581,6 @@ func (b *BlockChainAPI) GetLogs( return nil, err } - filter := logs.FilterCriteria{ - Addresses: criteria.Addresses, - Topics: criteria.Topics, - } - // if filter provided specific block ID if criteria.BlockHash != nil { // Check if the block exists, and return an error if not. @@ -598,7 +593,7 @@ func (b *BlockChainAPI) GetLogs( return []*types.Log{}, nil } - f, err := logs.NewIDFilter(*criteria.BlockHash, filter, b.blocks, b.receipts) + f, err := logs.NewIDFilter(criteria, b.blocks, b.receipts) if err != nil { return handleError[[]*types.Log](err, l, b.collector) } @@ -641,7 +636,7 @@ func (b *BlockChainAPI) GetLogs( to = latest } - f, err := logs.NewRangeFilter(from.Uint64(), to.Uint64(), filter, b.receipts) + f, err := logs.NewRangeFilter(from.Uint64(), to.Uint64(), criteria, b.receipts) if err != nil { return handleError[[]*types.Log](err, l, b.collector) } diff --git a/api/pull.go b/api/pull.go index 3eb29c596..b5df2b703 100644 --- a/api/pull.go +++ b/api/pull.go @@ -112,13 +112,13 @@ func newTransactionsFilter(expiry time.Duration, latestHeight uint64, fullTx boo // Criteria parameter filters the logs according to the criteria values. type logsFilter struct { *baseFilter - criteria *filters.FilterCriteria + criteria filters.FilterCriteria } func newLogsFilter( expiry time.Duration, latestHeight uint64, - criteria *filters.FilterCriteria, + criteria filters.FilterCriteria, ) *logsFilter { return &logsFilter{ newBaseFilter(expiry, latestHeight), @@ -272,7 +272,7 @@ func (api *PullAPI) NewFilter(ctx context.Context, criteria filters.FilterCriter // todo we should check for max range of from-to heights } - f := newLogsFilter(api.config.FilterExpiry, latest, &criteria) + f := newLogsFilter(api.config.FilterExpiry, latest, criteria) api.logger.Debug(). Str("id", string(f.id())). @@ -483,10 +483,6 @@ func (api *PullAPI) getTransactions(latestHeight uint64, filter *transactionsFil func (api *PullAPI) getLogs(latestHeight uint64, filter *logsFilter) (any, error) { nextHeight := filter.next() - criteria := logs.FilterCriteria{ - Addresses: filter.criteria.Addresses, - Topics: filter.criteria.Topics, - } to := filter.criteria.ToBlock // we use latest as default for end range @@ -506,7 +502,7 @@ func (api *PullAPI) getLogs(latestHeight uint64, filter *logsFilter) (any, error return []*gethTypes.Log{}, nil } - f, err := logs.NewRangeFilter(start, end, criteria, api.receipts) + f, err := logs.NewRangeFilter(start, end, filter.criteria, api.receipts) if err != nil { return nil, fmt.Errorf("could not create range filter from %d to %d: %w", start, end, err) } diff --git a/api/stream.go b/api/stream.go index d2093980a..439b97f9c 100644 --- a/api/stream.go +++ b/api/stream.go @@ -92,11 +92,6 @@ func (s *StreamAPI) NewPendingTransactions(ctx context.Context, fullTx *bool) (* // Logs creates a subscription that fires for all new log that match the given filter criteria. func (s *StreamAPI) Logs(ctx context.Context, criteria filters.FilterCriteria) (*rpc.Subscription, error) { - logCriteria, err := logs.NewFilterCriteria(criteria.Addresses, criteria.Topics) - if err != nil { - return nil, fmt.Errorf("failed to create log subscription filter: %w", err) - } - return newSubscription( ctx, s.logger, @@ -106,7 +101,7 @@ func (s *StreamAPI) Logs(ctx context.Context, criteria filters.FilterCriteria) ( for _, log := range allLogs { // todo we could optimize this matching for cases where we have multiple subscriptions // using the same filter criteria, we could only filter once and stream to all subscribers - if !logs.ExactMatch(log, logCriteria) { + if !logs.ExactMatch(log, criteria) { continue } diff --git a/services/logs/filter.go b/services/logs/filter.go index 084e4a908..53c13964d 100644 --- a/services/logs/filter.go +++ b/services/logs/filter.go @@ -8,58 +8,22 @@ import ( "github.com/onflow/flow-evm-gateway/storage" "github.com/onflow/go-ethereum/common" gethTypes "github.com/onflow/go-ethereum/core/types" + "github.com/onflow/go-ethereum/eth/filters" ) -// The maximum number of topic criteria allowed -const maxTopics = 4 - -// The maximum number of addresses allowed -const maxAddresses = 6 - -// FilterCriteria for log filtering. -// Address of the contract emitting the log. -// Topics that match the log topics, following the format: -// [] “anything” -// [A] “A in first position (and anything after)” -// [null, B] “anything in first position AND B in second position (and anything after)” -// [A, B] “A in first position AND B in second position (and anything after)” -// [[A, B], [A, B]] “(A OR B) in first position AND (A OR B) in second position (and anything after)” -type FilterCriteria struct { - Addresses []common.Address - Topics [][]common.Hash -} - -func NewFilterCriteria(addresses []common.Address, topics [][]common.Hash) (*FilterCriteria, error) { - if len(topics) > maxTopics { - return nil, fmt.Errorf("max topics exceeded, only %d allowed, got %d", maxTopics, len(topics)) - } - if len(addresses) > maxAddresses { - return nil, fmt.Errorf("max addresses exceeded, only %d allowed, got %d", maxAddresses, len(addresses)) - } - - return &FilterCriteria{ - Addresses: addresses, - Topics: topics, - }, nil -} - // RangeFilter matches all the indexed logs within the range defined as // start and end block height. The start must be strictly smaller or equal than end value. type RangeFilter struct { start, end uint64 - criteria *FilterCriteria + criteria filters.FilterCriteria receipts storage.ReceiptIndexer } func NewRangeFilter( start, end uint64, - criteria FilterCriteria, + criteria filters.FilterCriteria, receipts storage.ReceiptIndexer, ) (*RangeFilter, error) { - if len(criteria.Topics) > maxTopics { - return nil, fmt.Errorf("max topics exceeded, only %d allowed, got %d", maxTopics, len(criteria.Topics)) - } - // make sure that beginning number is not bigger than end if start > end { return nil, fmt.Errorf( @@ -73,7 +37,7 @@ func NewRangeFilter( return &RangeFilter{ start: start, end: end, - criteria: &criteria, + criteria: criteria, receipts: receipts, }, nil } @@ -126,24 +90,23 @@ func (r *RangeFilter) Match() ([]*gethTypes.Log, error) { // by the provided block ID. type IDFilter struct { id common.Hash - criteria *FilterCriteria + criteria filters.FilterCriteria blocks storage.BlockIndexer receipts storage.ReceiptIndexer } func NewIDFilter( - id common.Hash, - criteria FilterCriteria, + criteria filters.FilterCriteria, blocks storage.BlockIndexer, receipts storage.ReceiptIndexer, ) (*IDFilter, error) { - if len(criteria.Topics) > maxTopics { - return nil, fmt.Errorf("max topics exceeded, only %d allowed, got %d", maxTopics, len(criteria.Topics)) + if criteria.BlockHash == nil { + return nil, fmt.Errorf("filter criteria should have a non-nil block hash") } return &IDFilter{ - id: id, - criteria: &criteria, + id: *criteria.BlockHash, + criteria: criteria, blocks: blocks, receipts: receipts, }, nil @@ -173,7 +136,7 @@ func (i *IDFilter) Match() ([]*gethTypes.Log, error) { } // ExactMatch checks the topic and address values of the log match the filter exactly. -func ExactMatch(log *gethTypes.Log, criteria *FilterCriteria) bool { +func ExactMatch(log *gethTypes.Log, criteria filters.FilterCriteria) bool { // check criteria doesn't have more topics than the log, but it can have less due to wildcards if len(criteria.Topics) > len(log.Topics) { return false @@ -203,7 +166,7 @@ func ExactMatch(log *gethTypes.Log, criteria *FilterCriteria) bool { // If true is returned we should further check against the exactMatch to really make sure the log is matched. // // source: https://github.com/ethereum/go-ethereum/blob/8d1db1601d3a9e4fd067558a49db6f0b879c9b48/eth/filters/filter.go#L395 -func bloomMatch(bloom gethTypes.Bloom, criteria *FilterCriteria) bool { +func bloomMatch(bloom gethTypes.Bloom, criteria filters.FilterCriteria) bool { if len(criteria.Addresses) > 0 { var included bool for _, addr := range criteria.Addresses { diff --git a/services/logs/filter_test.go b/services/logs/filter_test.go index 6da9df642..01fcb7481 100644 --- a/services/logs/filter_test.go +++ b/services/logs/filter_test.go @@ -7,7 +7,7 @@ import ( "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/go-ethereum/common" gethTypes "github.com/onflow/go-ethereum/core/types" - "github.com/stretchr/testify/assert" + "github.com/onflow/go-ethereum/eth/filters" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -178,40 +178,40 @@ func toGethReceipt(sr *models.Receipt) *gethTypes.Receipt { func TestIDFilter(t *testing.T) { logs := receipts[0].Logs + blockHash := mustHash(blocks[0]) tests := []struct { desc string - id common.Hash expectLogs []*gethTypes.Log - criteria FilterCriteria + criteria filters.FilterCriteria }{{ desc: "wildcard all logs", - id: mustHash(blocks[0]), - criteria: FilterCriteria{ + criteria: filters.FilterCriteria{ + BlockHash: &blockHash, Addresses: []common.Address{}, Topics: [][]common.Hash{}, }, expectLogs: logs[:], // block 0 has 3 logs in total }, { desc: "single topic no address match single log", - id: mustHash(blocks[0]), - criteria: FilterCriteria{ + criteria: filters.FilterCriteria{ + BlockHash: &blockHash, Addresses: []common.Address{}, Topics: [][]common.Hash{logs[0].Topics[:1]}, }, expectLogs: logs[:1], }, { desc: "single out of order topic no address match no logs", - id: mustHash(blocks[0]), - criteria: FilterCriteria{ + criteria: filters.FilterCriteria{ + BlockHash: &blockHash, Addresses: []common.Address{}, Topics: [][]common.Hash{logs[0].Topics[1:]}, }, expectLogs: []*gethTypes.Log{}, }, { desc: "single topic with first position wildcard match single log", - id: mustHash(blocks[0]), - criteria: FilterCriteria{ + criteria: filters.FilterCriteria{ + BlockHash: &blockHash, Addresses: []common.Address{}, Topics: [][]common.Hash{ {}, @@ -221,8 +221,8 @@ func TestIDFilter(t *testing.T) { expectLogs: logs[:1], }, { desc: "single topic with second position wildcard match single log", - id: mustHash(blocks[0]), - criteria: FilterCriteria{ + criteria: filters.FilterCriteria{ + BlockHash: &blockHash, Addresses: []common.Address{}, Topics: [][]common.Hash{ {logs[0].Topics[0]}, @@ -232,40 +232,40 @@ func TestIDFilter(t *testing.T) { expectLogs: logs[:1], }, { desc: "single topic, single address match single log", - id: mustHash(blocks[0]), - criteria: FilterCriteria{ + criteria: filters.FilterCriteria{ + BlockHash: &blockHash, Addresses: []common.Address{logs[0].Address}, Topics: [][]common.Hash{logs[0].Topics[:1]}, }, expectLogs: logs[:1], }, { desc: "single address no topic match two logs", - id: mustHash(blocks[0]), - criteria: FilterCriteria{ + criteria: filters.FilterCriteria{ + BlockHash: &blockHash, Addresses: []common.Address{logs[0].Address}, Topics: [][]common.Hash{}, }, expectLogs: logs[:2], }, { desc: "single address, both topics match single log", - id: mustHash(blocks[0]), - criteria: FilterCriteria{ + criteria: filters.FilterCriteria{ + BlockHash: &blockHash, Addresses: []common.Address{logs[0].Address}, Topics: [][]common.Hash{logs[0].Topics}, }, expectLogs: logs[:1], }, { desc: "invalid topic match no logs", - id: mustHash(blocks[0]), - criteria: FilterCriteria{ - Topics: [][]common.Hash{{common.HexToHash("123")}}, + criteria: filters.FilterCriteria{ + BlockHash: &blockHash, + Topics: [][]common.Hash{{common.HexToHash("123")}}, }, expectLogs: []*gethTypes.Log{}, }} for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - filter, err := NewIDFilter(tt.id, tt.criteria, blockStorage(), receiptStorage()) + filter, err := NewIDFilter(tt.criteria, blockStorage(), receiptStorage()) require.NoError(t, err) matchedLogs, err := filter.Match() @@ -274,30 +274,6 @@ func TestIDFilter(t *testing.T) { require.Equal(t, tt.expectLogs, matchedLogs) }) } - - t.Run("with topics count exceeding limit", func(t *testing.T) { - _, err := NewIDFilter( - common.HexToHash("123"), - FilterCriteria{ - Topics: [][]common.Hash{ - {common.HexToHash("123")}, - {common.HexToHash("456")}, - {common.HexToHash("789")}, - {common.HexToHash("101")}, - {common.HexToHash("120")}, - }, - }, - blockStorage(), - receiptStorage(), - ) - - require.Error(t, err) - assert.ErrorContains( - t, - err, - "max topics exceeded, only 4 allowed", - ) - }) } func TestRangeFilter(t *testing.T) { @@ -307,12 +283,12 @@ func TestRangeFilter(t *testing.T) { desc string start, end uint64 expectLogs []*gethTypes.Log - criteria FilterCriteria + criteria filters.FilterCriteria }{{ desc: "single topic, single address, single block match single log", start: 0, end: 1, - criteria: FilterCriteria{ + criteria: filters.FilterCriteria{ Addresses: []common.Address{logs[0][0].Address}, Topics: [][]common.Hash{logs[0][0].Topics[:1]}, }, @@ -321,7 +297,7 @@ func TestRangeFilter(t *testing.T) { desc: "single topic, single address, all blocks match multiple logs", start: 0, end: 4, - criteria: FilterCriteria{ + criteria: filters.FilterCriteria{ Addresses: []common.Address{logs[0][0].Address}, Topics: [][]common.Hash{logs[0][0].Topics[:1]}, }, @@ -330,7 +306,7 @@ func TestRangeFilter(t *testing.T) { desc: "single topic, single address, subset of blocks match single log", start: 2, end: 4, - criteria: FilterCriteria{ + criteria: filters.FilterCriteria{ Addresses: []common.Address{logs[0][0].Address}, Topics: [][]common.Hash{logs[0][0].Topics[:1]}, }, @@ -339,7 +315,7 @@ func TestRangeFilter(t *testing.T) { desc: "single address, all blocks match multiple logs", start: 0, end: 4, - criteria: FilterCriteria{ + criteria: filters.FilterCriteria{ Addresses: []common.Address{logs[0][0].Address}, }, expectLogs: []*gethTypes.Log{logs[0][0], logs[0][1], logs[1][0], logs[3][1]}, @@ -347,7 +323,7 @@ func TestRangeFilter(t *testing.T) { desc: "invalid address, all blocks no match", start: 0, end: 4, - criteria: FilterCriteria{ + criteria: filters.FilterCriteria{ Addresses: []common.Address{common.HexToAddress("0x123")}, }, expectLogs: nil, @@ -355,7 +331,7 @@ func TestRangeFilter(t *testing.T) { desc: "single address, non-existing range no match", start: 5, end: 10, - criteria: FilterCriteria{ + criteria: filters.FilterCriteria{ Addresses: []common.Address{logs[0][0].Address}, }, expectLogs: nil, @@ -372,30 +348,6 @@ func TestRangeFilter(t *testing.T) { require.Equal(t, tt.expectLogs, matchedLogs) }) } - - t.Run("with topics count exceeding limit", func(t *testing.T) { - _, err := NewRangeFilter( - 0, - 4, - FilterCriteria{ - Topics: [][]common.Hash{ - {common.HexToHash("123")}, - {common.HexToHash("456")}, - {common.HexToHash("789")}, - {common.HexToHash("101")}, - {common.HexToHash("120")}, - }, - }, - receiptStorage(), - ) - - require.Error(t, err) - assert.ErrorContains( - t, - err, - "max topics exceeded, only 4 allowed", - ) - }) } func TestStreamFilter(t *testing.T) { diff --git a/tests/helpers.go b/tests/helpers.go index 23971ba63..cc47ba046 100644 --- a/tests/helpers.go +++ b/tests/helpers.go @@ -198,7 +198,7 @@ func servicesSetup(t *testing.T) (emulator.Emulator, func()) { // and will report failure or success of the test. func executeTest(t *testing.T, testFile string) { command := fmt.Sprintf( - "./web3js/node_modules/.bin/mocha ./web3js/%s.js --timeout 150s", + "./web3js/node_modules/.bin/mocha ./web3js/%s.js --timeout 150s --exit", testFile, ) parts := strings.Fields(command) diff --git a/tests/web3js/eth_filter_endpoints_test.js b/tests/web3js/eth_filter_endpoints_test.js index 17707acdf..350b4d46a 100644 --- a/tests/web3js/eth_filter_endpoints_test.js +++ b/tests/web3js/eth_filter_endpoints_test.js @@ -13,6 +13,118 @@ before(async () => { assert.equal(deployed.receipt.status, conf.successStatus) }) +describe('eth_newFilter', async () => { + it('should validate max number of topics', async () => { + let latestBlockNumber = await web3.eth.getBlockNumber() + let latestBlock = await web3.eth.getBlock(latestBlockNumber) + + let blockRangeFilter = { + fromBlock: web3.utils.numberToHex(latestBlock.number), + toBlock: web3.utils.numberToHex(latestBlock.number), + address: ['0x0000000071727de22e5e9d8baf0edac6f37da032'], + topics: [ + '0x76efea95e5da1fa661f235b2921ae1d89b99e457ec73fb88e34a1d150f95c64b', + '0x000000000000000000000000facf71692421039876a5bb4f10ef7a439d8ef61e', + '0x000000000000000000000000000000000000000000000000000000000000000a', + '0x0000000000000000000000000000000000000000000000000000000000000190', + '0x000000000000000000000000000000000000000000000000000000000000001a', + ] + } + + let response = await helpers.callRPCMethod('eth_newFilter', [blockRangeFilter]) + assert.equal(response.status, 200) + assert.isDefined(response.body.error) + assert.equal(response.body.error.message, 'invalid argument 0: exceed max topics') + + let blockHashFilter = { + blockHash: latestBlock.hash, + address: ['0x0000000071727de22e5e9d8baf0edac6f37da032'], + topics: [ + '0x76efea95e5da1fa661f235b2921ae1d89b99e457ec73fb88e34a1d150f95c64b', + '0x000000000000000000000000facf71692421039876a5bb4f10ef7a439d8ef61e', + '0x000000000000000000000000000000000000000000000000000000000000000a', + '0x0000000000000000000000000000000000000000000000000000000000000190', + '0x000000000000000000000000000000000000000000000000000000000000001a', + ] + } + + response = await helpers.callRPCMethod('eth_newFilter', [blockHashFilter]) + assert.equal(response.status, 200) + assert.isDefined(response.body.error) + assert.equal(response.body.error.message, 'invalid argument 0: exceed max topics') + }) + + it('should validate max number of addresses', async () => { + let latestBlockNumber = await web3.eth.getBlockNumber() + let latestBlock = await web3.eth.getBlock(latestBlockNumber) + + let addresses = [] + for (let i = 0; i <= 1000; i++) { + addresses.push( + '0x0000000071727de22e5e9d8baf0edac6f37da032' + ) + } + + let blockRangeFilter = { + fromBlock: web3.utils.numberToHex(latestBlock.number), + toBlock: web3.utils.numberToHex(latestBlock.number), + address: addresses, + topics: [] + } + + let response = await helpers.callRPCMethod('eth_newFilter', [blockRangeFilter]) + assert.equal(response.status, 200) + assert.isDefined(response.body.error) + assert.equal(response.body.error.message, 'invalid argument 0: exceed max addresses') + + let blockHashFilter = { + blockHash: latestBlock.hash, + address: addresses, + topics: [] + } + + response = await helpers.callRPCMethod('eth_newFilter', [blockHashFilter]) + assert.equal(response.status, 200) + assert.isDefined(response.body.error) + assert.equal(response.body.error.message, 'invalid argument 0: exceed max addresses') + }) + + it('should validate max number of sub-topics', async () => { + let latestBlockNumber = await web3.eth.getBlockNumber() + let latestBlock = await web3.eth.getBlock(latestBlockNumber) + + let subTopics = [] + for (let i = 0; i <= 1000; i++) { + subTopics.push( + '0x76efea95e5da1fa661f235b2921ae1d89b99e457ec73fb88e34a1d150f95c64b' + ) + } + + let blockRangeFilter = { + fromBlock: web3.utils.numberToHex(latestBlock.number), + toBlock: web3.utils.numberToHex(latestBlock.number), + address: ['0x0000000071727de22e5e9d8baf0edac6f37da032'], + topics: [subTopics] + } + + let response = await helpers.callRPCMethod('eth_newFilter', [blockRangeFilter]) + assert.equal(response.status, 200) + assert.isDefined(response.body.error) + assert.equal(response.body.error.message, 'invalid argument 0: exceed max topics') + + let blockHashFilter = { + blockHash: latestBlock.hash, + address: ['0x0000000071727de22e5e9d8baf0edac6f37da032'], + topics: [subTopics] + } + + response = await helpers.callRPCMethod('eth_newFilter', [blockHashFilter]) + assert.equal(response.status, 200) + assert.isDefined(response.body.error) + assert.equal(response.body.error.message, 'invalid argument 0: exceed max topics') + }) +}) + describe('eth_uninstallFilter', async () => { it('should return false for missing filter', async () => { let response = await helpers.callRPCMethod('eth_uninstallFilter', ['0xffa1']) diff --git a/tests/web3js/eth_logs_filtering_test.js b/tests/web3js/eth_logs_filtering_test.js index afa16f245..b1ac205cd 100644 --- a/tests/web3js/eth_logs_filtering_test.js +++ b/tests/web3js/eth_logs_filtering_test.js @@ -190,3 +190,113 @@ it('emit logs and retrieve them using different filters', async () => { ] ) }) + +it('validates max number of allowed topics', async () => { + let latestBlockNumber = await web3.eth.getBlockNumber() + let latestBlock = await web3.eth.getBlock(latestBlockNumber) + + let blockRangeFilter = { + fromBlock: web3.utils.numberToHex(latestBlock.number), + toBlock: web3.utils.numberToHex(latestBlock.number), + address: ['0x0000000071727de22e5e9d8baf0edac6f37da032'], + topics: [ + '0x76efea95e5da1fa661f235b2921ae1d89b99e457ec73fb88e34a1d150f95c64b', + '0x000000000000000000000000facf71692421039876a5bb4f10ef7a439d8ef61e', + '0x000000000000000000000000000000000000000000000000000000000000000a', + '0x0000000000000000000000000000000000000000000000000000000000000190', + '0x000000000000000000000000000000000000000000000000000000000000001a', + ] + } + + let response = await helpers.callRPCMethod('eth_getLogs', [blockRangeFilter]) + assert.equal(response.status, 200) + assert.isDefined(response.body.error) + assert.equal(response.body.error.message, 'invalid argument 0: exceed max topics') + + let blockHashFilter = { + blockHash: latestBlock.hash, + address: ['0x0000000071727de22e5e9d8baf0edac6f37da032'], + topics: [ + '0x76efea95e5da1fa661f235b2921ae1d89b99e457ec73fb88e34a1d150f95c64b', + '0x000000000000000000000000facf71692421039876a5bb4f10ef7a439d8ef61e', + '0x000000000000000000000000000000000000000000000000000000000000000a', + '0x0000000000000000000000000000000000000000000000000000000000000190', + '0x000000000000000000000000000000000000000000000000000000000000001a', + ] + } + + response = await helpers.callRPCMethod('eth_getLogs', [blockHashFilter]) + assert.equal(response.status, 200) + assert.isDefined(response.body.error) + assert.equal(response.body.error.message, 'invalid argument 0: exceed max topics') +}) + +it('validates max number of allowed addresses', async () => { + let latestBlockNumber = await web3.eth.getBlockNumber() + let latestBlock = await web3.eth.getBlock(latestBlockNumber) + + let addresses = [] + for (let i = 0; i <= 1000; i++) { + addresses.push( + '0x0000000071727de22e5e9d8baf0edac6f37da032' + ) + } + + let blockRangeFilter = { + fromBlock: web3.utils.numberToHex(latestBlock.number), + toBlock: web3.utils.numberToHex(latestBlock.number), + address: addresses, + topics: [] + } + + let response = await helpers.callRPCMethod('eth_getLogs', [blockRangeFilter]) + assert.equal(response.status, 200) + assert.isDefined(response.body.error) + assert.equal(response.body.error.message, 'invalid argument 0: exceed max addresses') + + let blockHashFilter = { + blockHash: latestBlock.hash, + address: addresses, + topics: [] + } + + response = await helpers.callRPCMethod('eth_getLogs', [blockHashFilter]) + assert.equal(response.status, 200) + assert.isDefined(response.body.error) + assert.equal(response.body.error.message, 'invalid argument 0: exceed max addresses') +}) + +it('validates max number of allowed sub-topics', async () => { + let latestBlockNumber = await web3.eth.getBlockNumber() + let latestBlock = await web3.eth.getBlock(latestBlockNumber) + + let subTopics = [] + for (let i = 0; i <= 1000; i++) { + subTopics.push( + '0x76efea95e5da1fa661f235b2921ae1d89b99e457ec73fb88e34a1d150f95c64b' + ) + } + + let blockRangeFilter = { + fromBlock: web3.utils.numberToHex(latestBlock.number), + toBlock: web3.utils.numberToHex(latestBlock.number), + address: ['0x0000000071727de22e5e9d8baf0edac6f37da032'], + topics: [subTopics] + } + + let response = await helpers.callRPCMethod('eth_getLogs', [blockRangeFilter]) + assert.equal(response.status, 200) + assert.isDefined(response.body.error) + assert.equal(response.body.error.message, 'invalid argument 0: exceed max topics') + + let blockHashFilter = { + blockHash: latestBlock.hash, + address: ['0x0000000071727de22e5e9d8baf0edac6f37da032'], + topics: [subTopics] + } + + response = await helpers.callRPCMethod('eth_getLogs', [blockHashFilter]) + assert.equal(response.status, 200) + assert.isDefined(response.body.error) + assert.equal(response.body.error.message, 'invalid argument 0: exceed max topics') +}) diff --git a/tests/web3js/eth_streaming_filters_test.js b/tests/web3js/eth_streaming_filters_test.js index c19f231cf..582a17bd0 100644 --- a/tests/web3js/eth_streaming_filters_test.js +++ b/tests/web3js/eth_streaming_filters_test.js @@ -3,6 +3,7 @@ const { Web3 } = require("web3"); const conf = require("./config"); const { assert } = require("chai"); const storageABI = require('../fixtures/storageABI.json') +const web3 = conf.web3 async function assertFilterLogs(subscription, expectedLogs) { let allLogs = [] @@ -23,8 +24,6 @@ async function assertFilterLogs(subscription, expectedLogs) { await new Promise(res => setTimeout(() => res(), 1000)) assert.equal(allLogs.length, expectedLogs.length) - console.log("## unsubscribe", subscription.id) - subscription.unsubscribe() // after we receive all logs, we make sure each received @@ -172,5 +171,158 @@ it('streaming of logs using filters', async () => { // make sure we can also get logs streamed after the transactions were executed (historic) await assertFilterLogs(await rawSubscribe({ address: contractAddress, fromBlock: "0x0" }), testValues) - process.exit(0) + ws.currentProvider.disconnect() +}) + +it('should validate max number of topics', async () => { + let latestBlockNumber = await web3.eth.getBlockNumber() + let latestBlock = await web3.eth.getBlock(latestBlockNumber) + + let blockRangeFilter = { + fromBlock: web3.utils.numberToHex(latestBlock.number), + toBlock: web3.utils.numberToHex(latestBlock.number), + address: ['0x0000000071727de22e5e9d8baf0edac6f37da032'], + topics: [ + '0x76efea95e5da1fa661f235b2921ae1d89b99e457ec73fb88e34a1d150f95c64b', + '0x000000000000000000000000facf71692421039876a5bb4f10ef7a439d8ef61e', + '0x000000000000000000000000000000000000000000000000000000000000000a', + '0x0000000000000000000000000000000000000000000000000000000000000190', + '0x000000000000000000000000000000000000000000000000000000000000001a', + ] + } + + let ws = new Web3('ws://127.0.0.1:8545') + + try { + await ws.eth.subscribe('logs', blockRangeFilter) + assert.fail('should have received response error') + } catch (err) { + assert.equal( + err.innerError.message, + 'invalid argument 1: exceed max topics' + ) + } + + let blockHashFilter = { + blockHash: latestBlock.hash, + address: ['0x0000000071727de22e5e9d8baf0edac6f37da032'], + topics: [ + '0x76efea95e5da1fa661f235b2921ae1d89b99e457ec73fb88e34a1d150f95c64b', + '0x000000000000000000000000facf71692421039876a5bb4f10ef7a439d8ef61e', + '0x000000000000000000000000000000000000000000000000000000000000000a', + '0x0000000000000000000000000000000000000000000000000000000000000190', + '0x000000000000000000000000000000000000000000000000000000000000001a', + ] + } + + try { + await ws.eth.subscribe('logs', blockHashFilter) + assert.fail('should have received response error') + } catch (err) { + assert.equal( + err.innerError.message, + 'invalid argument 1: exceed max topics' + ) + } + + ws.currentProvider.disconnect() +}) + +it('should validate max number of addresses', async () => { + let latestBlockNumber = await web3.eth.getBlockNumber() + let latestBlock = await web3.eth.getBlock(latestBlockNumber) + + let addresses = [] + for (let i = 0; i <= 1000; i++) { + addresses.push( + '0x0000000071727de22e5e9d8baf0edac6f37da032' + ) + } + + let blockRangeFilter = { + fromBlock: web3.utils.numberToHex(latestBlock.number), + toBlock: web3.utils.numberToHex(latestBlock.number), + address: addresses, + topics: [] + } + + let ws = new Web3('ws://127.0.0.1:8545') + + try { + await ws.eth.subscribe('logs', blockRangeFilter) + assert.fail('should have received response error') + } catch (err) { + assert.equal( + err.innerError.message, + 'invalid argument 1: exceed max addresses' + ) + } + + let blockHashFilter = { + blockHash: latestBlock.hash, + address: addresses, + topics: [] + } + + try { + await ws.eth.subscribe('logs', blockHashFilter) + assert.fail('should have received response error') + } catch (err) { + assert.equal( + err.innerError.message, + 'invalid argument 1: exceed max addresses' + ) + } + + ws.currentProvider.disconnect() +}) + + +it('should validate max number of sub-topics', async () => { + let latestBlockNumber = await web3.eth.getBlockNumber() + let latestBlock = await web3.eth.getBlock(latestBlockNumber) + + let subTopics = [] + for (let i = 0; i <= 1000; i++) { + subTopics.push( + '0x76efea95e5da1fa661f235b2921ae1d89b99e457ec73fb88e34a1d150f95c64b' + ) + } + + let blockRangeFilter = { + fromBlock: web3.utils.numberToHex(latestBlock.number), + toBlock: web3.utils.numberToHex(latestBlock.number), + address: ['0x0000000071727de22e5e9d8baf0edac6f37da032'], + topics: [subTopics] + } + + let ws = new Web3('ws://127.0.0.1:8545') + + try { + await ws.eth.subscribe('logs', blockRangeFilter) + assert.fail('should have received response error') + } catch (err) { + assert.equal( + err.innerError.message, + 'invalid argument 1: exceed max topics' + ) + } + + let blockHashFilter = { + blockHash: latestBlock.hash, + address: ['0x0000000071727de22e5e9d8baf0edac6f37da032'], + topics: [subTopics] + } + + try { + await ws.eth.subscribe('logs', blockHashFilter) + assert.fail('should have received response error') + } catch (err) { + assert.equal( + err.innerError.message, + 'invalid argument 1: exceed max topics' + ) + } + + ws.currentProvider.disconnect() })