def test_callback(): R = [] def the_callback(blockchain, ops): R.extend(ops) parent_for_0 = b'\0' * 32 # same as test_fork, above BC = BlockChain(parent_for_0) BC.add_change_callback(the_callback) ITEMS = dict((i, FakeBlock(i)) for i in range(7)) ITEMS[0] = FakeBlock(0, parent_for_0) ITEMS.update(dict((i, FakeBlock(i)) for i in range(301, 306))) ITEMS[301] = FakeBlock(301, 3) # send them all except 302 BC.add_headers((ITEMS[i] for i in ITEMS.keys() if i != 302)) # now send 302 BC.add_headers([ITEMS[302]]) expected = [("add", ITEMS[i], i) for i in range(7)] expected += [("remove", ITEMS[i], i) for i in range(6, 3, -1)] expected += [("add", ITEMS[i], i+4-301) for i in range(301, 306)] assert R == expected
def test_callback(): R = [] def the_callback(blockchain, ops): R.extend(ops) parent_for_0 = b'\0' * 32 # same as test_fork, above BC = BlockChain(parent_for_0) BC.add_change_callback(the_callback) ITEMS = dict((i, FakeBlock(i)) for i in range(7)) ITEMS[0] = FakeBlock(0, parent_for_0) ITEMS.update(dict((i, FakeBlock(i)) for i in range(301, 306))) ITEMS[301] = FakeBlock(301, 3) # send them all except 302 BC.add_headers((ITEMS[i] for i in ITEMS.keys() if i != 302)) # now send 302 BC.add_headers([ITEMS[302]]) expected = [("add", ITEMS[i], i) for i in range(7)] expected += [("remove", ITEMS[i], i) for i in range(6, 3, -1)] expected += [("add", ITEMS[i], i + 4 - 301) for i in range(301, 306)] assert R == expected
def locked_blocks_iterator(blockfile, start_info=(0, 0), cached_headers=50, batch_size=50): """ This method loads blocks from disk, skipping any orphan blocks. """ f = blockfile current_state = [] def change_state(bc, ops): for op, bh, work in ops: if op == 'add': current_state.append(bh) pass else: current_state.pop() bc = BlockChain() bc.add_change_callback(change_state) bhs = [] index = 0 info_offset = start_info while 1: v = blockfile.next_offset(info_offset) if v is None: break block_offset, info_offset = v f.jump_to(block_offset) bh = Block.parse_as_header(f) bh.info = block_offset bhs.append(bh) if len(bhs) > batch_size: bc.add_headers(bhs) bhs = [] if len(current_state) > cached_headers: for bh in current_state[:cached_headers]: bh.index = index yield bh index += 1 bc.lock_to_index(index) current_state = current_state[cached_headers:]
def locked_blocks_iterator(start_info=(0, 0), cached_headers=50, batch_size=50, base_dir=None, headers_only=False): """ This method loads blocks from disk, skipping any orphan blocks. """ block_class = BlockHeader if headers_only else Block f = Blockfiles(base_dir, start_info) for initial_location in block_info_iterator(start_info, base_dir): f.jump_to(initial_location) initial_header = BlockHeader.parse(f) break current_state = [] def change_state(bc, ops): for op, bh, work in ops: if op == 'add': current_state.append(bh) pass else: current_state.pop() bc = BlockChain() bc.add_change_callback(change_state) bhs = [] index = 0 for info in block_info_iterator(start_info, base_dir): bh = blockheader_for_offset_info(info, base_dir) bh.info = info bhs.append(bh) if len(bhs) > batch_size: bc.add_headers(bhs) bhs = [] if len(current_state) > cached_headers: for bh in current_state[:cached_headers]: f.jump_to(bh.info) block = block_class.parse(f) yield block index += 1 bc.lock_to_index(index) current_state = current_state[cached_headers:]
def locked_blocks_iterator(start_info=(0, 0), cached_headers=50, batch_size=50, base_dir=None, headers_only=False): """ This method loads blocks from disk, skipping any orphan blocks. """ parse_method = Block.parse_as_header if headers_only else Block.parse f = Blockfiles(base_dir, start_info) for initial_location in block_info_iterator(start_info, base_dir): f.jump_to(initial_location) parse_method(f) break current_state = [] def change_state(bc, ops): for op, bh, work in ops: if op == 'add': current_state.append(bh) pass else: current_state.pop() bc = BlockChain() bc.add_change_callback(change_state) bhs = [] index = 0 for info in block_info_iterator(start_info, base_dir): bh = blockheader_for_offset_info(info, base_dir) bh.info = info bhs.append(bh) if len(bhs) > batch_size: bc.add_headers(bhs) bhs = [] if len(current_state) > cached_headers: for bh in current_state[:cached_headers]: f.jump_to(bh.info) block = parse_method(f) yield block index += 1 bc.lock_to_index(index) current_state = current_state[cached_headers:]
def __init__(self, network, host_port_q, should_download_block_f, block_chain_store, blockchain_change_callback, server_port=9999): """ network: a value from pycoinnet.helpers.networks host_port_q: a Queue that is being fed potential places to connect should_download_block_f: a function that accepting(block_hash, block_index) and returning a boolean indicating whether that block should be downloaded. Only used during fast-forward. block_chain_store: usually a BlockChainStore instance blockchain_change_callback: a callback that expects (blockchain, list_of_ops) that is invoked whenever the block chain is updated; blockchain is a BlockChain object and list_of_ops is a pair of tuples of the form (op, block_hash, block_index) where op is one of "add" or "remove", block_hash is a binary block hash, and block_index is an integer index number. """ block_chain = BlockChain( did_lock_to_index_f=block_chain_store.did_lock_to_index) block_chain.preload_locked_blocks(block_chain_store.headers()) block_chain.add_change_callback(block_chain_locker_callback) self.blockfetcher = Blockfetcher() self.inv_collector = InvCollector() self.block_store = TwoLevelDict() @asyncio.coroutine def _rotate(block_store): while True: block_store.rotate() yield from asyncio.sleep(1800) self.rotate_task = asyncio.Task(_rotate(self.block_store)) self.blockhandler = BlockHandler( self.inv_collector, block_chain, self.block_store, should_download_f=should_download_block_f) block_chain.add_change_callback(blockchain_change_callback) self.fast_forward_add_peer = fast_forwarder_add_peer_f(block_chain) self.fetcher_task = asyncio.Task( new_block_fetcher(self.inv_collector, block_chain)) self.nonce = int.from_bytes(os.urandom(8), byteorder="big") self.subversion = "/Notoshi/".encode("utf8") @asyncio.coroutine def run_peer(peer, fetcher, fast_forward_add_peer, blockfetcher, inv_collector, blockhandler): yield from asyncio.wait_for(peer.connection_made_future, timeout=None) version_parameters = version_data_for_peer( peer, local_port=(server_port or 0), last_block_index=block_chain.length(), nonce=self.nonce, subversion=self.subversion) version_data = yield from initial_handshake( peer, version_parameters) last_block_index = version_data["last_block_index"] fast_forward_add_peer(peer, last_block_index) blockfetcher.add_peer(peer, fetcher, last_block_index) inv_collector.add_peer(peer) blockhandler.add_peer(peer) def create_protocol_callback(): peer = BitcoinPeerProtocol(network["MAGIC_HEADER"]) install_pingpong_manager(peer) fetcher = Fetcher(peer) peer.add_task( run_peer(peer, fetcher, self.fast_forward_add_peer, self.blockfetcher, self.inv_collector, self.blockhandler)) return peer self.connection_info_q = manage_connection_count( host_port_q, create_protocol_callback, 8) self.show_task = asyncio.Task( show_connection_info(self.connection_info_q)) # listener @asyncio.coroutine def run_listener(): abstract_server = None future_peer = asyncio.Future() try: abstract_server = yield from asyncio.get_event_loop( ).create_server(protocol_factory=create_protocol_callback, port=server_port) return abstract_server except OSError as ex: logging.info("can't listen on port %d", server_port) if server_port: self.server_task = asyncio.Task(run_listener())
def __init__(self, network, host_port_q, should_download_block_f, block_chain_store, blockchain_change_callback, server_port=9999): """ network: a value from pycoinnet.helpers.networks host_port_q: a Queue that is being fed potential places to connect should_download_block_f: a function that accepting(block_hash, block_index) and returning a boolean indicating whether that block should be downloaded. Only used during fast-forward. block_chain_store: usually a BlockChainStore instance blockchain_change_callback: a callback that expects (blockchain, list_of_ops) that is invoked whenever the block chain is updated; blockchain is a BlockChain object and list_of_ops is a pair of tuples of the form (op, block_hash, block_index) where op is one of "add" or "remove", block_hash is a binary block hash, and block_index is an integer index number. """ block_chain = BlockChain(did_lock_to_index_f=block_chain_store.did_lock_to_index) block_chain.preload_locked_blocks(block_chain_store.headers()) block_chain.add_change_callback(block_chain_locker_callback) self.blockfetcher = Blockfetcher() self.inv_collector = InvCollector() self.block_store = TwoLevelDict() @asyncio.coroutine def _rotate(block_store): while True: block_store.rotate() yield from asyncio.sleep(1800) self.rotate_task = asyncio.Task(_rotate(self.block_store)) self.blockhandler = BlockHandler(self.inv_collector, block_chain, self.block_store, should_download_f=should_download_block_f) block_chain.add_change_callback(blockchain_change_callback) self.fast_forward_add_peer = fast_forwarder_add_peer_f(block_chain) self.fetcher_task = asyncio.Task(new_block_fetcher(self.inv_collector, block_chain)) self.nonce = int.from_bytes(os.urandom(8), byteorder="big") self.subversion = "/Notoshi/".encode("utf8") @asyncio.coroutine def run_peer(peer, fetcher, fast_forward_add_peer, blockfetcher, inv_collector, blockhandler): yield from asyncio.wait_for(peer.connection_made_future, timeout=None) version_parameters = version_data_for_peer( peer, local_port=(server_port or 0), last_block_index=block_chain.length(), nonce=self.nonce, subversion=self.subversion) version_data = yield from initial_handshake(peer, version_parameters) last_block_index = version_data["last_block_index"] fast_forward_add_peer(peer, last_block_index) blockfetcher.add_peer(peer, fetcher, last_block_index) inv_collector.add_peer(peer) blockhandler.add_peer(peer) def create_protocol_callback(): peer = BitcoinPeerProtocol(network["MAGIC_HEADER"]) install_pingpong_manager(peer) fetcher = Fetcher(peer) peer.add_task(run_peer( peer, fetcher, self.fast_forward_add_peer, self.blockfetcher, self.inv_collector, self.blockhandler)) return peer self.connection_info_q = manage_connection_count(host_port_q, create_protocol_callback, 8) self.show_task = asyncio.Task(show_connection_info(self.connection_info_q)) # listener @asyncio.coroutine def run_listener(): abstract_server = None try: abstract_server = yield from asyncio.get_event_loop().create_server( protocol_factory=create_protocol_callback, port=server_port) return abstract_server except OSError: logging.info("can't listen on port %d", server_port) if server_port: self.server_task = asyncio.Task(run_listener())