Implement merkle proofs for blockchain.block.header
This commit is contained in:
parent
20884170e6
commit
46d8e0c608
@ -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
|
||||
========================
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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…
Reference in New Issue
Block a user