Browse Source

Add raw header subscriptions.

patch-2
Neil Booth 7 years ago
parent
commit
47f65ffda2
  1. 3
      docs/protocol-changes.rst
  2. 63
      docs/protocol-methods.rst
  3. 14
      server/controller.py
  4. 24
      server/session.py

3
docs/protocol-changes.rst

@ -52,6 +52,8 @@ Changes
* :func:`blockchain.transaction.get` now has an optional parameter
*verbose*.
* :func:`blockchain.headers.subscribe` now has an optional parameter
*raw*.
New methods
-----------
@ -75,3 +77,4 @@ Deprecated methods
:func:`blockchain.scripthash.listunspent`.
* :func:`blockchain.address.subscribe`. Switch to
:func:`blockchain.scripthash.subscribe`.
* :func:`blockchain.headers.subscribe` with *raw* other than :const:`True`.

63
docs/protocol-methods.rst

@ -243,12 +243,51 @@ Subscribe to receive block headers when a new block is found.
**Signature**
.. function:: blockchain.headers.subscribe()
.. function:: blockchain.headers.subscribe(raw=False)
.. versionchanged:: 1.2
Optional *raw* parameter added.
* *raw*
:const:`False` or :const:`True`. The value :const:`False` is
deprecated.
**Result**
The coin-specific :ref:`deserialized header <deserialized header>`
of the current block chain tip.
The header of the current block chain tip. If *raw* is
:const:`True` the result is a dictionary with two members:
* *hex*
The binary header as a hexadecimal string.
* *height*
The height of the header, an integer.
If *raw* is :const:`False` the result is the coin-specific
:ref:`deserialized header <deserialized header>`.
**Example Result**
With *raw* :const:`False`::
{
"bits": 402858285,
"block_height": 520481,
"merkle_root": "8e8e932eb858fd53cf09943d7efc9a8f674dc1363010ee64907a292d2fb0c25d",
"nonce": 3288656012,
"prev_block_hash": "000000000000000000b512b5d9fc7c5746587268547c04aa92383aaea0080289",
"timestamp": 1520495819,
"version": 536870912
}
With *raw* :const:`True`::
{
"height": 520481,
"hex": "00000020890208a0ae3a3892aa047c5468725846577cfcd9b512b50000000000000000005dc2b02f2d297a9064ee103036c14d678f9afc7e3d9409cf53fd58b82e938e8ecbeca05a2d2103188ce804c4"
}
**Notifications**
@ -257,13 +296,14 @@ Subscribe to receive block headers when a new block is found.
.. function:: blockchain.headers.subscribe(header)
* *header* The coin-specific :ref:`deserialized header
<deserialized header>` of the new block chain tip.
* *header*
See **Result** above.
.. note:: should a new block arrive quickly, perhaps while the server
is still processing prior blocks, the server may only notify of the
most recent chain tip. The protocol does not guarantee notification
of all intermediate blocks.
of all intermediate block headers.
In a similar way the client must be prepared to handle chain
reorganisations. Should a re-org happen the new chain tip will not
@ -294,13 +334,6 @@ Subscribe to receive the block height when a new block is found.
.. function:: blockchain.numblocks.subscribe(height)
.. note:: should a new block arrive quickly, perhaps while the server
is still processing prior blocks, the server may only notify of the
most recent height. The protocol does not guarantee notification of
all intermediate block heights. Similarly if a chain reorganization
occurs resulting in the same chain height, the client may or may not
receive a notification.
blockchain.relayfee
-------------------
@ -724,8 +757,8 @@ Return the address paid to by a UTXO.
**Signature**
.. function:: blockchain.utxo.get_address(tx_hash, index)
*Optional in version 1.0.*
*Removed in version 1.1.*
*Optional in version 1.0. Removed in version 1.1.*
*tx_hash*

14
server/controller.py

@ -297,17 +297,21 @@ class Controller(ServerBase):
for session in self.sessions:
session.notify_peers(updates)
def electrum_header(self, height):
def raw_header(self, height):
'''Return the binary header at the given height.'''
if height in self.header_cache:
return self.header_cache[height]
header, n = self.bp.read_headers(height, 1)
if n != 1:
raise RPCError('height {:,d} out of range'.format(height))
header = self.coin.electrum_header(header, height)
self.header_cache[height] = header
return header
def electrum_header(self, height):
'''Return the deserialized header at the given height.'''
if height not in self.header_cache:
raw_header = self.raw_header(height)
self.header_cache[height] = self.coin.electrum_header(raw_header,
height)
return self.header_cache[height]
def session_delay(self, session):
priority = self.session_priority(session)
excess = max(0, priority - self.BANDS)

24
server/session.py

@ -108,6 +108,7 @@ class ElectrumX(SessionBase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.subscribe_headers = False
self.subscribe_headers_raw = False
self.subscribe_height = False
self.notified_height = None
self.max_send = self.env.max_send
@ -164,7 +165,7 @@ class ElectrumX(SessionBase):
if height_changed:
self.notified_height = height
if self.subscribe_headers:
args = (self.controller.electrum_header(height), )
args = (self.subscribe_headers_result(height), )
self.send_notification('blockchain.headers.subscribe', args)
if self.subscribe_height:
args = (height, )
@ -180,12 +181,25 @@ class ElectrumX(SessionBase):
'''Return the current flushed database height.'''
return self.bp.db_height
def headers_subscribe(self):
def assert_boolean(self, value):
'''Return param value it is boolean otherwise raise an RPCError.'''
if value in (False, True):
return value
raise RPCError('{} should be a boolean value'.format(value))
def subscribe_headers_result(self, height):
'''The result of a header subscription for the given height.'''
if self.subscribe_headers_raw:
raw_header = self.controller.raw_header(height)
return {'hex': raw_header.hex(), 'height': height}
return self.controller.electrum_header(height)
def headers_subscribe(self, raw=False):
'''Subscribe to get headers of new blocks.'''
self.subscribe_headers = True
height = self.height()
self.notified_height = height
return self.controller.electrum_header(height)
self.subscribe_headers_raw = self.assert_boolean(raw)
self.notified_height = self.height()
return self.subscribe_headers_result(self.height())
def numblocks_subscribe(self):
'''Subscribe to get height of new blocks.'''

Loading…
Cancel
Save