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
|
proof that the given header is present in the blockchain; presumably
|
||||||
the client has the merkle root hard-coded as a checkpoint.
|
the client has the merkle root hard-coded as a checkpoint.
|
||||||
|
|
||||||
|
* *branch*
|
||||||
|
|
||||||
|
The merkle branch of *header* up to *root*, deepest pairing first.
|
||||||
|
|
||||||
* *header*
|
* *header*
|
||||||
|
|
||||||
The raw block header as a hexadecimal string.
|
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
|
The merkle root of all blockchain headers up to and including
|
||||||
*cp_height*.
|
*cp_height*.
|
||||||
|
|
||||||
* *branch*
|
|
||||||
|
|
||||||
The merkle branch of *header* up to *root*, deepest pairing first.
|
|
||||||
|
|
||||||
|
|
||||||
**Example Result**
|
**Example Result**
|
||||||
|
|
||||||
|
With *cp_height* zero:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
"0100000085144a84488ea88d221c8bd6c059da090e88f8a2c99690ee55dbba4e00000000e11c48fecdd9e72510ca84f023370c9a38bf91ac5cae88019bee94d24528526344c36649ffff001d1d03e477"
|
"0100000085144a84488ea88d221c8bd6c059da090e88f8a2c99690ee55dbba4e00000000e11c48fecdd9e72510ca84f023370c9a38bf91ac5cae88019bee94d24528526344c36649ffff001d1d03e477"
|
||||||
|
|
||||||
|
With *cp_height* 8 on the Bitcoin Cash chain::
|
||||||
|
|
||||||
|
{
|
||||||
|
"branch": [
|
||||||
|
"000000004ebadb55ee9096c9a2f8880e09da59c0d68b1c228da88e48844a1485",
|
||||||
|
"96cbbc84783888e4cc971ae8acf86dd3c1a419370336bb3c634c97695a8c5ac9",
|
||||||
|
"965ac94082cebbcffe458075651e9cc33ce703ab0115c72d9e8b1a9906b2b636",
|
||||||
|
"89e5daa6950b895190716dd26054432b564ccdc2868188ba1da76de8e1dc7591"
|
||||||
|
],
|
||||||
|
"header": "0100000085144a84488ea88d221c8bd6c059da090e88f8a2c99690ee55dbba4e00000000e11c48fecdd9e72510ca84f023370c9a38bf91ac5cae88019bee94d24528526344c36649ffff001d1d03e477",
|
||||||
|
"root": "e347b1c43fd9b5415bf0d92708db8284b78daf4d0e24f9c3405f45feb85e25db"
|
||||||
|
}
|
||||||
|
|
||||||
blockchain.block.headers
|
blockchain.block.headers
|
||||||
========================
|
========================
|
||||||
|
|||||||
@ -20,9 +20,9 @@ from functools import partial
|
|||||||
import pylru
|
import pylru
|
||||||
|
|
||||||
from aiorpcx import RPCError, TaskSet, _version as aiorpcx_version
|
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.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.peer import Peer
|
||||||
from electrumx.lib.server_base import ServerBase
|
from electrumx.lib.server_base import ServerBase
|
||||||
import electrumx.lib.util as util
|
import electrumx.lib.util as util
|
||||||
@ -37,6 +37,12 @@ version_string = util.version_string
|
|||||||
merkle = Merkle()
|
merkle = Merkle()
|
||||||
|
|
||||||
|
|
||||||
|
class HeaderSource(object):
|
||||||
|
|
||||||
|
def __init__(self, db):
|
||||||
|
self.hashes = db.fs_block_hashes
|
||||||
|
|
||||||
|
|
||||||
class SessionGroup(object):
|
class SessionGroup(object):
|
||||||
|
|
||||||
def __init__(self, gid):
|
def __init__(self, gid):
|
||||||
@ -54,7 +60,7 @@ class Controller(ServerBase):
|
|||||||
|
|
||||||
CATCHING_UP, LISTENING, PAUSED, SHUTTING_DOWN = range(4)
|
CATCHING_UP, LISTENING, PAUSED, SHUTTING_DOWN = range(4)
|
||||||
PROTOCOL_MIN = '1.1'
|
PROTOCOL_MIN = '1.1'
|
||||||
PROTOCOL_MAX = '1.3'
|
PROTOCOL_MAX = '1.4'
|
||||||
AIORPCX_MIN = (0, 5, 6)
|
AIORPCX_MIN = (0, 5, 6)
|
||||||
VERSION = VERSION
|
VERSION = VERSION
|
||||||
|
|
||||||
@ -236,6 +242,10 @@ class Controller(ServerBase):
|
|||||||
synchronize, then kick off server background processes.'''
|
synchronize, then kick off server background processes.'''
|
||||||
await self.bp.caught_up_event.wait()
|
await self.bp.caught_up_event.wait()
|
||||||
self.logger.info('block processor has caught up')
|
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())
|
self.create_task(self.mempool.main_loop())
|
||||||
await self.mempool.synchronized_event.wait()
|
await self.mempool.synchronized_event.wait()
|
||||||
self.create_task(self.peer_mgr.main_loop())
|
self.create_task(self.peer_mgr.main_loop())
|
||||||
|
|||||||
@ -297,13 +297,35 @@ class ElectrumX(SessionBase):
|
|||||||
hashX = self.controller.scripthash_to_hashX(scripthash)
|
hashX = self.controller.scripthash_to_hashX(scripthash)
|
||||||
return await self.hashX_subscribe(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.
|
'''Return a raw block header as a hexadecimal string.
|
||||||
|
|
||||||
height: the header's height'''
|
height: the header's height'''
|
||||||
height = self.controller.non_negative_integer(height)
|
height = self.controller.non_negative_integer(height)
|
||||||
raw_header = self.controller.raw_header(height)
|
cp_height = self.controller.non_negative_integer(cp_height)
|
||||||
return raw_header.hex()
|
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):
|
def block_headers(self, start_height, count):
|
||||||
'''Return count concatenated block headers as hex for the main chain;
|
'''Return count concatenated block headers as hex for the main chain;
|
||||||
@ -477,9 +499,14 @@ class ElectrumX(SessionBase):
|
|||||||
'server.ping': self.ping,
|
'server.ping': self.ping,
|
||||||
})
|
})
|
||||||
|
|
||||||
if ptuple >= (1, 3):
|
if ptuple >= (1, 4):
|
||||||
handlers.update({
|
handlers.update({
|
||||||
'blockchain.block.header': self.block_header,
|
'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,
|
'blockchain.headers.subscribe': self.headers_subscribe_True,
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
@ -496,11 +523,6 @@ class ElectrumX(SessionBase):
|
|||||||
'blockchain.address.subscribe': self.address_subscribe,
|
'blockchain.address.subscribe': self.address_subscribe,
|
||||||
})
|
})
|
||||||
|
|
||||||
if ptuple >= (1, 4):
|
|
||||||
handlers.update({
|
|
||||||
`'blockchain.headers.subscribe': self.headers_subscribe,
|
|
||||||
})
|
|
||||||
|
|
||||||
self.electrumx_handlers = handlers
|
self.electrumx_handlers = handlers
|
||||||
|
|
||||||
def request_handler(self, method):
|
def request_handler(self, method):
|
||||||
|
|||||||
@ -149,10 +149,10 @@ class Source(object):
|
|||||||
def __init__(self, length):
|
def __init__(self, length):
|
||||||
self._hashes = [os.urandom(32) for _ in range(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 >= 0
|
||||||
assert start + length <= len(self._hashes)
|
assert start + count <= len(self._hashes)
|
||||||
return self._hashes[start: start + length]
|
return self._hashes[start: start + count]
|
||||||
|
|
||||||
|
|
||||||
def test_merkle_cache():
|
def test_merkle_cache():
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user