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.

622 lines
16 KiB

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
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
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
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
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. "context"
  4. "encoding/hex"
  5. "fmt"
  6. "strconv"
  7. "strings"
  8. "github.com/gogo/protobuf/proto"
  9. "github.com/google/orderedcode"
  10. dbm "github.com/tendermint/tm-db"
  11. abci "github.com/tendermint/tendermint/abci/types"
  12. "github.com/tendermint/tendermint/libs/pubsub/query"
  13. "github.com/tendermint/tendermint/state/indexer"
  14. "github.com/tendermint/tendermint/state/txindex"
  15. "github.com/tendermint/tendermint/types"
  16. )
  17. var _ txindex.TxIndexer = (*TxIndex)(nil)
  18. // TxIndex is the simplest possible indexer
  19. // It is backed by two kv stores:
  20. // 1. txhash - result (primary key)
  21. // 2. event - txhash (secondary key)
  22. type TxIndex struct {
  23. store dbm.DB
  24. }
  25. // NewTxIndex creates new KV indexer.
  26. func NewTxIndex(store dbm.DB) *TxIndex {
  27. return &TxIndex{
  28. store: store,
  29. }
  30. }
  31. // Get gets transaction from the TxIndex storage and returns it or nil if the
  32. // transaction is not found.
  33. func (txi *TxIndex) Get(hash []byte) (*abci.TxResult, error) {
  34. if len(hash) == 0 {
  35. return nil, txindex.ErrorEmptyHash
  36. }
  37. rawBytes, err := txi.store.Get(primaryKey(hash))
  38. if err != nil {
  39. panic(err)
  40. }
  41. if rawBytes == nil {
  42. return nil, nil
  43. }
  44. txResult := new(abci.TxResult)
  45. err = proto.Unmarshal(rawBytes, txResult)
  46. if err != nil {
  47. return nil, fmt.Errorf("error reading TxResult: %v", err)
  48. }
  49. return txResult, nil
  50. }
  51. // AddBatch indexes a batch of transactions using the given list of events. Each
  52. // key that indexed from the tx's events is a composite of the event type and
  53. // the respective attribute's key delimited by a "." (eg. "account.number").
  54. // Any event with an empty type is not indexed.
  55. func (txi *TxIndex) AddBatch(b *txindex.Batch) error {
  56. storeBatch := txi.store.NewBatch()
  57. defer storeBatch.Close()
  58. for _, result := range b.Ops {
  59. hash := types.Tx(result.Tx).Hash()
  60. // index tx by events
  61. err := txi.indexEvents(result, hash, storeBatch)
  62. if err != nil {
  63. return err
  64. }
  65. // index by height (always)
  66. err = storeBatch.Set(keyFromHeight(result), hash)
  67. if err != nil {
  68. return err
  69. }
  70. rawBytes, err := proto.Marshal(result)
  71. if err != nil {
  72. return err
  73. }
  74. // index by hash (always)
  75. err = storeBatch.Set(primaryKey(hash), rawBytes)
  76. if err != nil {
  77. return err
  78. }
  79. }
  80. return storeBatch.WriteSync()
  81. }
  82. // Index indexes a single transaction using the given list of events. Each key
  83. // that indexed from the tx's events is a composite of the event type and the
  84. // respective attribute's key delimited by a "." (eg. "account.number").
  85. // Any event with an empty type is not indexed.
  86. func (txi *TxIndex) Index(result *abci.TxResult) error {
  87. b := txi.store.NewBatch()
  88. defer b.Close()
  89. hash := types.Tx(result.Tx).Hash()
  90. // index tx by events
  91. err := txi.indexEvents(result, hash, b)
  92. if err != nil {
  93. return err
  94. }
  95. // index by height (always)
  96. err = b.Set(keyFromHeight(result), hash)
  97. if err != nil {
  98. return err
  99. }
  100. rawBytes, err := proto.Marshal(result)
  101. if err != nil {
  102. return err
  103. }
  104. // index by hash (always)
  105. err = b.Set(primaryKey(hash), rawBytes)
  106. if err != nil {
  107. return err
  108. }
  109. return b.WriteSync()
  110. }
  111. func (txi *TxIndex) indexEvents(result *abci.TxResult, hash []byte, store dbm.Batch) error {
  112. for _, event := range result.Result.Events {
  113. // only index events with a non-empty type
  114. if len(event.Type) == 0 {
  115. continue
  116. }
  117. for _, attr := range event.Attributes {
  118. if len(attr.Key) == 0 {
  119. continue
  120. }
  121. // index if `index: true` is set
  122. compositeTag := fmt.Sprintf("%s.%s", event.Type, string(attr.Key))
  123. // ensure event does not conflict with a reserved prefix key
  124. if compositeTag == types.TxHashKey || compositeTag == types.TxHeightKey {
  125. return fmt.Errorf("event type and attribute key \"%s\" is reserved; please use a different key", compositeTag)
  126. }
  127. if attr.GetIndex() {
  128. err := store.Set(keyFromEvent(compositeTag, attr.Value, result), hash)
  129. if err != nil {
  130. return err
  131. }
  132. }
  133. }
  134. }
  135. return nil
  136. }
  137. // Search performs a search using the given query.
  138. //
  139. // It breaks the query into conditions (like "tx.height > 5"). For each
  140. // condition, it queries the DB index. One special use cases here: (1) if
  141. // "tx.hash" is found, it returns tx result for it (2) for range queries it is
  142. // better for the client to provide both lower and upper bounds, so we are not
  143. // performing a full scan. Results from querying indexes are then intersected
  144. // and returned to the caller, in no particular order.
  145. //
  146. // Search will exit early and return any result fetched so far,
  147. // when a message is received on the context chan.
  148. func (txi *TxIndex) Search(ctx context.Context, q *query.Query) ([]*abci.TxResult, error) {
  149. select {
  150. case <-ctx.Done():
  151. return make([]*abci.TxResult, 0), nil
  152. default:
  153. }
  154. var hashesInitialized bool
  155. filteredHashes := make(map[string][]byte)
  156. // get a list of conditions (like "tx.height > 5")
  157. conditions, err := q.Conditions()
  158. if err != nil {
  159. return nil, fmt.Errorf("error during parsing conditions from query: %w", err)
  160. }
  161. // if there is a hash condition, return the result immediately
  162. hash, ok, err := lookForHash(conditions)
  163. if err != nil {
  164. return nil, fmt.Errorf("error during searching for a hash in the query: %w", err)
  165. } else if ok {
  166. res, err := txi.Get(hash)
  167. switch {
  168. case err != nil:
  169. return []*abci.TxResult{}, fmt.Errorf("error while retrieving the result: %w", err)
  170. case res == nil:
  171. return []*abci.TxResult{}, nil
  172. default:
  173. return []*abci.TxResult{res}, nil
  174. }
  175. }
  176. // conditions to skip because they're handled before "everything else"
  177. skipIndexes := make([]int, 0)
  178. // extract ranges
  179. // if both upper and lower bounds exist, it's better to get them in order not
  180. // no iterate over kvs that are not within range.
  181. ranges, rangeIndexes := indexer.LookForRanges(conditions)
  182. if len(ranges) > 0 {
  183. skipIndexes = append(skipIndexes, rangeIndexes...)
  184. for _, qr := range ranges {
  185. if !hashesInitialized {
  186. filteredHashes = txi.matchRange(ctx, qr, prefixFromCompositeKey(qr.Key), filteredHashes, true)
  187. hashesInitialized = true
  188. // Ignore any remaining conditions if the first condition resulted
  189. // in no matches (assuming implicit AND operand).
  190. if len(filteredHashes) == 0 {
  191. break
  192. }
  193. } else {
  194. filteredHashes = txi.matchRange(ctx, qr, prefixFromCompositeKey(qr.Key), filteredHashes, false)
  195. }
  196. }
  197. }
  198. // if there is a height condition ("tx.height=3"), extract it
  199. height := lookForHeight(conditions)
  200. // for all other conditions
  201. for i, c := range conditions {
  202. if intInSlice(i, skipIndexes) {
  203. continue
  204. }
  205. if !hashesInitialized {
  206. filteredHashes = txi.match(ctx, c, prefixForCondition(c, height), filteredHashes, true)
  207. hashesInitialized = true
  208. // Ignore any remaining conditions if the first condition resulted
  209. // in no matches (assuming implicit AND operand).
  210. if len(filteredHashes) == 0 {
  211. break
  212. }
  213. } else {
  214. filteredHashes = txi.match(ctx, c, prefixForCondition(c, height), filteredHashes, false)
  215. }
  216. }
  217. results := make([]*abci.TxResult, 0, len(filteredHashes))
  218. for _, h := range filteredHashes {
  219. res, err := txi.Get(h)
  220. if err != nil {
  221. return nil, fmt.Errorf("failed to get Tx{%X}: %w", h, err)
  222. }
  223. results = append(results, res)
  224. // Potentially exit early.
  225. select {
  226. case <-ctx.Done():
  227. break
  228. default:
  229. }
  230. }
  231. return results, nil
  232. }
  233. func lookForHash(conditions []query.Condition) (hash []byte, ok bool, err error) {
  234. for _, c := range conditions {
  235. if c.CompositeKey == types.TxHashKey {
  236. decoded, err := hex.DecodeString(c.Operand.(string))
  237. return decoded, true, err
  238. }
  239. }
  240. return
  241. }
  242. // lookForHeight returns a height if there is an "height=X" condition.
  243. func lookForHeight(conditions []query.Condition) (height int64) {
  244. for _, c := range conditions {
  245. if c.CompositeKey == types.TxHeightKey && c.Op == query.OpEqual {
  246. return c.Operand.(int64)
  247. }
  248. }
  249. return 0
  250. }
  251. // match returns all matching txs by hash that meet a given condition and start
  252. // key. An already filtered result (filteredHashes) is provided such that any
  253. // non-intersecting matches are removed.
  254. //
  255. // NOTE: filteredHashes may be empty if no previous condition has matched.
  256. func (txi *TxIndex) match(
  257. ctx context.Context,
  258. c query.Condition,
  259. startKeyBz []byte,
  260. filteredHashes map[string][]byte,
  261. firstRun bool,
  262. ) map[string][]byte {
  263. // A previous match was attempted but resulted in no matches, so we return
  264. // no matches (assuming AND operand).
  265. if !firstRun && len(filteredHashes) == 0 {
  266. return filteredHashes
  267. }
  268. tmpHashes := make(map[string][]byte)
  269. switch {
  270. case c.Op == query.OpEqual:
  271. it, err := dbm.IteratePrefix(txi.store, startKeyBz)
  272. if err != nil {
  273. panic(err)
  274. }
  275. defer it.Close()
  276. for ; it.Valid(); it.Next() {
  277. tmpHashes[string(it.Value())] = it.Value()
  278. // Potentially exit early.
  279. select {
  280. case <-ctx.Done():
  281. break
  282. default:
  283. }
  284. }
  285. if err := it.Error(); err != nil {
  286. panic(err)
  287. }
  288. case c.Op == query.OpExists:
  289. // XXX: can't use startKeyBz here because c.Operand is nil
  290. // (e.g. "account.owner/<nil>/" won't match w/ a single row)
  291. it, err := dbm.IteratePrefix(txi.store, prefixFromCompositeKey(c.CompositeKey))
  292. if err != nil {
  293. panic(err)
  294. }
  295. defer it.Close()
  296. for ; it.Valid(); it.Next() {
  297. tmpHashes[string(it.Value())] = it.Value()
  298. // Potentially exit early.
  299. select {
  300. case <-ctx.Done():
  301. break
  302. default:
  303. }
  304. }
  305. if err := it.Error(); err != nil {
  306. panic(err)
  307. }
  308. case c.Op == query.OpContains:
  309. // XXX: startKey does not apply here.
  310. // For example, if startKey = "account.owner/an/" and search query = "account.owner CONTAINS an"
  311. // we can't iterate with prefix "account.owner/an/" because we might miss keys like "account.owner/Ulan/"
  312. it, err := dbm.IteratePrefix(txi.store, prefixFromCompositeKey(c.CompositeKey))
  313. if err != nil {
  314. panic(err)
  315. }
  316. defer it.Close()
  317. for ; it.Valid(); it.Next() {
  318. value, err := parseValueFromKey(it.Key())
  319. if err != nil {
  320. continue
  321. }
  322. if strings.Contains(value, c.Operand.(string)) {
  323. tmpHashes[string(it.Value())] = it.Value()
  324. }
  325. // Potentially exit early.
  326. select {
  327. case <-ctx.Done():
  328. break
  329. default:
  330. }
  331. }
  332. if err := it.Error(); err != nil {
  333. panic(err)
  334. }
  335. default:
  336. panic("other operators should be handled already")
  337. }
  338. if len(tmpHashes) == 0 || firstRun {
  339. // Either:
  340. //
  341. // 1. Regardless if a previous match was attempted, which may have had
  342. // results, but no match was found for the current condition, then we
  343. // return no matches (assuming AND operand).
  344. //
  345. // 2. A previous match was not attempted, so we return all results.
  346. return tmpHashes
  347. }
  348. // Remove/reduce matches in filteredHashes that were not found in this
  349. // match (tmpHashes).
  350. for k := range filteredHashes {
  351. if tmpHashes[k] == nil {
  352. delete(filteredHashes, k)
  353. // Potentially exit early.
  354. select {
  355. case <-ctx.Done():
  356. break
  357. default:
  358. }
  359. }
  360. }
  361. return filteredHashes
  362. }
  363. // matchRange returns all matching txs by hash that meet a given queryRange and
  364. // start key. An already filtered result (filteredHashes) is provided such that
  365. // any non-intersecting matches are removed.
  366. //
  367. // NOTE: filteredHashes may be empty if no previous condition has matched.
  368. func (txi *TxIndex) matchRange(
  369. ctx context.Context,
  370. qr indexer.QueryRange,
  371. startKey []byte,
  372. filteredHashes map[string][]byte,
  373. firstRun bool,
  374. ) map[string][]byte {
  375. // A previous match was attempted but resulted in no matches, so we return
  376. // no matches (assuming AND operand).
  377. if !firstRun && len(filteredHashes) == 0 {
  378. return filteredHashes
  379. }
  380. tmpHashes := make(map[string][]byte)
  381. lowerBound := qr.LowerBoundValue()
  382. upperBound := qr.UpperBoundValue()
  383. it, err := dbm.IteratePrefix(txi.store, startKey)
  384. if err != nil {
  385. panic(err)
  386. }
  387. defer it.Close()
  388. LOOP:
  389. for ; it.Valid(); it.Next() {
  390. value, err := parseValueFromKey(it.Key())
  391. if err != nil {
  392. continue
  393. }
  394. if _, ok := qr.AnyBound().(int64); ok {
  395. v, err := strconv.ParseInt(value, 10, 64)
  396. if err != nil {
  397. continue LOOP
  398. }
  399. include := true
  400. if lowerBound != nil && v < lowerBound.(int64) {
  401. include = false
  402. }
  403. if upperBound != nil && v > upperBound.(int64) {
  404. include = false
  405. }
  406. if include {
  407. tmpHashes[string(it.Value())] = it.Value()
  408. }
  409. // XXX: passing time in a ABCI Events is not yet implemented
  410. // case time.Time:
  411. // v := strconv.ParseInt(extractValueFromKey(it.Key()), 10, 64)
  412. // if v == r.upperBound {
  413. // break
  414. // }
  415. }
  416. // Potentially exit early.
  417. select {
  418. case <-ctx.Done():
  419. break
  420. default:
  421. }
  422. }
  423. if err := it.Error(); err != nil {
  424. panic(err)
  425. }
  426. if len(tmpHashes) == 0 || firstRun {
  427. // Either:
  428. //
  429. // 1. Regardless if a previous match was attempted, which may have had
  430. // results, but no match was found for the current condition, then we
  431. // return no matches (assuming AND operand).
  432. //
  433. // 2. A previous match was not attempted, so we return all results.
  434. return tmpHashes
  435. }
  436. // Remove/reduce matches in filteredHashes that were not found in this
  437. // match (tmpHashes).
  438. for k := range filteredHashes {
  439. if tmpHashes[k] == nil {
  440. delete(filteredHashes, k)
  441. // Potentially exit early.
  442. select {
  443. case <-ctx.Done():
  444. break
  445. default:
  446. }
  447. }
  448. }
  449. return filteredHashes
  450. }
  451. // ########################## Keys #############################
  452. //
  453. // The indexer has two types of kv stores:
  454. // 1. txhash - result (primary key)
  455. // 2. event - txhash (secondary key)
  456. //
  457. // The event key can be decomposed into 4 parts.
  458. // 1. A composite key which can be any string.
  459. // Usually something like "tx.height" or "account.owner"
  460. // 2. A value. That corresponds to the key. In the above
  461. // example the value could be "5" or "Ivan"
  462. // 3. The height of the Tx that aligns with the key and value.
  463. // 4. The index of the Tx that aligns with the key and value
  464. // the hash/primary key
  465. func primaryKey(hash []byte) []byte {
  466. key, err := orderedcode.Append(
  467. nil,
  468. types.TxHashKey,
  469. string(hash),
  470. )
  471. if err != nil {
  472. panic(err)
  473. }
  474. return key
  475. }
  476. // The event/secondary key
  477. func secondaryKey(compositeKey, value string, height int64, index uint32) []byte {
  478. key, err := orderedcode.Append(
  479. nil,
  480. compositeKey,
  481. value,
  482. height,
  483. int64(index),
  484. )
  485. if err != nil {
  486. panic(err)
  487. }
  488. return key
  489. }
  490. // parseValueFromKey parses an event key and extracts out the value, returning an error if one arises.
  491. // This will also involve ensuring that the key has the correct format.
  492. // CONTRACT: function doesn't check that the prefix is correct. This should have already been done by the iterator
  493. func parseValueFromKey(key []byte) (string, error) {
  494. var (
  495. compositeKey, value string
  496. height, index int64
  497. )
  498. remaining, err := orderedcode.Parse(string(key), &compositeKey, &value, &height, &index)
  499. if err != nil {
  500. return "", err
  501. }
  502. if len(remaining) != 0 {
  503. return "", fmt.Errorf("unexpected remainder in key: %s", remaining)
  504. }
  505. return value, nil
  506. }
  507. func keyFromEvent(compositeKey string, value []byte, result *abci.TxResult) []byte {
  508. return secondaryKey(compositeKey, string(value), result.Height, result.Index)
  509. }
  510. func keyFromHeight(result *abci.TxResult) []byte {
  511. return secondaryKey(types.TxHeightKey, fmt.Sprintf("%d", result.Height), result.Height, result.Index)
  512. }
  513. // Prefixes: these represent an initial part of the key and are used by iterators to iterate over a small
  514. // section of the kv store during searches.
  515. func prefixFromCompositeKey(compositeKey string) []byte {
  516. key, err := orderedcode.Append(nil, compositeKey)
  517. if err != nil {
  518. panic(err)
  519. }
  520. return key
  521. }
  522. func prefixFromCompositeKeyAndValue(compositeKey, value string) []byte {
  523. key, err := orderedcode.Append(nil, compositeKey, value)
  524. if err != nil {
  525. panic(err)
  526. }
  527. return key
  528. }
  529. // a small utility function for getting a keys prefix based on a condition and a height
  530. func prefixForCondition(c query.Condition, height int64) []byte {
  531. key := prefixFromCompositeKeyAndValue(c.CompositeKey, fmt.Sprintf("%v", c.Operand))
  532. if height > 0 {
  533. var err error
  534. key, err = orderedcode.Append(key, height)
  535. if err != nil {
  536. panic(err)
  537. }
  538. }
  539. return key
  540. }