You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

627 lines
15 KiB

7 years ago
abci: Refactor tagging events using list of lists (#3643) ## PR This PR introduces a fundamental breaking change to the structure of ABCI response and tx tags and the way they're processed. Namely, the SDK can support more complex and aggregated events for distribution and slashing. In addition, block responses can include duplicate keys in events. Implement new Event type. An event has a type and a list of KV pairs (ie. list-of-lists). Typical events may look like: "rewards": [{"amount": "5000uatom", "validator": "...", "recipient": "..."}] "sender": [{"address": "...", "balance": "100uatom"}] The events are indexed by {even.type}.{even.attribute[i].key}/.... In this case a client would subscribe or query for rewards.recipient='...' ABCI response types and related types now include Events []Event instead of Tags []cmn.KVPair. PubSub logic now publishes/matches against map[string][]string instead of map[string]string to support duplicate keys in response events (from #1385). A match is successful if the value is found in the slice of strings. closes: #1859 closes: #2905 ## Commits: * Implement Event ABCI type and updates responses to use events * Update messages_test.go * Update kvstore.go * Update event_bus.go * Update subscription.go * Update pubsub.go * Update kvstore.go * Update query logic to handle slice of strings in events * Update Empty#Matches and unit tests * Update pubsub logic * Update EventBus#Publish * Update kv tx indexer * Update godocs * Update ResultEvent to use slice of strings; update RPC * Update more tests * Update abci.md * Check for key in validateAndStringifyEvents * Fix KV indexer to skip empty keys * Fix linting errors * Update CHANGELOG_PENDING.md * Update docs/spec/abci/abci.md Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * Update abci/types/types.proto Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update docs/spec/abci/abci.md Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update libs/pubsub/query/query.go Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update match function to match if ANY value matches * Implement TestSubscribeDuplicateKeys * Update TestMatches to include multi-key test cases * Update events.go * Update Query interface godoc * Update match godoc * Add godoc for matchValue * DRY-up tx indexing * Return error from PublishWithEvents in EventBus#Publish * Update PublishEventNewBlockHeader to return an error * Fix build * Update events doc in ABCI * Update ABCI events godoc * Implement TestEventBusPublishEventTxDuplicateKeys * Update TestSubscribeDuplicateKeys to be table-driven * Remove mod file * Remove markdown from events godoc * Implement TestTxSearchDeprecatedIndexing test
6 years ago
abci: Refactor tagging events using list of lists (#3643) ## PR This PR introduces a fundamental breaking change to the structure of ABCI response and tx tags and the way they're processed. Namely, the SDK can support more complex and aggregated events for distribution and slashing. In addition, block responses can include duplicate keys in events. Implement new Event type. An event has a type and a list of KV pairs (ie. list-of-lists). Typical events may look like: "rewards": [{"amount": "5000uatom", "validator": "...", "recipient": "..."}] "sender": [{"address": "...", "balance": "100uatom"}] The events are indexed by {even.type}.{even.attribute[i].key}/.... In this case a client would subscribe or query for rewards.recipient='...' ABCI response types and related types now include Events []Event instead of Tags []cmn.KVPair. PubSub logic now publishes/matches against map[string][]string instead of map[string]string to support duplicate keys in response events (from #1385). A match is successful if the value is found in the slice of strings. closes: #1859 closes: #2905 ## Commits: * Implement Event ABCI type and updates responses to use events * Update messages_test.go * Update kvstore.go * Update event_bus.go * Update subscription.go * Update pubsub.go * Update kvstore.go * Update query logic to handle slice of strings in events * Update Empty#Matches and unit tests * Update pubsub logic * Update EventBus#Publish * Update kv tx indexer * Update godocs * Update ResultEvent to use slice of strings; update RPC * Update more tests * Update abci.md * Check for key in validateAndStringifyEvents * Fix KV indexer to skip empty keys * Fix linting errors * Update CHANGELOG_PENDING.md * Update docs/spec/abci/abci.md Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * Update abci/types/types.proto Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update docs/spec/abci/abci.md Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update libs/pubsub/query/query.go Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update match function to match if ANY value matches * Implement TestSubscribeDuplicateKeys * Update TestMatches to include multi-key test cases * Update events.go * Update Query interface godoc * Update match godoc * Add godoc for matchValue * DRY-up tx indexing * Return error from PublishWithEvents in EventBus#Publish * Update PublishEventNewBlockHeader to return an error * Fix build * Update events doc in ABCI * Update ABCI events godoc * Implement TestEventBusPublishEventTxDuplicateKeys * Update TestSubscribeDuplicateKeys to be table-driven * Remove mod file * Remove markdown from events godoc * Implement TestTxSearchDeprecatedIndexing test
6 years ago
7 years ago
7 years ago
abci: Refactor tagging events using list of lists (#3643) ## PR This PR introduces a fundamental breaking change to the structure of ABCI response and tx tags and the way they're processed. Namely, the SDK can support more complex and aggregated events for distribution and slashing. In addition, block responses can include duplicate keys in events. Implement new Event type. An event has a type and a list of KV pairs (ie. list-of-lists). Typical events may look like: "rewards": [{"amount": "5000uatom", "validator": "...", "recipient": "..."}] "sender": [{"address": "...", "balance": "100uatom"}] The events are indexed by {even.type}.{even.attribute[i].key}/.... In this case a client would subscribe or query for rewards.recipient='...' ABCI response types and related types now include Events []Event instead of Tags []cmn.KVPair. PubSub logic now publishes/matches against map[string][]string instead of map[string]string to support duplicate keys in response events (from #1385). A match is successful if the value is found in the slice of strings. closes: #1859 closes: #2905 ## Commits: * Implement Event ABCI type and updates responses to use events * Update messages_test.go * Update kvstore.go * Update event_bus.go * Update subscription.go * Update pubsub.go * Update kvstore.go * Update query logic to handle slice of strings in events * Update Empty#Matches and unit tests * Update pubsub logic * Update EventBus#Publish * Update kv tx indexer * Update godocs * Update ResultEvent to use slice of strings; update RPC * Update more tests * Update abci.md * Check for key in validateAndStringifyEvents * Fix KV indexer to skip empty keys * Fix linting errors * Update CHANGELOG_PENDING.md * Update docs/spec/abci/abci.md Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * Update abci/types/types.proto Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update docs/spec/abci/abci.md Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update libs/pubsub/query/query.go Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update match function to match if ANY value matches * Implement TestSubscribeDuplicateKeys * Update TestMatches to include multi-key test cases * Update events.go * Update Query interface godoc * Update match godoc * Add godoc for matchValue * DRY-up tx indexing * Return error from PublishWithEvents in EventBus#Publish * Update PublishEventNewBlockHeader to return an error * Fix build * Update events doc in ABCI * Update ABCI events godoc * Implement TestEventBusPublishEventTxDuplicateKeys * Update TestSubscribeDuplicateKeys to be table-driven * Remove mod file * Remove markdown from events godoc * Implement TestTxSearchDeprecatedIndexing test
6 years ago
abci: Refactor tagging events using list of lists (#3643) ## PR This PR introduces a fundamental breaking change to the structure of ABCI response and tx tags and the way they're processed. Namely, the SDK can support more complex and aggregated events for distribution and slashing. In addition, block responses can include duplicate keys in events. Implement new Event type. An event has a type and a list of KV pairs (ie. list-of-lists). Typical events may look like: "rewards": [{"amount": "5000uatom", "validator": "...", "recipient": "..."}] "sender": [{"address": "...", "balance": "100uatom"}] The events are indexed by {even.type}.{even.attribute[i].key}/.... In this case a client would subscribe or query for rewards.recipient='...' ABCI response types and related types now include Events []Event instead of Tags []cmn.KVPair. PubSub logic now publishes/matches against map[string][]string instead of map[string]string to support duplicate keys in response events (from #1385). A match is successful if the value is found in the slice of strings. closes: #1859 closes: #2905 ## Commits: * Implement Event ABCI type and updates responses to use events * Update messages_test.go * Update kvstore.go * Update event_bus.go * Update subscription.go * Update pubsub.go * Update kvstore.go * Update query logic to handle slice of strings in events * Update Empty#Matches and unit tests * Update pubsub logic * Update EventBus#Publish * Update kv tx indexer * Update godocs * Update ResultEvent to use slice of strings; update RPC * Update more tests * Update abci.md * Check for key in validateAndStringifyEvents * Fix KV indexer to skip empty keys * Fix linting errors * Update CHANGELOG_PENDING.md * Update docs/spec/abci/abci.md Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * Update abci/types/types.proto Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update docs/spec/abci/abci.md Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update libs/pubsub/query/query.go Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update match function to match if ANY value matches * Implement TestSubscribeDuplicateKeys * Update TestMatches to include multi-key test cases * Update events.go * Update Query interface godoc * Update match godoc * Add godoc for matchValue * DRY-up tx indexing * Return error from PublishWithEvents in EventBus#Publish * Update PublishEventNewBlockHeader to return an error * Fix build * Update events doc in ABCI * Update ABCI events godoc * Implement TestEventBusPublishEventTxDuplicateKeys * Update TestSubscribeDuplicateKeys to be table-driven * Remove mod file * Remove markdown from events godoc * Implement TestTxSearchDeprecatedIndexing test
6 years ago
7 years ago
7 years ago
abci: Refactor tagging events using list of lists (#3643) ## PR This PR introduces a fundamental breaking change to the structure of ABCI response and tx tags and the way they're processed. Namely, the SDK can support more complex and aggregated events for distribution and slashing. In addition, block responses can include duplicate keys in events. Implement new Event type. An event has a type and a list of KV pairs (ie. list-of-lists). Typical events may look like: "rewards": [{"amount": "5000uatom", "validator": "...", "recipient": "..."}] "sender": [{"address": "...", "balance": "100uatom"}] The events are indexed by {even.type}.{even.attribute[i].key}/.... In this case a client would subscribe or query for rewards.recipient='...' ABCI response types and related types now include Events []Event instead of Tags []cmn.KVPair. PubSub logic now publishes/matches against map[string][]string instead of map[string]string to support duplicate keys in response events (from #1385). A match is successful if the value is found in the slice of strings. closes: #1859 closes: #2905 ## Commits: * Implement Event ABCI type and updates responses to use events * Update messages_test.go * Update kvstore.go * Update event_bus.go * Update subscription.go * Update pubsub.go * Update kvstore.go * Update query logic to handle slice of strings in events * Update Empty#Matches and unit tests * Update pubsub logic * Update EventBus#Publish * Update kv tx indexer * Update godocs * Update ResultEvent to use slice of strings; update RPC * Update more tests * Update abci.md * Check for key in validateAndStringifyEvents * Fix KV indexer to skip empty keys * Fix linting errors * Update CHANGELOG_PENDING.md * Update docs/spec/abci/abci.md Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * Update abci/types/types.proto Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update docs/spec/abci/abci.md Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update libs/pubsub/query/query.go Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update match function to match if ANY value matches * Implement TestSubscribeDuplicateKeys * Update TestMatches to include multi-key test cases * Update events.go * Update Query interface godoc * Update match godoc * Add godoc for matchValue * DRY-up tx indexing * Return error from PublishWithEvents in EventBus#Publish * Update PublishEventNewBlockHeader to return an error * Fix build * Update events doc in ABCI * Update ABCI events godoc * Implement TestEventBusPublishEventTxDuplicateKeys * Update TestSubscribeDuplicateKeys to be table-driven * Remove mod file * Remove markdown from events godoc * Implement TestTxSearchDeprecatedIndexing test
6 years ago
abci: Refactor tagging events using list of lists (#3643) ## PR This PR introduces a fundamental breaking change to the structure of ABCI response and tx tags and the way they're processed. Namely, the SDK can support more complex and aggregated events for distribution and slashing. In addition, block responses can include duplicate keys in events. Implement new Event type. An event has a type and a list of KV pairs (ie. list-of-lists). Typical events may look like: "rewards": [{"amount": "5000uatom", "validator": "...", "recipient": "..."}] "sender": [{"address": "...", "balance": "100uatom"}] The events are indexed by {even.type}.{even.attribute[i].key}/.... In this case a client would subscribe or query for rewards.recipient='...' ABCI response types and related types now include Events []Event instead of Tags []cmn.KVPair. PubSub logic now publishes/matches against map[string][]string instead of map[string]string to support duplicate keys in response events (from #1385). A match is successful if the value is found in the slice of strings. closes: #1859 closes: #2905 ## Commits: * Implement Event ABCI type and updates responses to use events * Update messages_test.go * Update kvstore.go * Update event_bus.go * Update subscription.go * Update pubsub.go * Update kvstore.go * Update query logic to handle slice of strings in events * Update Empty#Matches and unit tests * Update pubsub logic * Update EventBus#Publish * Update kv tx indexer * Update godocs * Update ResultEvent to use slice of strings; update RPC * Update more tests * Update abci.md * Check for key in validateAndStringifyEvents * Fix KV indexer to skip empty keys * Fix linting errors * Update CHANGELOG_PENDING.md * Update docs/spec/abci/abci.md Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * Update abci/types/types.proto Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update docs/spec/abci/abci.md Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update libs/pubsub/query/query.go Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update match function to match if ANY value matches * Implement TestSubscribeDuplicateKeys * Update TestMatches to include multi-key test cases * Update events.go * Update Query interface godoc * Update match godoc * Add godoc for matchValue * DRY-up tx indexing * Return error from PublishWithEvents in EventBus#Publish * Update PublishEventNewBlockHeader to return an error * Fix build * Update events doc in ABCI * Update ABCI events godoc * Implement TestEventBusPublishEventTxDuplicateKeys * Update TestSubscribeDuplicateKeys to be table-driven * Remove mod file * Remove markdown from events godoc * Implement TestTxSearchDeprecatedIndexing test
6 years ago
abci: Refactor tagging events using list of lists (#3643) ## PR This PR introduces a fundamental breaking change to the structure of ABCI response and tx tags and the way they're processed. Namely, the SDK can support more complex and aggregated events for distribution and slashing. In addition, block responses can include duplicate keys in events. Implement new Event type. An event has a type and a list of KV pairs (ie. list-of-lists). Typical events may look like: "rewards": [{"amount": "5000uatom", "validator": "...", "recipient": "..."}] "sender": [{"address": "...", "balance": "100uatom"}] The events are indexed by {even.type}.{even.attribute[i].key}/.... In this case a client would subscribe or query for rewards.recipient='...' ABCI response types and related types now include Events []Event instead of Tags []cmn.KVPair. PubSub logic now publishes/matches against map[string][]string instead of map[string]string to support duplicate keys in response events (from #1385). A match is successful if the value is found in the slice of strings. closes: #1859 closes: #2905 ## Commits: * Implement Event ABCI type and updates responses to use events * Update messages_test.go * Update kvstore.go * Update event_bus.go * Update subscription.go * Update pubsub.go * Update kvstore.go * Update query logic to handle slice of strings in events * Update Empty#Matches and unit tests * Update pubsub logic * Update EventBus#Publish * Update kv tx indexer * Update godocs * Update ResultEvent to use slice of strings; update RPC * Update more tests * Update abci.md * Check for key in validateAndStringifyEvents * Fix KV indexer to skip empty keys * Fix linting errors * Update CHANGELOG_PENDING.md * Update docs/spec/abci/abci.md Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * Update abci/types/types.proto Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update docs/spec/abci/abci.md Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update libs/pubsub/query/query.go Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update match function to match if ANY value matches * Implement TestSubscribeDuplicateKeys * Update TestMatches to include multi-key test cases * Update events.go * Update Query interface godoc * Update match godoc * Add godoc for matchValue * DRY-up tx indexing * Return error from PublishWithEvents in EventBus#Publish * Update PublishEventNewBlockHeader to return an error * Fix build * Update events doc in ABCI * Update ABCI events godoc * Implement TestEventBusPublishEventTxDuplicateKeys * Update TestSubscribeDuplicateKeys to be table-driven * Remove mod file * Remove markdown from events godoc * Implement TestTxSearchDeprecatedIndexing test
6 years ago
abci: Refactor tagging events using list of lists (#3643) ## PR This PR introduces a fundamental breaking change to the structure of ABCI response and tx tags and the way they're processed. Namely, the SDK can support more complex and aggregated events for distribution and slashing. In addition, block responses can include duplicate keys in events. Implement new Event type. An event has a type and a list of KV pairs (ie. list-of-lists). Typical events may look like: "rewards": [{"amount": "5000uatom", "validator": "...", "recipient": "..."}] "sender": [{"address": "...", "balance": "100uatom"}] The events are indexed by {even.type}.{even.attribute[i].key}/.... In this case a client would subscribe or query for rewards.recipient='...' ABCI response types and related types now include Events []Event instead of Tags []cmn.KVPair. PubSub logic now publishes/matches against map[string][]string instead of map[string]string to support duplicate keys in response events (from #1385). A match is successful if the value is found in the slice of strings. closes: #1859 closes: #2905 ## Commits: * Implement Event ABCI type and updates responses to use events * Update messages_test.go * Update kvstore.go * Update event_bus.go * Update subscription.go * Update pubsub.go * Update kvstore.go * Update query logic to handle slice of strings in events * Update Empty#Matches and unit tests * Update pubsub logic * Update EventBus#Publish * Update kv tx indexer * Update godocs * Update ResultEvent to use slice of strings; update RPC * Update more tests * Update abci.md * Check for key in validateAndStringifyEvents * Fix KV indexer to skip empty keys * Fix linting errors * Update CHANGELOG_PENDING.md * Update docs/spec/abci/abci.md Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * Update abci/types/types.proto Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update docs/spec/abci/abci.md Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update libs/pubsub/query/query.go Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update match function to match if ANY value matches * Implement TestSubscribeDuplicateKeys * Update TestMatches to include multi-key test cases * Update events.go * Update Query interface godoc * Update match godoc * Add godoc for matchValue * DRY-up tx indexing * Return error from PublishWithEvents in EventBus#Publish * Update PublishEventNewBlockHeader to return an error * Fix build * Update events doc in ABCI * Update ABCI events godoc * Implement TestEventBusPublishEventTxDuplicateKeys * Update TestSubscribeDuplicateKeys to be table-driven * Remove mod file * Remove markdown from events godoc * Implement TestTxSearchDeprecatedIndexing test
6 years ago
libs/pubsub: relax tx querying (#4070) Some linting/cleanup missed from the initial events refactor Don't panic; instead, return false, error when matching breaks unexpectedly Strip non-numeric chars from values when attempting to match against query values Have the server log during send upon error * cleanup/lint Query#Conditions and do not panic * cleanup/lint Query#Matches and do not panic * cleanup/lint matchValue and do not panic * rever to panic in Query#Conditions * linting * strip alpha chars when attempting to match * add pending log entries * Update libs/pubsub/query/query.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * build: update variable names * update matchValue to return an error * update Query#Matches to return an error * update TestMatches * log error in send * Fix tests * Fix TestEmptyQueryMatchesAnything * fix linting * Update libs/pubsub/query/query.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * Update libs/pubsub/query/query.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * Update libs/pubsub/query/query.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * Update libs/pubsub/query/query.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * Update libs/pubsub/query/query.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * Update libs/pubsub/pubsub.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * add missing errors pkg import * update Query#Conditions to return an error * update query pkg unit tests * update TxIndex#Search * update pending changelog
5 years ago
libs/pubsub: relax tx querying (#4070) Some linting/cleanup missed from the initial events refactor Don't panic; instead, return false, error when matching breaks unexpectedly Strip non-numeric chars from values when attempting to match against query values Have the server log during send upon error * cleanup/lint Query#Conditions and do not panic * cleanup/lint Query#Matches and do not panic * cleanup/lint matchValue and do not panic * rever to panic in Query#Conditions * linting * strip alpha chars when attempting to match * add pending log entries * Update libs/pubsub/query/query.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * build: update variable names * update matchValue to return an error * update Query#Matches to return an error * update TestMatches * log error in send * Fix tests * Fix TestEmptyQueryMatchesAnything * fix linting * Update libs/pubsub/query/query.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * Update libs/pubsub/query/query.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * Update libs/pubsub/query/query.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * Update libs/pubsub/query/query.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * Update libs/pubsub/query/query.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * Update libs/pubsub/pubsub.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * add missing errors pkg import * update Query#Conditions to return an error * update query pkg unit tests * update TxIndex#Search * update pending changelog
5 years ago
lint: Enable Golint (#4212) * Fix many golint errors * Fix golint errors in the 'lite' package * Don't export Pool.store * Fix typo * Revert unwanted changes * Fix errors in counter package * Fix linter errors in kvstore package * Fix linter error in example package * Fix error in tests package * Fix linter errors in v2 package * Fix linter errors in consensus package * Fix linter errors in evidence package * Fix linter error in fail package * Fix linter errors in query package * Fix linter errors in core package * Fix linter errors in node package * Fix linter errors in mempool package * Fix linter error in conn package * Fix linter errors in pex package * Rename PEXReactor export to Reactor * Fix linter errors in trust package * Fix linter errors in upnp package * Fix linter errors in p2p package * Fix linter errors in proxy package * Fix linter errors in mock_test package * Fix linter error in client_test package * Fix linter errors in coretypes package * Fix linter errors in coregrpc package * Fix linter errors in rpcserver package * Fix linter errors in rpctypes package * Fix linter errors in rpctest package * Fix linter error in json2wal script * Fix linter error in wal2json script * Fix linter errors in kv package * Fix linter error in state package * Fix linter error in grpc_client * Fix linter errors in types package * Fix linter error in version package * Fix remaining errors * Address review comments * Fix broken tests * Reconcile package coregrpc * Fix golangci bot error * Fix new golint errors * Fix broken reference * Enable golint linter * minor changes to bring golint into line * fix failing test * fix pex reactor naming * address PR comments
5 years ago
lint: Enable Golint (#4212) * Fix many golint errors * Fix golint errors in the 'lite' package * Don't export Pool.store * Fix typo * Revert unwanted changes * Fix errors in counter package * Fix linter errors in kvstore package * Fix linter error in example package * Fix error in tests package * Fix linter errors in v2 package * Fix linter errors in consensus package * Fix linter errors in evidence package * Fix linter error in fail package * Fix linter errors in query package * Fix linter errors in core package * Fix linter errors in node package * Fix linter errors in mempool package * Fix linter error in conn package * Fix linter errors in pex package * Rename PEXReactor export to Reactor * Fix linter errors in trust package * Fix linter errors in upnp package * Fix linter errors in p2p package * Fix linter errors in proxy package * Fix linter errors in mock_test package * Fix linter error in client_test package * Fix linter errors in coretypes package * Fix linter errors in coregrpc package * Fix linter errors in rpcserver package * Fix linter errors in rpctypes package * Fix linter errors in rpctest package * Fix linter error in json2wal script * Fix linter error in wal2json script * Fix linter errors in kv package * Fix linter error in state package * Fix linter error in grpc_client * Fix linter errors in types package * Fix linter error in version package * Fix remaining errors * Address review comments * Fix broken tests * Reconcile package coregrpc * Fix golangci bot error * Fix new golint errors * Fix broken reference * Enable golint linter * minor changes to bring golint into line * fix failing test * fix pex reactor naming * address PR comments
5 years ago
lint: Enable Golint (#4212) * Fix many golint errors * Fix golint errors in the 'lite' package * Don't export Pool.store * Fix typo * Revert unwanted changes * Fix errors in counter package * Fix linter errors in kvstore package * Fix linter error in example package * Fix error in tests package * Fix linter errors in v2 package * Fix linter errors in consensus package * Fix linter errors in evidence package * Fix linter error in fail package * Fix linter errors in query package * Fix linter errors in core package * Fix linter errors in node package * Fix linter errors in mempool package * Fix linter error in conn package * Fix linter errors in pex package * Rename PEXReactor export to Reactor * Fix linter errors in trust package * Fix linter errors in upnp package * Fix linter errors in p2p package * Fix linter errors in proxy package * Fix linter errors in mock_test package * Fix linter error in client_test package * Fix linter errors in coretypes package * Fix linter errors in coregrpc package * Fix linter errors in rpcserver package * Fix linter errors in rpctypes package * Fix linter errors in rpctest package * Fix linter error in json2wal script * Fix linter error in wal2json script * Fix linter errors in kv package * Fix linter error in state package * Fix linter error in grpc_client * Fix linter errors in types package * Fix linter error in version package * Fix remaining errors * Address review comments * Fix broken tests * Reconcile package coregrpc * Fix golangci bot error * Fix new golint errors * Fix broken reference * Enable golint linter * minor changes to bring golint into line * fix failing test * fix pex reactor naming * address PR comments
5 years ago
abci: Refactor tagging events using list of lists (#3643) ## PR This PR introduces a fundamental breaking change to the structure of ABCI response and tx tags and the way they're processed. Namely, the SDK can support more complex and aggregated events for distribution and slashing. In addition, block responses can include duplicate keys in events. Implement new Event type. An event has a type and a list of KV pairs (ie. list-of-lists). Typical events may look like: "rewards": [{"amount": "5000uatom", "validator": "...", "recipient": "..."}] "sender": [{"address": "...", "balance": "100uatom"}] The events are indexed by {even.type}.{even.attribute[i].key}/.... In this case a client would subscribe or query for rewards.recipient='...' ABCI response types and related types now include Events []Event instead of Tags []cmn.KVPair. PubSub logic now publishes/matches against map[string][]string instead of map[string]string to support duplicate keys in response events (from #1385). A match is successful if the value is found in the slice of strings. closes: #1859 closes: #2905 ## Commits: * Implement Event ABCI type and updates responses to use events * Update messages_test.go * Update kvstore.go * Update event_bus.go * Update subscription.go * Update pubsub.go * Update kvstore.go * Update query logic to handle slice of strings in events * Update Empty#Matches and unit tests * Update pubsub logic * Update EventBus#Publish * Update kv tx indexer * Update godocs * Update ResultEvent to use slice of strings; update RPC * Update more tests * Update abci.md * Check for key in validateAndStringifyEvents * Fix KV indexer to skip empty keys * Fix linting errors * Update CHANGELOG_PENDING.md * Update docs/spec/abci/abci.md Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * Update abci/types/types.proto Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update docs/spec/abci/abci.md Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update libs/pubsub/query/query.go Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update match function to match if ANY value matches * Implement TestSubscribeDuplicateKeys * Update TestMatches to include multi-key test cases * Update events.go * Update Query interface godoc * Update match godoc * Add godoc for matchValue * DRY-up tx indexing * Return error from PublishWithEvents in EventBus#Publish * Update PublishEventNewBlockHeader to return an error * Fix build * Update events doc in ABCI * Update ABCI events godoc * Implement TestEventBusPublishEventTxDuplicateKeys * Update TestSubscribeDuplicateKeys to be table-driven * Remove mod file * Remove markdown from events godoc * Implement TestTxSearchDeprecatedIndexing test
6 years ago
abci: Refactor tagging events using list of lists (#3643) ## PR This PR introduces a fundamental breaking change to the structure of ABCI response and tx tags and the way they're processed. Namely, the SDK can support more complex and aggregated events for distribution and slashing. In addition, block responses can include duplicate keys in events. Implement new Event type. An event has a type and a list of KV pairs (ie. list-of-lists). Typical events may look like: "rewards": [{"amount": "5000uatom", "validator": "...", "recipient": "..."}] "sender": [{"address": "...", "balance": "100uatom"}] The events are indexed by {even.type}.{even.attribute[i].key}/.... In this case a client would subscribe or query for rewards.recipient='...' ABCI response types and related types now include Events []Event instead of Tags []cmn.KVPair. PubSub logic now publishes/matches against map[string][]string instead of map[string]string to support duplicate keys in response events (from #1385). A match is successful if the value is found in the slice of strings. closes: #1859 closes: #2905 ## Commits: * Implement Event ABCI type and updates responses to use events * Update messages_test.go * Update kvstore.go * Update event_bus.go * Update subscription.go * Update pubsub.go * Update kvstore.go * Update query logic to handle slice of strings in events * Update Empty#Matches and unit tests * Update pubsub logic * Update EventBus#Publish * Update kv tx indexer * Update godocs * Update ResultEvent to use slice of strings; update RPC * Update more tests * Update abci.md * Check for key in validateAndStringifyEvents * Fix KV indexer to skip empty keys * Fix linting errors * Update CHANGELOG_PENDING.md * Update docs/spec/abci/abci.md Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * Update abci/types/types.proto Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update docs/spec/abci/abci.md Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update libs/pubsub/query/query.go Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update match function to match if ANY value matches * Implement TestSubscribeDuplicateKeys * Update TestMatches to include multi-key test cases * Update events.go * Update Query interface godoc * Update match godoc * Add godoc for matchValue * DRY-up tx indexing * Return error from PublishWithEvents in EventBus#Publish * Update PublishEventNewBlockHeader to return an error * Fix build * Update events doc in ABCI * Update ABCI events godoc * Implement TestEventBusPublishEventTxDuplicateKeys * Update TestSubscribeDuplicateKeys to be table-driven * Remove mod file * Remove markdown from events godoc * Implement TestTxSearchDeprecatedIndexing test
6 years ago
abci: Refactor tagging events using list of lists (#3643) ## PR This PR introduces a fundamental breaking change to the structure of ABCI response and tx tags and the way they're processed. Namely, the SDK can support more complex and aggregated events for distribution and slashing. In addition, block responses can include duplicate keys in events. Implement new Event type. An event has a type and a list of KV pairs (ie. list-of-lists). Typical events may look like: "rewards": [{"amount": "5000uatom", "validator": "...", "recipient": "..."}] "sender": [{"address": "...", "balance": "100uatom"}] The events are indexed by {even.type}.{even.attribute[i].key}/.... In this case a client would subscribe or query for rewards.recipient='...' ABCI response types and related types now include Events []Event instead of Tags []cmn.KVPair. PubSub logic now publishes/matches against map[string][]string instead of map[string]string to support duplicate keys in response events (from #1385). A match is successful if the value is found in the slice of strings. closes: #1859 closes: #2905 ## Commits: * Implement Event ABCI type and updates responses to use events * Update messages_test.go * Update kvstore.go * Update event_bus.go * Update subscription.go * Update pubsub.go * Update kvstore.go * Update query logic to handle slice of strings in events * Update Empty#Matches and unit tests * Update pubsub logic * Update EventBus#Publish * Update kv tx indexer * Update godocs * Update ResultEvent to use slice of strings; update RPC * Update more tests * Update abci.md * Check for key in validateAndStringifyEvents * Fix KV indexer to skip empty keys * Fix linting errors * Update CHANGELOG_PENDING.md * Update docs/spec/abci/abci.md Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * Update abci/types/types.proto Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update docs/spec/abci/abci.md Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update libs/pubsub/query/query.go Co-Authored-By: Ethan Buchman <ethan@coinculture.info> * Update match function to match if ANY value matches * Implement TestSubscribeDuplicateKeys * Update TestMatches to include multi-key test cases * Update events.go * Update Query interface godoc * Update match godoc * Add godoc for matchValue * DRY-up tx indexing * Return error from PublishWithEvents in EventBus#Publish * Update PublishEventNewBlockHeader to return an error * Fix build * Update events doc in ABCI * Update ABCI events godoc * Implement TestEventBusPublishEventTxDuplicateKeys * Update TestSubscribeDuplicateKeys to be table-driven * Remove mod file * Remove markdown from events godoc * Implement TestTxSearchDeprecatedIndexing test
6 years ago
  1. package kv
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/hex"
  6. "fmt"
  7. "strconv"
  8. "strings"
  9. "time"
  10. dbm "github.com/tendermint/tm-db"
  11. "github.com/tendermint/tendermint/libs/pubsub/query"
  12. tmstring "github.com/tendermint/tendermint/libs/strings"
  13. "github.com/tendermint/tendermint/state/txindex"
  14. "github.com/tendermint/tendermint/types"
  15. )
  16. const (
  17. tagKeySeparator = "/"
  18. )
  19. var _ txindex.TxIndexer = (*TxIndex)(nil)
  20. // TxIndex is the simplest possible indexer, backed by key-value storage (levelDB).
  21. type TxIndex struct {
  22. store dbm.DB
  23. compositeKeysToIndex []string
  24. indexAllEvents bool
  25. }
  26. // NewTxIndex creates new KV indexer.
  27. func NewTxIndex(store dbm.DB, options ...func(*TxIndex)) *TxIndex {
  28. txi := &TxIndex{store: store, compositeKeysToIndex: make([]string, 0), indexAllEvents: false}
  29. for _, o := range options {
  30. o(txi)
  31. }
  32. return txi
  33. }
  34. // IndexEvents is an option for setting which composite keys to index.
  35. func IndexEvents(compositeKeys []string) func(*TxIndex) {
  36. return func(txi *TxIndex) {
  37. txi.compositeKeysToIndex = compositeKeys
  38. }
  39. }
  40. // IndexAllEvents is an option for indexing all events.
  41. func IndexAllEvents() func(*TxIndex) {
  42. return func(txi *TxIndex) {
  43. txi.indexAllEvents = true
  44. }
  45. }
  46. // Get gets transaction from the TxIndex storage and returns it or nil if the
  47. // transaction is not found.
  48. func (txi *TxIndex) Get(hash []byte) (*types.TxResult, error) {
  49. if len(hash) == 0 {
  50. return nil, txindex.ErrorEmptyHash
  51. }
  52. rawBytes, err := txi.store.Get(hash)
  53. if err != nil {
  54. panic(err)
  55. }
  56. if rawBytes == nil {
  57. return nil, nil
  58. }
  59. txResult := new(types.TxResult)
  60. err = cdc.UnmarshalBinaryBare(rawBytes, &txResult)
  61. if err != nil {
  62. return nil, fmt.Errorf("error reading TxResult: %v", err)
  63. }
  64. return txResult, nil
  65. }
  66. // AddBatch indexes a batch of transactions using the given list of events. Each
  67. // key that indexed from the tx's events is a composite of the event type and
  68. // the respective attribute's key delimited by a "." (eg. "account.number").
  69. // Any event with an empty type is not indexed.
  70. func (txi *TxIndex) AddBatch(b *txindex.Batch) error {
  71. storeBatch := txi.store.NewBatch()
  72. defer storeBatch.Close()
  73. for _, result := range b.Ops {
  74. hash := result.Tx.Hash()
  75. // index tx by events
  76. txi.indexEvents(result, hash, storeBatch)
  77. // index tx by height
  78. if txi.indexAllEvents || tmstring.StringInSlice(types.TxHeightKey, txi.compositeKeysToIndex) {
  79. storeBatch.Set(keyForHeight(result), hash)
  80. }
  81. // index tx by hash
  82. rawBytes, err := cdc.MarshalBinaryBare(result)
  83. if err != nil {
  84. return err
  85. }
  86. storeBatch.Set(hash, rawBytes)
  87. }
  88. storeBatch.WriteSync()
  89. return nil
  90. }
  91. // Index indexes a single transaction using the given list of events. Each key
  92. // that indexed from the tx's events is a composite of the event type and the
  93. // respective attribute's key delimited by a "." (eg. "account.number").
  94. // Any event with an empty type is not indexed.
  95. func (txi *TxIndex) Index(result *types.TxResult) error {
  96. b := txi.store.NewBatch()
  97. defer b.Close()
  98. hash := result.Tx.Hash()
  99. // index tx by events
  100. txi.indexEvents(result, hash, b)
  101. // index tx by height
  102. if txi.indexAllEvents || tmstring.StringInSlice(types.TxHeightKey, txi.compositeKeysToIndex) {
  103. b.Set(keyForHeight(result), hash)
  104. }
  105. // index tx by hash
  106. rawBytes, err := cdc.MarshalBinaryBare(result)
  107. if err != nil {
  108. return err
  109. }
  110. b.Set(hash, rawBytes)
  111. b.WriteSync()
  112. return nil
  113. }
  114. func (txi *TxIndex) indexEvents(result *types.TxResult, hash []byte, store dbm.SetDeleter) {
  115. for _, event := range result.Result.Events {
  116. // only index events with a non-empty type
  117. if len(event.Type) == 0 {
  118. continue
  119. }
  120. for _, attr := range event.Attributes {
  121. if len(attr.Key) == 0 {
  122. continue
  123. }
  124. compositeTag := fmt.Sprintf("%s.%s", event.Type, string(attr.Key))
  125. if txi.indexAllEvents || tmstring.StringInSlice(compositeTag, txi.compositeKeysToIndex) || attr.GetIndex() {
  126. store.Set(keyForEvent(compositeTag, attr.Value, result), hash)
  127. }
  128. }
  129. }
  130. }
  131. // Search performs a search using the given query.
  132. //
  133. // It breaks the query into conditions (like "tx.height > 5"). For each
  134. // condition, it queries the DB index. One special use cases here: (1) if
  135. // "tx.hash" is found, it returns tx result for it (2) for range queries it is
  136. // better for the client to provide both lower and upper bounds, so we are not
  137. // performing a full scan. Results from querying indexes are then intersected
  138. // and returned to the caller, in no particular order.
  139. //
  140. // Search will exit early and return any result fetched so far,
  141. // when a message is received on the context chan.
  142. func (txi *TxIndex) Search(ctx context.Context, q *query.Query) ([]*types.TxResult, error) {
  143. // Potentially exit early.
  144. select {
  145. case <-ctx.Done():
  146. results := make([]*types.TxResult, 0)
  147. return results, nil
  148. default:
  149. }
  150. var hashesInitialized bool
  151. filteredHashes := make(map[string][]byte)
  152. // get a list of conditions (like "tx.height > 5")
  153. conditions, err := q.Conditions()
  154. if err != nil {
  155. return nil, fmt.Errorf("error during parsing conditions from query: %w", err)
  156. }
  157. // if there is a hash condition, return the result immediately
  158. hash, ok, err := lookForHash(conditions)
  159. if err != nil {
  160. return nil, fmt.Errorf("error during searching for a hash in the query: %w", err)
  161. } else if ok {
  162. res, err := txi.Get(hash)
  163. switch {
  164. case err != nil:
  165. return []*types.TxResult{}, fmt.Errorf("error while retrieving the result: %w", err)
  166. case res == nil:
  167. return []*types.TxResult{}, nil
  168. default:
  169. return []*types.TxResult{res}, nil
  170. }
  171. }
  172. // conditions to skip because they're handled before "everything else"
  173. skipIndexes := make([]int, 0)
  174. // extract ranges
  175. // if both upper and lower bounds exist, it's better to get them in order not
  176. // no iterate over kvs that are not within range.
  177. ranges, rangeIndexes := lookForRanges(conditions)
  178. if len(ranges) > 0 {
  179. skipIndexes = append(skipIndexes, rangeIndexes...)
  180. for _, r := range ranges {
  181. if !hashesInitialized {
  182. filteredHashes = txi.matchRange(ctx, r, startKey(r.key), filteredHashes, true)
  183. hashesInitialized = true
  184. // Ignore any remaining conditions if the first condition resulted
  185. // in no matches (assuming implicit AND operand).
  186. if len(filteredHashes) == 0 {
  187. break
  188. }
  189. } else {
  190. filteredHashes = txi.matchRange(ctx, r, startKey(r.key), filteredHashes, false)
  191. }
  192. }
  193. }
  194. // if there is a height condition ("tx.height=3"), extract it
  195. height := lookForHeight(conditions)
  196. // for all other conditions
  197. for i, c := range conditions {
  198. if intInSlice(i, skipIndexes) {
  199. continue
  200. }
  201. if !hashesInitialized {
  202. filteredHashes = txi.match(ctx, c, startKeyForCondition(c, height), filteredHashes, true)
  203. hashesInitialized = true
  204. // Ignore any remaining conditions if the first condition resulted
  205. // in no matches (assuming implicit AND operand).
  206. if len(filteredHashes) == 0 {
  207. break
  208. }
  209. } else {
  210. filteredHashes = txi.match(ctx, c, startKeyForCondition(c, height), filteredHashes, false)
  211. }
  212. }
  213. results := make([]*types.TxResult, 0, len(filteredHashes))
  214. for _, h := range filteredHashes {
  215. res, err := txi.Get(h)
  216. if err != nil {
  217. return nil, fmt.Errorf("failed to get Tx{%X}: %w", h, err)
  218. }
  219. results = append(results, res)
  220. // Potentially exit early.
  221. select {
  222. case <-ctx.Done():
  223. break
  224. default:
  225. }
  226. }
  227. return results, nil
  228. }
  229. func lookForHash(conditions []query.Condition) (hash []byte, ok bool, err error) {
  230. for _, c := range conditions {
  231. if c.CompositeKey == types.TxHashKey {
  232. decoded, err := hex.DecodeString(c.Operand.(string))
  233. return decoded, true, err
  234. }
  235. }
  236. return
  237. }
  238. // lookForHeight returns a height if there is an "height=X" condition.
  239. func lookForHeight(conditions []query.Condition) (height int64) {
  240. for _, c := range conditions {
  241. if c.CompositeKey == types.TxHeightKey && c.Op == query.OpEqual {
  242. return c.Operand.(int64)
  243. }
  244. }
  245. return 0
  246. }
  247. // special map to hold range conditions
  248. // Example: account.number => queryRange{lowerBound: 1, upperBound: 5}
  249. type queryRanges map[string]queryRange
  250. type queryRange struct {
  251. lowerBound interface{} // int || time.Time
  252. upperBound interface{} // int || time.Time
  253. key string
  254. includeLowerBound bool
  255. includeUpperBound bool
  256. }
  257. func (r queryRange) lowerBoundValue() interface{} {
  258. if r.lowerBound == nil {
  259. return nil
  260. }
  261. if r.includeLowerBound {
  262. return r.lowerBound
  263. }
  264. switch t := r.lowerBound.(type) {
  265. case int64:
  266. return t + 1
  267. case time.Time:
  268. return t.Unix() + 1
  269. default:
  270. panic("not implemented")
  271. }
  272. }
  273. func (r queryRange) AnyBound() interface{} {
  274. if r.lowerBound != nil {
  275. return r.lowerBound
  276. }
  277. return r.upperBound
  278. }
  279. func (r queryRange) upperBoundValue() interface{} {
  280. if r.upperBound == nil {
  281. return nil
  282. }
  283. if r.includeUpperBound {
  284. return r.upperBound
  285. }
  286. switch t := r.upperBound.(type) {
  287. case int64:
  288. return t - 1
  289. case time.Time:
  290. return t.Unix() - 1
  291. default:
  292. panic("not implemented")
  293. }
  294. }
  295. func lookForRanges(conditions []query.Condition) (ranges queryRanges, indexes []int) {
  296. ranges = make(queryRanges)
  297. for i, c := range conditions {
  298. if isRangeOperation(c.Op) {
  299. r, ok := ranges[c.CompositeKey]
  300. if !ok {
  301. r = queryRange{key: c.CompositeKey}
  302. }
  303. switch c.Op {
  304. case query.OpGreater:
  305. r.lowerBound = c.Operand
  306. case query.OpGreaterEqual:
  307. r.includeLowerBound = true
  308. r.lowerBound = c.Operand
  309. case query.OpLess:
  310. r.upperBound = c.Operand
  311. case query.OpLessEqual:
  312. r.includeUpperBound = true
  313. r.upperBound = c.Operand
  314. }
  315. ranges[c.CompositeKey] = r
  316. indexes = append(indexes, i)
  317. }
  318. }
  319. return ranges, indexes
  320. }
  321. func isRangeOperation(op query.Operator) bool {
  322. switch op {
  323. case query.OpGreater, query.OpGreaterEqual, query.OpLess, query.OpLessEqual:
  324. return true
  325. default:
  326. return false
  327. }
  328. }
  329. // match returns all matching txs by hash that meet a given condition and start
  330. // key. An already filtered result (filteredHashes) is provided such that any
  331. // non-intersecting matches are removed.
  332. //
  333. // NOTE: filteredHashes may be empty if no previous condition has matched.
  334. func (txi *TxIndex) match(
  335. ctx context.Context,
  336. c query.Condition,
  337. startKeyBz []byte,
  338. filteredHashes map[string][]byte,
  339. firstRun bool,
  340. ) map[string][]byte {
  341. // A previous match was attempted but resulted in no matches, so we return
  342. // no matches (assuming AND operand).
  343. if !firstRun && len(filteredHashes) == 0 {
  344. return filteredHashes
  345. }
  346. tmpHashes := make(map[string][]byte)
  347. switch {
  348. case c.Op == query.OpEqual:
  349. it, err := dbm.IteratePrefix(txi.store, startKeyBz)
  350. if err != nil {
  351. panic(err)
  352. }
  353. defer it.Close()
  354. for ; it.Valid(); it.Next() {
  355. tmpHashes[string(it.Value())] = it.Value()
  356. // Potentially exit early.
  357. select {
  358. case <-ctx.Done():
  359. break
  360. default:
  361. }
  362. }
  363. case c.Op == query.OpContains:
  364. // XXX: startKey does not apply here.
  365. // For example, if startKey = "account.owner/an/" and search query = "account.owner CONTAINS an"
  366. // we can't iterate with prefix "account.owner/an/" because we might miss keys like "account.owner/Ulan/"
  367. it, err := dbm.IteratePrefix(txi.store, startKey(c.CompositeKey))
  368. if err != nil {
  369. panic(err)
  370. }
  371. defer it.Close()
  372. for ; it.Valid(); it.Next() {
  373. if !isTagKey(it.Key()) {
  374. continue
  375. }
  376. if strings.Contains(extractValueFromKey(it.Key()), c.Operand.(string)) {
  377. tmpHashes[string(it.Value())] = it.Value()
  378. }
  379. // Potentially exit early.
  380. select {
  381. case <-ctx.Done():
  382. break
  383. default:
  384. }
  385. }
  386. default:
  387. panic("other operators should be handled already")
  388. }
  389. if len(tmpHashes) == 0 || firstRun {
  390. // Either:
  391. //
  392. // 1. Regardless if a previous match was attempted, which may have had
  393. // results, but no match was found for the current condition, then we
  394. // return no matches (assuming AND operand).
  395. //
  396. // 2. A previous match was not attempted, so we return all results.
  397. return tmpHashes
  398. }
  399. // Remove/reduce matches in filteredHashes that were not found in this
  400. // match (tmpHashes).
  401. for k := range filteredHashes {
  402. if tmpHashes[k] == nil {
  403. delete(filteredHashes, k)
  404. // Potentially exit early.
  405. select {
  406. case <-ctx.Done():
  407. break
  408. default:
  409. }
  410. }
  411. }
  412. return filteredHashes
  413. }
  414. // matchRange returns all matching txs by hash that meet a given queryRange and
  415. // start key. An already filtered result (filteredHashes) is provided such that
  416. // any non-intersecting matches are removed.
  417. //
  418. // NOTE: filteredHashes may be empty if no previous condition has matched.
  419. func (txi *TxIndex) matchRange(
  420. ctx context.Context,
  421. r queryRange,
  422. startKey []byte,
  423. filteredHashes map[string][]byte,
  424. firstRun bool,
  425. ) map[string][]byte {
  426. // A previous match was attempted but resulted in no matches, so we return
  427. // no matches (assuming AND operand).
  428. if !firstRun && len(filteredHashes) == 0 {
  429. return filteredHashes
  430. }
  431. tmpHashes := make(map[string][]byte)
  432. lowerBound := r.lowerBoundValue()
  433. upperBound := r.upperBoundValue()
  434. it, err := dbm.IteratePrefix(txi.store, startKey)
  435. if err != nil {
  436. panic(err)
  437. }
  438. defer it.Close()
  439. LOOP:
  440. for ; it.Valid(); it.Next() {
  441. if !isTagKey(it.Key()) {
  442. continue
  443. }
  444. if _, ok := r.AnyBound().(int64); ok {
  445. v, err := strconv.ParseInt(extractValueFromKey(it.Key()), 10, 64)
  446. if err != nil {
  447. continue LOOP
  448. }
  449. include := true
  450. if lowerBound != nil && v < lowerBound.(int64) {
  451. include = false
  452. }
  453. if upperBound != nil && v > upperBound.(int64) {
  454. include = false
  455. }
  456. if include {
  457. tmpHashes[string(it.Value())] = it.Value()
  458. }
  459. // XXX: passing time in a ABCI Events is not yet implemented
  460. // case time.Time:
  461. // v := strconv.ParseInt(extractValueFromKey(it.Key()), 10, 64)
  462. // if v == r.upperBound {
  463. // break
  464. // }
  465. }
  466. // Potentially exit early.
  467. select {
  468. case <-ctx.Done():
  469. break
  470. default:
  471. }
  472. }
  473. if len(tmpHashes) == 0 || firstRun {
  474. // Either:
  475. //
  476. // 1. Regardless if a previous match was attempted, which may have had
  477. // results, but no match was found for the current condition, then we
  478. // return no matches (assuming AND operand).
  479. //
  480. // 2. A previous match was not attempted, so we return all results.
  481. return tmpHashes
  482. }
  483. // Remove/reduce matches in filteredHashes that were not found in this
  484. // match (tmpHashes).
  485. for k := range filteredHashes {
  486. if tmpHashes[k] == nil {
  487. delete(filteredHashes, k)
  488. // Potentially exit early.
  489. select {
  490. case <-ctx.Done():
  491. break
  492. default:
  493. }
  494. }
  495. }
  496. return filteredHashes
  497. }
  498. ///////////////////////////////////////////////////////////////////////////////
  499. // Keys
  500. func isTagKey(key []byte) bool {
  501. return strings.Count(string(key), tagKeySeparator) == 3
  502. }
  503. func extractValueFromKey(key []byte) string {
  504. parts := strings.SplitN(string(key), tagKeySeparator, 3)
  505. return parts[1]
  506. }
  507. func keyForEvent(key string, value []byte, result *types.TxResult) []byte {
  508. return []byte(fmt.Sprintf("%s/%s/%d/%d",
  509. key,
  510. value,
  511. result.Height,
  512. result.Index,
  513. ))
  514. }
  515. func keyForHeight(result *types.TxResult) []byte {
  516. return []byte(fmt.Sprintf("%s/%d/%d/%d",
  517. types.TxHeightKey,
  518. result.Height,
  519. result.Height,
  520. result.Index,
  521. ))
  522. }
  523. func startKeyForCondition(c query.Condition, height int64) []byte {
  524. if height > 0 {
  525. return startKey(c.CompositeKey, c.Operand, height)
  526. }
  527. return startKey(c.CompositeKey, c.Operand)
  528. }
  529. func startKey(fields ...interface{}) []byte {
  530. var b bytes.Buffer
  531. for _, f := range fields {
  532. b.Write([]byte(fmt.Sprintf("%v", f) + tagKeySeparator))
  533. }
  534. return b.Bytes()
  535. }