github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/docs/notifications.md (about) 1 # Notification subsystem 2 3 Original motivation, requirements and general solution strategy are described 4 in the [issue #895](https://github.com/nspcc-dev/neo-go/issues/895). 5 6 This extension allows a websocket client to subscribe to various events and 7 receive them as JSON-RPC notifications from the server. 8 9 ## Events 10 Currently supported events: 11 * new block added 12 13 Contents: block. Filters: primary ID, since/till block indexes. 14 * new transaction in the block 15 16 Contents: transaction. Filters: sender and signer. 17 * notification generated during execution 18 19 Contents: container hash, contract hash, notification name, stack item. Filters: contract hash, notification name. 20 * transaction/persisting script executed 21 22 Contents: application execution result. Filters: VM state, script container hash. 23 * new/removed P2P notary request (if `P2PSigExtensions` are enabled) 24 25 Contents: P2P notary request. Filters: request sender and main tx signer. 26 27 Filters use conjunctional logic. 28 29 ## Ordering and persistence guarantees 30 * new block and header of this block are only announced after block's processing 31 is complete and the chain is updated to the new height 32 * no disk-level persistence guarantees are given 33 * header of newly added block is announced after block processing, but before 34 announcing the block itself 35 * new in-block transaction is announced after block processing, but before 36 announcing the block header and the block itself 37 * transaction notifications are only announced for successful transactions 38 * all announcements are being done in the same order they happen on the chain. 39 First, OnPersist script execution is announced followed by notifications generated 40 during the script execution. After that transaction execution is announced. It is 41 then followed by notifications generated during this execution. Next, follows the 42 transaction announcement. Transaction announcements are ordered the same way 43 they're in the block. After all in-block transactions announcements PostPersist 44 script execution is announced followed by notifications generated during the 45 script execution. Finally, block header is announced followed by the block 46 announcement itself. 47 * notary request events announcements are not bound to the chain processing. 48 Trigger for notary request notifications is notary request mempool content 49 change, thus, notary request event is announced every time notary request 50 enters or leaves notary pool. 51 * unsubscription may not cancel pending, but not yet sent events 52 53 ## Subscription management 54 55 To receive events, clients need to subscribe to them first via `subscribe` 56 method. Upon successful subscription, clients receive subscription ID for 57 subsequent management of this subscription. Subscription is only valid for 58 connection lifetime, no long-term client identification is being made. 59 60 Errors are not described down below, but they can be returned as standard 61 JSON-RPC errors (most often caused by invalid parameters). 62 63 ### `subscribe` method 64 65 Parameters: event stream name, stream-specific filter rules hash (can be 66 omitted if empty). 67 68 Recognized stream names: 69 * `block_added` 70 Filter: `primary` as an integer with a valid range of 0-255 with 71 primary (speaker) node index from ConsensusData and/or `since` field as 72 an integer value with block index starting from which new block 73 notifications will be received and/or `till` field as an integer values 74 containing block index till which new block notifications will be received. 75 * `header_of_added_block` 76 Filter: `primary` as an integer with primary (speaker) node index from 77 ConsensusData and/or `since` field as an integer value with header 78 index starting from which new header notifications will be received and/or 79 `till` field as an integer values containing header index till which new 80 header notifications will be received. 81 * `transaction_added` 82 Filter: `sender` field containing a string with hex-encoded Uint160 (LE 83 representation) for transaction's `Sender` and/or `signer` in the same 84 format for one of transaction's `Signers`. 85 * `notification_from_execution` 86 Filter: `contract` field containing a string with hex-encoded Uint160 (LE 87 representation) and/or `name` field containing a string with execution 88 notification name which should be a valid UTF-8 string not longer than 89 32 bytes. 90 * `transaction_executed` 91 Filter: `state` field containing `HALT` or `FAULT` string for successful 92 and failed executions respectively and/or `container` field containing 93 script container (block/transaction) hash. 94 * `notary_request_event` 95 Filter: `sender` field containing a string with hex-encoded Uint160 (LE 96 representation) for notary request's `Sender` and/or `signer` in the same 97 format for one of main transaction's `Signers`. `type` field containing a 98 string with event type, which could be one of "added" or "removed". 99 100 Response: returns subscription ID (string) as a result. This ID can be used to 101 cancel this subscription and has no meaning other than that. 102 103 Example request (subscribe to notifications from contract 104 0x6293a440ed80a427038e175a507d3def1e04fb67 generated when executing 105 transactions): 106 107 ``` 108 { 109 "jsonrpc": "2.0", 110 "method": "subscribe", 111 "params": ["notification_from_execution", {"contract": "6293a440ed80a427038e175a507d3def1e04fb67"}], 112 "id": 1 113 } 114 115 ``` 116 117 Example response: 118 119 ``` 120 { 121 "jsonrpc": "2.0", 122 "id": 1, 123 "result": "55aaff00" 124 } 125 ``` 126 127 ### `unsubscribe` method 128 129 Parameters: subscription ID as a string. 130 131 Response: boolean true. 132 133 Example request (unsubscribe from "55aaff00"): 134 135 ``` 136 { 137 "jsonrpc": "2.0", 138 "method": "unsubscribe", 139 "params": ["55aaff00"], 140 "id": 1 141 } 142 ``` 143 144 Example response: 145 146 ``` 147 { 148 "jsonrpc": "2.0", 149 "id": 1, 150 "result": true 151 } 152 ``` 153 154 ## Events 155 156 Events are sent as JSON-RPC notifications from the server with `method` field 157 being used for notification names. Notification names are identical to stream 158 names described for `subscribe` method with one important addition for 159 `event_missed`, which can be sent for any subscription to signify that some 160 events have not been delivered (usually when a client is unable to keep up with 161 the event flow). 162 163 Verbose responses for various structures like blocks and transactions are used 164 to simplify working with notifications on the client side. Returned structures 165 mostly follow the one used by standard Neo RPC calls but may have some minor 166 differences. 167 168 If a server-side event matches several subscriptions from one client, it's 169 only sent once. 170 171 ### `block_added` notification 172 173 The first parameter (`params` section) contains a block converted to a JSON 174 structure, which is similar to a verbose `getblock` response but with the 175 following differences: 176 * it doesn't have `size` field (you can calculate it client-side) 177 * it doesn't have `nextblockhash` field (it's supposed to be the latest one 178 anyway) 179 * it doesn't have `confirmations` field (see previous) 180 181 No other parameters are sent. 182 183 Example: 184 ``` 185 { 186 "params" : [ 187 { 188 "index" : 207, 189 "time" : 1590006200, 190 "nextconsensus" : "AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL", 191 "consensusdata" : { 192 "primary" : 0, 193 "nonce" : "0000000000000457" 194 }, 195 "previousblockhash" : "0x04f7580b111ec75f0ce68d3a9fd70a0544b4521b4a98541694d8575c548b759e", 196 "witnesses" : [ 197 { 198 "invocation" : "0c4063429fca5ff75c964d9e38179c75978e33f8174d91a780c2e825265cf2447281594afdd5f3e216dcaf5ff0693aec83f415996cf224454495495f6bd0a4c5d08f0c4099680903a954278580d8533121c2cd3e53a089817b6a784901ec06178a60b5f1da6e70422bdcadc89029767e08d66ce4180b99334cb2d42f42e4216394af15920c4067d5e362189e48839a24e187c59d46f5d9db862c8a029777f1548b19632bfdc73ad373827ed02369f925e89c2303b64e6b9838dca229949b9b9d3bd4c0c3ed8f0c4021d4c00d4522805883f1db929554441bcbbee127c48f6b7feeeb69a72a78c7f0a75011663e239c0820ef903f36168f42936de10f0ef20681cb735a4b53d0390f", 199 "verification" : "130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb" 200 } 201 ], 202 "version" : 0, 203 "hash" : "0x239fea00c54c2f6812612874183b72bef4473fcdf68bf8da08d74fd5b6cab030", 204 "tx" : [ 205 { 206 "txid" : "0xf736cd91ab84062a21a09b424346b241987f6245ffe8c2b2db39d595c3c222f7", 207 "witnesses" : [ 208 { 209 "verification" : "0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4", 210 "invocation" : "0c4016e7a112742409cdfaad89dcdbcb52c94c5c1a69dfe5d8b999649eaaa787e31ca496d1734d6ea606c749ad36e9a88892240ae59e0efa7f544e0692124898d512" 211 } 212 ], 213 "vout" : [], 214 "cosigners" : [], 215 "validuntilblock" : 1200, 216 "nonce" : 8, 217 "netfee" : "0.0030421", 218 "sender" : "ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG", 219 "sysfee" : "0", 220 "type" : "InvocationTransaction", 221 "attributes" : [], 222 "version" : 1, 223 "vin" : [], 224 "size" : 204, 225 "script" : "10c00c04696e69740c14769162241eedf97c2481652adf1ba0f5bf57431b41627d5b52" 226 }, 227 { 228 "script" : "01e8030c14316e851039019d39dfc2c37d6c3fee19fd5809870c14769162241eedf97c2481652adf1ba0f5bf57431b13c00c087472616e736665720c14769162241eedf97c2481652adf1ba0f5bf57431b41627d5b5238", 229 "size" : 277, 230 "attributes" : [], 231 "version" : 1, 232 "vin" : [], 233 "netfee" : "0.0037721", 234 "sender" : "ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG", 235 "sysfee" : "0", 236 "type" : "InvocationTransaction", 237 "nonce" : 9, 238 "signers" : [ 239 { 240 "scopes" : 1, 241 "account" : "0x870958fd19ee3f6c7dc3c2df399d013910856e31" 242 } 243 ], 244 "validuntilblock" : 1200, 245 "witnesses" : [ 246 { 247 "invocation" : "0c4027727296b84853c5d9e07fb8a40e885246ae25641383b16eefbe92027ecb1635b794aacf6bbfc3e828c73829b14791c483d19eb758b57638e3191393dbf2d288", 248 "verification" : "0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4" 249 } 250 ], 251 "vout" : [], 252 "txid" : "0xe1cd5e57e721d2a2e05fb1f08721b12057b25ab1dd7fd0f33ee1639932fdfad7" 253 } 254 ], 255 "merkleroot" : "0xb2c7230ebee4cb83bc03afadbba413e6bca8fcdeaf9c077bea060918da0e52a1" 256 } 257 ], 258 "jsonrpc" : "2.0", 259 "method" : "block_added" 260 } 261 ``` 262 263 ### `header_of_added_block` notification 264 265 The first parameter (`params` section) contains a header of added block 266 converted to a JSON structure, which is similar to a verbose 267 `getblockheader` response but with the following differences: 268 * it doesn't have `size` field (you can calculate it client-side) 269 * it doesn't have `nextblockhash` field (it's supposed to be the latest 270 one anyway) 271 * it doesn't have `confirmations` field (see previous) 272 273 No other parameters are sent. 274 275 Example: 276 ``` 277 { 278 "jsonrpc": "2.0", 279 "method": "header_of_added_block", 280 "params": [ 281 { 282 "index" : 207, 283 "time" : 1590006200, 284 "nextconsensus" : "AXSvJVzydxXuL9da4GVwK25zdesCrVKkHL", 285 "consensusdata" : { 286 "primary" : 0, 287 "nonce" : "0000000000000457" 288 }, 289 "previousblockhash" : "0x04f7580b111ec75f0ce68d3a9fd70a0544b4521b4a98541694d8575c548b759e", 290 "witnesses" : [ 291 { 292 "invocation" : "0c4063429fca5ff75c964d9e38179c75978e33f8174d91a780c2e825265cf2447281594afdd5f3e216dcaf5ff0693aec83f415996cf224454495495f6bd0a4c5d08f0c4099680903a954278580d8533121c2cd3e53a089817b6a784901ec06178a60b5f1da6e70422bdcadc89029767e08d66ce4180b99334cb2d42f42e4216394af15920c4067d5e362189e48839a24e187c59d46f5d9db862c8a029777f1548b19632bfdc73ad373827ed02369f925e89c2303b64e6b9838dca229949b9b9d3bd4c0c3ed8f0c4021d4c00d4522805883f1db929554441bcbbee127c48f6b7feeeb69a72a78c7f0a75011663e239c0820ef903f36168f42936de10f0ef20681cb735a4b53d0390f", 293 "verification" : "130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb" 294 } 295 ], 296 "version" : 0, 297 "hash" : "0x239fea00c54c2f6812612874183b72bef4473fcdf68bf8da08d74fd5b6cab030", 298 "merkleroot" : "0xb2c7230ebee4cb83bc03afadbba413e6bca8fcdeaf9c077bea060918da0e52a1" 299 } 300 ] 301 } 302 ``` 303 304 ### `transaction_added` notification 305 306 The first parameter (`params` section) contains a transaction converted to 307 JSON, which is similar to a verbose `getrawtransaction` response, but with the 308 following differences: 309 * block's metadata is missing (`blockhash`, `confirmations`, `blocktime`) 310 311 No other parameters are sent. 312 313 Example: 314 ``` 315 { 316 "method" : "transaction_added", 317 "params" : [ 318 { 319 "validuntilblock" : 1200, 320 "version" : 1, 321 "txid" : "0xe1cd5e57e721d2a2e05fb1f08721b12057b25ab1dd7fd0f33ee1639932fdfad7", 322 "witnesses" : [ 323 { 324 "invocation" : "0c4027727296b84853c5d9e07fb8a40e885246ae25641383b16eefbe92027ecb1635b794aacf6bbfc3e828c73829b14791c483d19eb758b57638e3191393dbf2d288", 325 "verification" : "0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b410a906ad4" 326 } 327 ], 328 "sysfee" : "0", 329 "sender" : "ALHF9wsXZVEuCGgmDA6ZNsCLtrb4A1g4yG", 330 "vout" : [], 331 "netfee" : "0.0037721", 332 "size" : 277, 333 "attributes" : [], 334 "script" : "01e8030c14316e851039019d39dfc2c37d6c3fee19fd5809870c14769162241eedf97c2481652adf1ba0f5bf57431b13c00c087472616e736665720c14769162241eedf97c2481652adf1ba0f5bf57431b41627d5b5238", 335 "nonce" : 9, 336 "vin" : [], 337 "type" : "InvocationTransaction", 338 "signers" : [ 339 { 340 "account" : "0x870958fd19ee3f6c7dc3c2df399d013910856e31", 341 "scopes" : 1 342 } 343 ] 344 } 345 ], 346 "jsonrpc" : "2.0" 347 } 348 ``` 349 350 ### `notification_from_execution` notification 351 352 Contains four parameters: container hash (block's or transaction's hex-encoded LE 353 Uint256 hash in a string), contract hash (hex-encoded LE Uint160 in a string), 354 notification name and stack item (encoded the same way as `state` field contents 355 for notifications from `getapplicationlog` response). 356 357 Example: 358 359 ``` 360 { 361 "jsonrpc" : "2.0", 362 "method" : "notification_from_execution", 363 "params" : [ 364 { 365 "state" : { 366 "value" : [ 367 { 368 "value" : "636f6e74726163742063616c6c", 369 "type" : "ByteString" 370 }, 371 { 372 "value" : "7472616e73666572", 373 "type" : "ByteString" 374 }, 375 { 376 "value" : [ 377 { 378 "value" : "769162241eedf97c2481652adf1ba0f5bf57431b", 379 "type" : "ByteString" 380 }, 381 { 382 "value" : "316e851039019d39dfc2c37d6c3fee19fd580987", 383 "type" : "ByteString" 384 }, 385 { 386 "value" : "1000", 387 "type" : "Integer" 388 } 389 ], 390 "type" : "Array" 391 } 392 ], 393 "type" : "Array" 394 }, 395 "contract" : "0x1b4357bff5a01bdf2a6581247cf9ed1e24629176", 396 "name" : "transfer", 397 "container" : "0xe1cd5e57e721d2a2e05fb1f08721b12057b25ab1dd7fd0f33ee1639932fdfad7", 398 } 399 ] 400 } 401 ``` 402 403 ### `transaction_executed` notification 404 405 It contains the same result as from `getapplicationlog` method in the first 406 parameter and no other parameters. The difference from `getapplicationlog` is 407 that it has block's or transaction's hex-encoded LE Uint256 hash in the `container` 408 field instead of two separate `txid` and `blockhash` fields and a single execution 409 instead of an executions array. 410 411 Example: 412 ``` 413 { 414 "method" : "transaction_executed", 415 "params" : [ 416 { 417 "container" : "0xe1cd5e57e721d2a2e05fb1f08721b12057b25ab1dd7fd0f33ee1639932fdfad7", 418 "trigger" : "Application", 419 "gasconsumed" : "2.291", 420 "stack" : [], 421 "notifications" : [ 422 { 423 "state" : { 424 "type" : "Array", 425 "value" : [ 426 { 427 "value" : "636f6e74726163742063616c6c", 428 "type" : "ByteString" 429 }, 430 { 431 "type" : "ByteString", 432 "value" : "7472616e73666572" 433 }, 434 { 435 "value" : [ 436 { 437 "value" : "769162241eedf97c2481652adf1ba0f5bf57431b", 438 "type" : "ByteString" 439 }, 440 { 441 "type" : "ByteString", 442 "value" : "316e851039019d39dfc2c37d6c3fee19fd580987" 443 }, 444 { 445 "value" : "1000", 446 "type" : "Integer" 447 } 448 ], 449 "type" : "Array" 450 } 451 ] 452 }, 453 "contract" : "0x1b4357bff5a01bdf2a6581247cf9ed1e24629176" 454 }, 455 { 456 "contract" : "0x1b4357bff5a01bdf2a6581247cf9ed1e24629176", 457 "state" : { 458 "value" : [ 459 { 460 "value" : "7472616e73666572", 461 "type" : "ByteString" 462 }, 463 { 464 "value" : "769162241eedf97c2481652adf1ba0f5bf57431b", 465 "type" : "ByteString" 466 }, 467 { 468 "value" : "316e851039019d39dfc2c37d6c3fee19fd580987", 469 "type" : "ByteString" 470 }, 471 { 472 "value" : "1000", 473 "type" : "Integer" 474 } 475 ], 476 "type" : "Array" 477 } 478 } 479 ], 480 "vmstate" : "HALT" 481 } 482 ], 483 "jsonrpc" : "2.0" 484 } 485 ``` 486 487 ### `notary_request_event` notification 488 489 It contains two parameters: event type, which could be one of "added" or "removed", and 490 added (or removed) notary request. 491 492 Example: 493 494 ``` 495 { 496 "jsonrpc" : "2.0", 497 "method" : "notary_request_event", 498 "params" : [ 499 { 500 "notaryrequest" : { 501 "Witness" : { 502 "verification" : "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBVuezJw==", 503 "invocation" : "DECWLkFhNqBMCewLxjAWiXXA1YE/GmX6EWmIRM17F9lwwpXyWtzp+hkxvJNWHpDlslDvpXizGiB/YBd05kadXlSv" 504 }, 505 "fallbacktx" : { 506 "validuntilblock" : 115, 507 "attributes" : [ 508 { 509 "type" : "NotValidBefore", 510 "height" : 65 511 }, 512 { 513 "type" : "Conflicts", 514 "hash" : "0x03c564ed28ba3d50beb1a52dcb751b929e1d747281566bd510363470be186bc0" 515 }, 516 { 517 "type" : "NotaryAssisted", 518 "nkeys" : 0 519 } 520 ], 521 "sender" : "NRNp25VPHahL3umVxBcMLuEENGZR9cHxtc", 522 "size" : 291, 523 "netfee" : "200000000", 524 "witnesses" : [ 525 { 526 "invocation" : "DEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 527 "verification" : "" 528 }, 529 { 530 "invocation" : "DEBnVePpwnsM54K72RmxZR8cWTGxQveJ1cAdd3/zQUh6KVDnj+G5F8AI6gYlbnEK5qJwP40WfGWlmy3A8mYHGVLm", 531 "verification" : "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBVuezJw==" 532 } 533 ], 534 "nonce" : 0, 535 "sysfee" : "0", 536 "signers" : [ 537 { 538 "scopes" : "None", 539 "account" : "0xc1e14f19c3e60d0b9244d06dd7ba9b113135ec3b" 540 }, 541 { 542 "account" : "0xb248508f4ef7088e10c48f14d04be3272ca29eee", 543 "scopes" : "None" 544 } 545 ], 546 "version" : 0, 547 "hash" : "0x5eb5f89d04648d43ba7563130e8bfd1710392ab97cba8e35857aed4206db3643", 548 "script" : "QA==" 549 }, 550 "maintx" : { 551 "sender" : "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn", 552 "attributes" : [ 553 { 554 "nkeys" : 1, 555 "type" : "NotaryAssisted" 556 } 557 ], 558 "validuntilblock" : 115, 559 "witnesses" : [ 560 { 561 "invocation" : "AQQH", 562 "verification" : "AwYJ" 563 } 564 ], 565 "netfee" : "0", 566 "size" : 62, 567 "version" : 0, 568 "signers" : [ 569 { 570 "scopes" : "None", 571 "account" : "0xb248508f4ef7088e10c48f14d04be3272ca29eee" 572 } 573 ], 574 "sysfee" : "0", 575 "nonce" : 1, 576 "script" : "QA==", 577 "hash" : "0x03c564ed28ba3d50beb1a52dcb751b929e1d747281566bd510363470be186bc0" 578 } 579 }, 580 "type" : "added" 581 } 582 ] 583 } 584 ``` 585 586 ### `event_missed` notification 587 588 Never has any parameters. Example: 589 590 ``` 591 { 592 "jsonrpc": "2.0", 593 "method": "event_missed", 594 "params": [] 595 } 596 ```