github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/docs/source/architecture/events_and_transactions_receipts.rst (about) 1 ******************************* 2 Events and Transaction Receipts 3 ******************************* 4 5 Hyperledger Sawtooth supports creating and broadcasting events. 6 This allows applications to do the following: 7 8 - Subscribe to events that occur related to the blockchain, such 9 as a new block being committed or switching to a new fork. 10 - Subscribe to application specific events defined by a transaction family. 11 - Relay information about the execution of a transaction back to 12 clients without storing that data in state. 13 14 .. image:: ../images/event_subsystem.* 15 :width: 80% 16 :align: center 17 :alt: Event Subsystem 18 19 .. _events-reference-label: 20 21 Events 22 ====== 23 24 Events are represented with the following protobuf message: 25 26 .. code-block:: protobuf 27 28 message Event { 29 // Used to subscribe to events and servers as a hint for how to deserialize 30 // event_data and what pairs to expect in attributes. 31 string event_type = 1; 32 33 // Transparent data defined by the event_type. 34 message Attribute { 35 string key = 1; 36 string value = 2; 37 } 38 repeated Attribute attributes = 2; 39 40 // Opaque data defined by the event_type. 41 bytes data = 3; 42 } 43 44 45 Events are extracted from other data structures such as blocks or 46 transaction receipts. In order to treat this extraction uniformly, an 47 EventExtractor interface is implemented for each event source. The 48 EventExtractor interface takes a list of EventSubscriptions and will only 49 generate events that are in the union of all subscriptions. An event is "in a 50 subscription" if the event's event_type field matches the subscription's 51 event_type field and the filter's key-value pair (if the subscription has 52 any filters) matches a key-value pair in the event's attribute field. 53 54 .. code-block:: python 55 56 interface EventExtractor: 57 // Construct all the events of interest by taking the union of all subscriptions. 58 // One extractor should be created for each input source that events can be 59 // extracted from. This input source should be passed to the implementation through 60 // the constructor. 61 extract(list<EventSubscription> subscriptions) -> list<Event> 62 63 // If no subscriptions of a given event_type are passed to EventExtractor.extract, 64 // the extractor does not need to return events of that type. 65 class EventSubscription: 66 string event_type 67 list<EventFilter> filters 68 69 The following filters are implemented: 70 71 SIMPLE_ANY 72 Represents a subset of events within an event type. 73 74 Since multiple event attributes with the same key can be present in an 75 event, an event is considered part of this filter if its match string matches 76 the value of ANY attribute with the filter's key. 77 78 For example, if an event has the following attributes: 79 80 - Attribute(key="address", value="abc") 81 - Attribute(key="address", value="def") 82 83 it will pass the following filter: 84 85 SimpleAnyFilter(key="address", match_string"abc") 86 87 Because it matches one of the two attributes with the key "address". 88 89 SIMPLE_ALL 90 Represents a subset of events within an event type. 91 92 Since multiple event attributes with the same key can be present in an 93 event, an event is considered part of this filter if its match string matches 94 the value of ALL attribute with the filter's key. 95 96 For example, if an event has the following attributes: 97 98 - Attribute(key="address", value="abc") 99 - Attribute(key="address", value="def") 100 101 it will NOT pass this filter: 102 103 SimpleAllFilter(key="address", value="abc") 104 105 Because it does not match all attributes with the key "address". 106 107 REGEX_ANY 108 Represents a subset of events within an event type. Pattern must be a 109 valid regular expression that can be compiled by the re module. 110 111 Since multiple event attributes with the same key can be present in an 112 event, an event is considered part of this filter if its pattern matches 113 the value of ANY attribute with the filter's key. 114 115 For example, if an event has the following attributes: 116 117 - Attribute(key="address", value="abc") 118 - Attribute(key="address", value="def") 119 120 it will pass the following filter: 121 122 AnyRegexFilter(key="address", value="abc") 123 124 Because it matches one of the two attributes with the key "address". 125 126 REGEX_ALL 127 Represents a subset of events within an event type. Pattern must be a 128 valid regular expression that can be compiled by the re module. 129 130 Since multiple event attributes with the same key can be present in an 131 event, an event is considered part of this filter if its pattern matches 132 the value of ALL attribute with the filter's key. 133 134 For example, if an event has the following attributes: 135 136 - Attribute(key="address", value="abc") 137 - Attribute(key="address", value="def") 138 139 it will NOT pass this filter: 140 141 AllRegexFilter(key="address", value="abc") 142 143 Because it does not match all attributes with the key "address". 144 145 An EventBroadcaster manages external event subscriptions and forwards events to 146 subscribers as they occur. In order for the EventBroadcaster to learn about 147 events, the ChainController class implements the Observer pattern. The 148 ChainController acts as the subject and observers of the ChainController 149 implement the ChainObserver interface. 150 151 .. code-block:: python 152 153 interface ChainObserver: 154 // This method is called by the ChainController on block boundaries. 155 chain_update(Block block, list<TransactionReceipt> receipts) 156 157 class EventBroadcaster: 158 // Register the subscriber for the given event subscriptions and begin sending it 159 // events on block boundaries. 160 // 161 // If any of the block ids in last_known_block_ids are part of the current chain, 162 // the observer will be notified of all events that it would have received based on 163 // its subscriptions for each block in the chain since the most recent 164 // block in last_known_block_ids. 165 // 166 // Raises an exception if: 167 // 1. The subscription is unsuccessful. 168 // 2. None of the block ids in last_known_block_ids are part of the current chain. 169 add_subscriber(string connection_id, list<EventSubscription> subscriptions, 170 list<string> last_known_block_ids) 171 172 // Stop sending events to the subscriber 173 remove_subscriber(string connection_id) 174 175 // Notify all observers of all events they are subscribed to. 176 chain_update(Block block, list<TransactionReceipt> receipts) 177 178 179 On receiving a chain_update() notification from the ChainController, the 180 EventBroadcaster instantiates a new EventExtractor, passes each extractor all 181 the EventSubscriptions for all subscribers, and receives the list of events 182 that is the union of the events that all subscribers are interested in. The 183 EventBroadcaster then distributes the events to each subscriber based on 184 the subscriber's list of subscriptions. 185 186 To reduce the number of messages sent to subscribers, multiple Event messages 187 are wrapped in an EventList message when possible: 188 189 .. code-block:: python 190 191 EventList { 192 repeated Event events = 1; 193 } 194 195 ClientEventSubscribeRequest messages are sent by external clients to the 196 validator in order to subscribe to events. ClientEventSubscribeResponse messages 197 are sent by the validator to the client in response to notify the client whether 198 their subscription was successful. When an external client subscribes to events, 199 they may optionally send a list of block ids along with their subscriptions. If 200 any of the blocks sent are in the current chain, the EventBroadcaster will bring 201 the client up to date by sending it events for all blocks since the most recent 202 block sent with the subscribe request. 203 204 .. code-block:: protobuf 205 206 message ClientEventsSubscribeRequest { 207 repeated EventSubscription subscriptions = 1; 208 // The block id (or ids, if trying to walk back a fork) the subscriber last 209 // received events on. It can be set to empty if it has not yet received the 210 // genesis block. 211 repeated string last_known_block_ids = 2; 212 } 213 214 message ClientEventsSubscribeResponse { 215 enum Status { 216 OK = 0; 217 INVALID_FILTER = 1; 218 UNKNOWN_BLOCK = 2; 219 } 220 Status status = 1; 221 // Additional information about the response status 222 string response_message = 2; 223 } 224 225 Event Extractors 226 ---------------- 227 228 Two event extractors are created to extract events from blocks and 229 transaction receipts: BlockEventExtractor and ReceiptEventExtractor. The 230 BlockEventExtractor will extract events of type "sawtooth/block-commit". The 231 ReceiptEventExtractor will extract events of type "sawtooth/state-delta" and 232 events defined by transaction families. 233 234 Example events generated by BlockEventExtractor: 235 236 .. code-block:: protobuf 237 238 // Example sawtooth/block-commit event 239 Event { 240 event_type = "sawtooth/block-commit", 241 attributes = [ 242 Attribute { key = "block_id", value = "abc...123" }, 243 Attribute { key = "block_num", value = "523" }, 244 Attribute { key = "state_root_hash", value = "def...456" }, 245 Attribute { key = "previous_block_id", value = "acf...146" }, 246 ], 247 } 248 249 Example events generated by ReceiptEventExtractor: 250 251 .. code-block:: protobuf 252 253 254 // Example transaction family specific event 255 Event { 256 event_type = "xo/create", 257 attributes = [Attribute { key = "game_name", value = "game0" }], 258 } 259 260 // Example sawtooth/block-commit event 261 Event { 262 event_type = "sawtooth/state-delta", 263 attributes = [Attribute { key = "address", value = "abc...def" }], 264 event_data = <bytes> 265 } 266 267 268 Transaction Receipts 269 ==================== 270 271 Transaction receipts provide clients with information that is related to the 272 execution of a transaction but should not be stored in state, such as: 273 274 - Whether the transaction was valid 275 - How the transaction changed state 276 - Events of interest that occurred during execution of the transaction 277 - Other transaction-family-specific execution information 278 279 Transaction receipts can also provide the validator with information about 280 transaction execution without re-executing the transaction. 281 282 Sawtooth transaction receipts are represented as a protobuf message when exposed 283 to clients. A transaction receipt contains a list of StateChange messages, a 284 list of Event messages, and a list of TransactionReceipt.Data messages: 285 286 .. code-block:: protobuf 287 288 message TransactionReceipt { 289 // State changes made by this transaction 290 // StateChange is already defined in protos/state_delta.proto 291 repeated StateChange state_changes = 1; 292 // Events fired by this transaction 293 repeated Event events = 2; 294 // Transaction family defined data 295 repeated bytes data = 3; 296 297 string transaction_id = 4; 298 } 299 300 The fields in the TransactionReceipt.Data are opaque to Sawtooth and their 301 interpretation is left up to the transaction family. TransactionReceipt.Data 302 can be requested for a transaction that has been committed and should only be 303 used to store information about a transaction’s execution that should not be 304 kept in state. Clients can use the event system described above to subscribe to 305 events produced by transaction processors. 306 307 Transaction Receipt Store 308 ------------------------- 309 310 The TransactionReceiptStore class stores receipts. New receipts are written to 311 the TransactionReceiptStore after a block is validated. 312 313 Transaction receipts will only be stored in this off-chain store and will not 314 be included in the block. Note that because a transaction may exist in multiple 315 blocks at a time, the transaction receipt is stored by both transaction id and 316 block state root hash. 317 318 .. code-block:: python 319 320 class TransactionReceiptStore: 321 def put_receipt(self, txn_id, receipt): 322 """Add the given transaction receipt to the store. Does not guarantee 323 it has been written to the backing store. 324 325 Args: 326 txn_id (str): the id of the transaction being stored. 327 state_root_hash: the state root of the block this transaction was 328 executed in. 329 receipt (TransactionReceipt): the receipt object to store. 330 """ 331 332 def get_receipt(self, txn_id): 333 """Returns the TransactionReceipt 334 335 Args: 336 txn_id (str): the id of the transaction for which the receipt 337 should be retrieved. 338 state_root_hash: the state root of the block this transaction was 339 executed in. 340 341 Returns: 342 TransactionReceipt: The receipt for the given transaction id. 343 344 Raises: 345 KeyError: if the transaction id is unknown. 346 """ 347 348 Message Handlers 349 ---------------- 350 351 Once transaction receipts are stored in the TransactionReceiptStore, clients 352 can request a transaction receipt for a given transaction id. 353 354 .. code-block:: protobuf 355 356 // Fetches a specific txn by its id (header_signature) from the blockchain. 357 message ClientReceiptGetRequest { 358 repeated string transaction_ids = 1; 359 } 360 361 // A response that returns the txn receipt specified by a 362 // ClientReceiptGetRequest. 363 // 364 // Statuses: 365 // * OK - everything worked as expected, txn receipt has been fetched 366 // * INTERNAL_ERROR - general error, such as protobuf failing to deserialize 367 // * NO_RESOURCE - no receipt exists for the transaction id specified 368 message ClientReceiptGetResponse { 369 enum Status { 370 OK = 0; 371 INTERNAL_ERROR = 1; 372 NO_RESOURCE = 4; 373 } 374 Status status = 1; 375 repeated TransactionReceipt receipts = 2; 376 377 .. Licensed under Creative Commons Attribution 4.0 International License 378 .. https://creativecommons.org/licenses/by/4.0/