Browse Source

Implement merkle proofs for blockchain.block.header

patch-2
Neil Booth 7 years ago
parent
commit
46d8e0c608
  1. 22
      docs/protocol-methods.rst
  2. 16
      electrumx/server/controller.py
  3. 40
      electrumx/server/session.py
  4. 6
      tests/lib/test_merkle.py

22
docs/protocol-methods.rst

@ -34,6 +34,10 @@ Return the block header at the given height.
proof that the given header is present in the blockchain; presumably
the client has the merkle root hard-coded as a checkpoint.
* *branch*
The merkle branch of *header* up to *root*, deepest pairing first.
* *header*
The raw block header as a hexadecimal string.
@ -43,17 +47,27 @@ Return the block header at the given height.
The merkle root of all blockchain headers up to and including
*cp_height*.
* *branch*
The merkle branch of *header* up to *root*, deepest pairing first.
**Example Result**
With *cp_height* zero:
::
"0100000085144a84488ea88d221c8bd6c059da090e88f8a2c99690ee55dbba4e00000000e11c48fecdd9e72510ca84f023370c9a38bf91ac5cae88019bee94d24528526344c36649ffff001d1d03e477"
With *cp_height* 8 on the Bitcoin Cash chain::
{
"branch": [
"000000004ebadb55ee9096c9a2f8880e09da59c0d68b1c228da88e48844a1485",
"96cbbc84783888e4cc971ae8acf86dd3c1a419370336bb3c634c97695a8c5ac9",
"965ac94082cebbcffe458075651e9cc33ce703ab0115c72d9e8b1a9906b2b636",
"89e5daa6950b895190716dd26054432b564ccdc2868188ba1da76de8e1dc7591"
],
"header": "0100000085144a84488ea88d221c8bd6c059da090e88f8a2c99690ee55dbba4e00000000e11c48fecdd9e72510ca84f023370c9a38bf91ac5cae88019bee94d24528526344c36649ffff001d1d03e477",
"root": "e347b1c43fd9b5415bf0d92708db8284b78daf4d0e24f9c3405f45feb85e25db"
}
blockchain.block.headers
========================

16
electrumx/server/controller.py

@ -20,9 +20,9 @@ from functools import partial
import pylru
from aiorpcx import RPCError, TaskSet, _version as aiorpcx_version
from electrumx.lib.hash import double_sha256, hash_to_hex_str, hex_str_to_hash
from electrumx.lib.hash import hash_to_hex_str, hex_str_to_hash
from electrumx.lib.hash import HASHX_LEN
from electrumx.lib.merkle import Merkle
from electrumx.lib.merkle import Merkle, MerkleCache
from electrumx.lib.peer import Peer
from electrumx.lib.server_base import ServerBase
import electrumx.lib.util as util
@ -37,6 +37,12 @@ version_string = util.version_string
merkle = Merkle()
class HeaderSource(object):
def __init__(self, db):
self.hashes = db.fs_block_hashes
class SessionGroup(object):
def __init__(self, gid):
@ -54,7 +60,7 @@ class Controller(ServerBase):
CATCHING_UP, LISTENING, PAUSED, SHUTTING_DOWN = range(4)
PROTOCOL_MIN = '1.1'
PROTOCOL_MAX = '1.3'
PROTOCOL_MAX = '1.4'
AIORPCX_MIN = (0, 5, 6)
VERSION = VERSION
@ -236,6 +242,10 @@ class Controller(ServerBase):
synchronize, then kick off server background processes.'''
await self.bp.caught_up_event.wait()
self.logger.info('block processor has caught up')
length = max(1, self.bp.db_height - self.env.reorg_limit)
source = HeaderSource(self.bp)
self.header_mc = MerkleCache(merkle, source, length)
self.logger.info('populated header merkle cache')
self.create_task(self.mempool.main_loop())
await self.mempool.synchronized_event.wait()
self.create_task(self.peer_mgr.main_loop())

40
electrumx/server/session.py

@ -297,13 +297,35 @@ class ElectrumX(SessionBase):
hashX = self.controller.scripthash_to_hashX(scripthash)
return await self.hashX_subscribe(hashX, scripthash)
def block_header(self, height):
def block_header(self, height, cp_height=0):
'''Return a raw block header as a hexadecimal string.
height: the header's height'''
height = self.controller.non_negative_integer(height)
raw_header = self.controller.raw_header(height)
return raw_header.hex()
cp_height = self.controller.non_negative_integer(cp_height)
raw_header_hex = self.controller.raw_header(height).hex()
if cp_height == 0:
return raw_header_hex
if height > cp_height:
raise RPCError(BAD_REQUEST,
f'height {height:,d} > cp_height {cp_height:,d}')
max_height = self.height()
if cp_height > max_height:
raise RPCError(BAD_REQUEST, f'cp_height {cp_height:,d} > '
f'max height {max_height:,d}')
header_mc = self.controller.header_mc
branch, root = header_mc.branch_and_root(cp_height + 1, height)
return {
'branch': [hash_to_hex_str(elt) for elt in branch],
'header': raw_header_hex,
'root': hash_to_hex_str(root),
}
def block_header_13(self, height):
'''Return a raw block header as a hexadecimal string.
height: the header's height'''
return self.block_header(height)
def block_headers(self, start_height, count):
'''Return count concatenated block headers as hex for the main chain;
@ -477,9 +499,14 @@ class ElectrumX(SessionBase):
'server.ping': self.ping,
})
if ptuple >= (1, 3):
if ptuple >= (1, 4):
handlers.update({
'blockchain.block.header': self.block_header,
'blockchain.headers.subscribe': self.headers_subscribe,
})
elif ptuple >= (1, 3):
handlers.update({
'blockchain.block.header': self.block_header_13,
'blockchain.headers.subscribe': self.headers_subscribe_True,
})
else:
@ -496,11 +523,6 @@ class ElectrumX(SessionBase):
'blockchain.address.subscribe': self.address_subscribe,
})
if ptuple >= (1, 4):
handlers.update({
`'blockchain.headers.subscribe': self.headers_subscribe,
})
self.electrumx_handlers = handlers
def request_handler(self, method):

6
tests/lib/test_merkle.py

@ -149,10 +149,10 @@ class Source(object):
def __init__(self, length):
self._hashes = [os.urandom(32) for _ in range(length)]
def hashes(self, start, length):
def hashes(self, start, count):
assert start >= 0
assert start + length <= len(self._hashes)
return self._hashes[start: start + length]
assert start + count <= len(self._hashes)
return self._hashes[start: start + count]
def test_merkle_cache():

Loading…
Cancel
Save