def setUp(self): self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) address = 'HC7w4j7mPet49BBN5a2An3XUiPvK6C1TL7' self.client = HathorClientTest(server_url='') self.loop.run_until_complete(self.client.start()) self.manager = TxMiningManager(backend=self.client, address=address) self.loop.run_until_complete(self.manager.start()) self.loop.run_until_complete(self.manager.wait_for_block_template()) self.assertTrue(len(self.manager.block_template) > 0)
def setUp(self): address = 'HC7w4j7mPet49BBN5a2An3XUiPvK6C1TL7' from tests.utils import Clock self.clock = Clock(self.loop) self.clock.enable() self.client = HathorClientTest(server_url='') self.loop.run_until_complete(self.client.start()) self.manager = TxMiningManager(backend=self.client, address=address) self.loop.run_until_complete(self.manager.start()) self.loop.run_until_complete(self.manager.wait_for_block_template()) self.assertTrue(len(self.manager.block_template) > 0)
def test_invalid_mining_address(self): from hathorlib.exceptions import InvalidAddress address = 'HC7w4j7mPet49BBN5a2An3XUiPvK6C1TL7' invalid_addresses = [ ('Invalid base58', address[:-1] + 'I'), # No 'I' in base58 symbols. ('Invalid checksum', address[:-1] + 'A'), ('Invalid size (smaller)', address[:-1]), ('Invalid size (bigger)', address + '7'), ] for idx, (cause, invalid_address) in enumerate(invalid_addresses): with self.assertRaises(InvalidAddress): print('Address #{}: {} ({})'.format(idx, cause, invalid_address)) TxMiningManager(backend=self.client, address=invalid_address)
def setUp(self): self.manager = TxMiningManager(backend=None, address=None) self.tmpdir = tempfile.mkdtemp()
async def get_application(self): self.manager = TxMiningManager(backend=None, address=None) self.myapp = App(self.manager) return self.myapp.app
def execute(args: Namespace) -> None: """Run the service according to the args.""" from hathorlib.client import HathorClient from txstratum.api import App from txstratum.manager import TxMiningManager from txstratum.utils import start_logging # Configure log. start_logging() if os.path.exists(args.log_config): logging.config.fileConfig(args.log_config) from structlog.stdlib import LoggerFactory structlog.configure(logger_factory=LoggerFactory()) logger.info('tx-mining-service', backend=args.backend) logger.info('Configuring log...', log_config=args.log_config) else: logger.info('tx-mining-service', backend=args.backend) logger.info('Log config file not found; using default configuration.', log_config=args.log_config) # Set up all parts. loop = asyncio.get_event_loop() backend = HathorClient(args.backend) manager = TxMiningManager( backend=backend, address=args.address, ) loop.run_until_complete(backend.start()) loop.run_until_complete(manager.start()) server = loop.run_until_complete( loop.create_server(manager, '0.0.0.0', args.stratum_port)) if args.prometheus: from txstratum.prometheus import PrometheusExporter metrics = PrometheusExporter(manager, args.prometheus) metrics.start() api_app = App(manager, max_tx_weight=args.max_tx_weight, max_timestamp_delta=args.max_timestamp_delta, tx_timeout=args.tx_timeout, fix_invalid_timestamp=args.fix_invalid_timestamp) logger.info('API Configuration', max_tx_weight=api_app.max_tx_weight, tx_timeout=api_app.tx_timeout, max_timestamp_delta=api_app.max_timestamp_delta, fix_invalid_timestamp=api_app.fix_invalid_timestamp) web_runner = web.AppRunner(api_app.app) loop.run_until_complete(web_runner.setup()) site = web.TCPSite(web_runner, '0.0.0.0', args.api_port) loop.run_until_complete(site.start()) try: logger.info('Stratum Server running at 0.0.0.0:{}...'.format( args.stratum_port)) logger.info('TxMining API running at 0.0.0.0:{}...'.format( args.api_port)) if args.testnet: logger.info('Running with testnet config file') loop.run_forever() except KeyboardInterrupt: logger.info('Stopping...') server.close() loop.run_until_complete(server.wait_closed()) loop.run_until_complete(backend.stop()) loop.close()
class ManagerClockedTestCase(asynctest.ClockedTestCase): # type: ignore def setUp(self): address = 'HC7w4j7mPet49BBN5a2An3XUiPvK6C1TL7' from tests.utils import Clock self.clock = Clock(self.loop) self.clock.enable() self.client = HathorClientTest(server_url='') self.loop.run_until_complete(self.client.start()) self.manager = TxMiningManager(backend=self.client, address=address) self.loop.run_until_complete(self.manager.start()) self.loop.run_until_complete(self.manager.wait_for_block_template()) self.assertTrue(len(self.manager.block_template) > 0) def tearDown(self): self.clock.disable() async def test_block_timestamp_update(self): job = self.manager.get_best_job(None) self.assertTrue(True, job.is_block) job.update_timestamp(force=True) self.assertEqual(int(txstratum.time.time()), job._block.timestamp) # Update timestamp. await self.advance(10) job.update_timestamp() self.assertEqual(int(txstratum.time.time()), job._block.timestamp) # Do not update timestamp. old_ts = txstratum.time.time() await self.advance(40) job.update_timestamp() self.assertEqual(int(old_ts), job._block.timestamp) async def test_tx_resubmit(self): job1 = TxJob(TX1_DATA, timeout=10) ret1 = self.manager.add_job(job1) self.assertTrue(ret1) # When a similar job is submitted, manager declines it. job2 = TxJob(TX1_DATA) ret2 = self.manager.add_job(job2) self.assertFalse(ret2) # Wait until job1 is marked as timeout. await self.advance(15) self.assertEqual(job1.status, JobStatus.TIMEOUT) # Try to resubmit a similar job. job3 = TxJob(TX1_DATA) ret3 = self.manager.add_job(job3) self.assertTrue(ret3) async def test_tx_timeout_and_cleanup(self): job1 = TxJob(TX1_DATA, timeout=10) ret1 = self.manager.add_job(job1) self.assertTrue(ret1) self.assertIn(job1, self.manager.tx_queue) self.assertIn(job1.uuid, self.manager.tx_jobs) # Wait until job1 is marked as timeout. await self.advance(15) self.assertEqual(job1.status, JobStatus.TIMEOUT) self.assertNotIn(job1, self.manager.tx_queue) self.assertIn(job1.uuid, self.manager.tx_jobs) # Wait until job1 is cleared. await self.advance(self.manager.TX_CLEAN_UP_INTERVAL) self.assertNotIn(job1, self.manager.tx_queue) self.assertNotIn(job1.uuid, self.manager.tx_jobs) async def test_tx_race_condition(self): """Test race condition caused when job2 replaces job1 and job1's clean up is close to be executed. In this case job1's clean up was cleaning job2 instead. """ job1 = TxJob(TX1_DATA, timeout=10) ret1 = self.manager.add_job(job1) self.assertTrue(ret1) # Wait until job1 is marked as timeout. await self.advance(10) self.assertEqual(job1.status, JobStatus.TIMEOUT) self.assertNotIn(job1, self.manager.tx_queue) self.assertIn(job1.uuid, self.manager.tx_jobs) # We are 1 second away to cleanup the tx. await self.advance(self.manager.TX_CLEAN_UP_INTERVAL - 1) # Resubmit a similar job. job2 = TxJob(TX1_DATA, timeout=10) ret2 = self.manager.add_job(job2) self.assertTrue(ret2) self.assertIn(job2, self.manager.tx_queue) self.assertIn(job2.uuid, self.manager.tx_jobs) # Reach the cleanup time of job1. await self.advance(2) self.assertEqual(job2.status, JobStatus.ENQUEUED) self.assertIn(job2, self.manager.tx_queue) self.assertIn(job2.uuid, self.manager.tx_jobs) # Job2 timeouts. await self.advance(15) self.assertEqual(job2.status, JobStatus.TIMEOUT)
class ManagerTestCase(unittest.TestCase): def setUp(self): self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) address = 'HC7w4j7mPet49BBN5a2An3XUiPvK6C1TL7' self.client = HathorClientTest(server_url='') self.loop.run_until_complete(self.client.start()) self.manager = TxMiningManager(backend=self.client, address=address) self.loop.run_until_complete(self.manager.start()) self.loop.run_until_complete(self.manager.wait_for_block_template()) self.assertTrue(len(self.manager.block_template) > 0) def _run_all_pending_events(self): """Run all pending events.""" # pending = asyncio.all_tasks(self.loop) # self.loop.run_until_complete(asyncio.gather(*pending)) async def _fn(): pass future = asyncio.ensure_future(_fn()) self.loop.run_until_complete(future) def test_invalid_mining_address(self): from hathorlib.exceptions import InvalidAddress address = 'HC7w4j7mPet49BBN5a2An3XUiPvK6C1TL7' invalid_addresses = [ ('Invalid base58', address[:-1] + 'I'), # No 'I' in base58 symbols. ('Invalid checksum', address[:-1] + 'A'), ('Invalid size (smaller)', address[:-1]), ('Invalid size (bigger)', address + '7'), ] for idx, (cause, invalid_address) in enumerate(invalid_addresses): with self.assertRaises(InvalidAddress): print('Address #{}: {} ({})'.format(idx, cause, invalid_address)) TxMiningManager(backend=self.client, address=invalid_address) def test_miner_connect_disconnect(self): conn = StratumProtocol(self.manager) conn.connection_made(transport=None) self.assertEqual(1, len(self.manager.connections)) self.assertEqual(0, len(self.manager.miners)) conn.connection_lost(exc=None) self.assertEqual(0, len(self.manager.connections)) self.assertEqual(0, len(self.manager.miners)) def test_miner_connect_ready_disconnect(self): conn = StratumProtocol(self.manager) transport = Mock() conn.connection_made(transport=transport) self.assertEqual(1, len(self.manager.connections)) self.assertEqual(0, len(self.manager.miners)) conn.method_subscribe(params=None, msgid=None) conn.method_authorize(params=None, msgid=None) self.assertEqual(1, len(self.manager.miners)) conn.connection_lost(exc=None) self.assertEqual(0, len(self.manager.connections)) self.assertEqual(0, len(self.manager.miners)) def test_many_miners_connect_ready_disconnect(self, qty=5): transport = Mock() connections = [] for idx in range(qty): conn = StratumProtocol(self.manager) conn.connection_made(transport=transport) self.assertEqual(idx + 1, len(self.manager.connections)) self.assertEqual(0, len(self.manager.miners)) connections.append(conn) self.assertEqual(qty, len(self.manager.connections)) self.assertEqual(0, len(self.manager.miners)) for idx, conn in enumerate(connections): conn.method_subscribe(params=None, msgid=None) conn.method_authorize(params=None, msgid=None) self.assertEqual(idx + 1, len(self.manager.miners)) self.assertEqual(qty, len(self.manager.connections)) self.assertEqual(qty, len(self.manager.miners)) self.manager.status() for idx, conn in enumerate(connections): conn.connection_lost(exc=None) self.assertEqual(qty - idx - 1, len(self.manager.connections)) self.assertEqual(qty - idx - 1, len(self.manager.miners)) self.assertEqual(0, len(self.manager.connections)) self.assertEqual(0, len(self.manager.miners)) def test_miner_some_jsonrpc_methods(self): conn = StratumProtocol(self.manager) conn.connection_made(transport=None) conn.send_result = MagicMock(return_value=None) conn.method_extranonce_subscribe(params=None, msgid=None) conn.send_result.assert_called_with(None, True) conn.send_result = MagicMock(return_value=None) conn.method_multi_version(params=None, msgid=None) conn.send_result.assert_called_with(None, True) def test_miner_method_subscribe_invalid_address1(self): conn = StratumProtocol(self.manager) transport = Mock() conn.connection_made(transport=transport) conn.send_error = MagicMock(return_value=None) params = { 'address': 'abc!' } conn.method_subscribe(params=params, msgid=None) conn.send_error.assert_called_once() transport.close.assert_called_once() def test_miner_method_subscribe_invalid_address2(self): conn = StratumProtocol(self.manager) transport = Mock() conn.connection_made(transport=transport) conn.send_error = MagicMock(return_value=None) params = { 'address': 'ZiCa' } conn.method_subscribe(params=params, msgid=None) conn.send_error.assert_called_once() transport.close.assert_called_once() def test_miner_method_subscribe_invalid_address3(self): conn = StratumProtocol(self.manager) transport = Mock() conn.connection_made(transport=transport) conn.send_error = MagicMock(return_value=None) params = { 'address': 'HVZjvL1FJ23kH3buGNuttVRsRKq66WHXXX' } conn.method_subscribe(params=params, msgid=None) conn.send_error.assert_called_once() transport.close.assert_called_once() def test_miner_method_subscribe_valid_address(self): conn = StratumProtocol(self.manager) transport = Mock() conn.connection_made(transport=transport) conn.send_error = MagicMock(return_value=None) params = { 'address': 'HVZjvL1FJ23kH3buGNuttVRsRKq66WHUVZ' } conn.method_subscribe(params=params, msgid=None) conn.send_error.assert_not_called() transport.close.assert_not_called() def _get_ready_miner(self, address: Optional[str] = None) -> StratumProtocol: conn = StratumProtocol(self.manager) conn._update_job_timestamp = False transport = Mock() conn.connection_made(transport=transport) if address: params = {'address': address} else: params = {} conn.method_subscribe(params=params, msgid=None) conn.method_authorize(params=None, msgid=None) return conn def test_miner_invalid_address(self): conn = StratumProtocol(self.manager) conn.send_error = MagicMock(return_value=None) transport = Mock() conn.connection_made(transport=transport) params = {'address': 'X'} conn.method_subscribe(params=params, msgid=None) conn.send_error.assert_called_once_with(None, conn.INVALID_ADDRESS) def test_miner_only_blocks_submit_failed_1(self): conn = self._get_ready_miner('HVZjvL1FJ23kH3buGNuttVRsRKq66WHUVZ') self.assertIsNotNone(conn.current_job) self.assertTrue(conn.current_job.is_block) conn.send_error = MagicMock(return_value=None) conn.method_submit(params={}, msgid=None) conn.send_error.assert_called_once_with(None, conn.INVALID_PARAMS, ANY) def test_miner_only_blocks_submit_failed_2(self): conn = self._get_ready_miner('HVZjvL1FJ23kH3buGNuttVRsRKq66WHUVZ') self.assertIsNotNone(conn.current_job) self.assertTrue(conn.current_job.is_block) params = { 'job_id': 'abc!', 'nonce': '123', } conn.send_error = MagicMock(return_value=None) conn.method_submit(params=params, msgid=None) conn.send_error.assert_called_once_with(None, conn.INVALID_PARAMS, ANY) def test_miner_only_blocks_submit_failed_3(self): conn = self._get_ready_miner('HVZjvL1FJ23kH3buGNuttVRsRKq66WHUVZ') self.assertIsNotNone(conn.current_job) self.assertTrue(conn.current_job.is_block) params = { 'job_id': 'ffff', 'nonce': '123', } conn.send_error = MagicMock(return_value=None) conn.method_submit(params=params, msgid=None) conn.send_error.assert_called_once_with(None, conn.JOB_NOT_FOUND) def test_miner_only_blocks_submit_failed_4(self): conn = self._get_ready_miner('HVZjvL1FJ23kH3buGNuttVRsRKq66WHUVZ') self.assertIsNotNone(conn.current_job) self.assertTrue(conn.current_job.is_block) params = { 'job_id': conn.current_job.uuid.hex(), 'nonce': 'FFZZ', } conn.send_error = MagicMock(return_value=None) conn.method_submit(params=params, msgid=None) conn.send_error.assert_called_once_with(None, conn.INVALID_PARAMS, ANY) def test_miner_only_blocks_submit_failed_5(self): conn = self._get_ready_miner('HVZjvL1FJ23kH3buGNuttVRsRKq66WHUVZ') self.assertIsNotNone(conn.current_job) self.assertTrue(conn.current_job.is_block) params = { 'job_id': conn.current_job.uuid.hex(), 'nonce': '123', } conn.send_error = MagicMock(return_value=None) conn.method_submit(params=params, msgid=None) conn.send_error.assert_called_once_with(None, conn.INVALID_SOLUTION) def test_miner_only_blocks_submit(self): conn = self._get_ready_miner() self.assertIsNotNone(conn.current_job) self.assertTrue(conn.current_job.is_block) self.assertEqual(0, conn.current_job.height) # First submission: success params = { 'job_id': conn.current_job.uuid.hex(), 'nonce': '00000000000000000000000000278a7e', } conn.send_error = MagicMock(return_value=None) conn.send_result = MagicMock(return_value=None) conn.method_submit(params=params, msgid=None) conn.send_error.assert_not_called() conn.send_result.assert_called_once_with(None, 'ok') # Second submission: stale job conn.send_error = MagicMock(return_value=None) conn.send_result = MagicMock(return_value=None) conn.method_submit(params=params, msgid=None) conn.send_error.assert_called_once_with(None, conn.STALE_JOB, ANY) conn.send_result.assert_not_called() self._run_all_pending_events() self.loop.run_until_complete(self.manager.update_block_template()) self.assertEqual(1, conn.current_job.height) # conn.connection_lost(exc=None) # self.loop.run_until_complete(self.manager.stop()) def test_miner_only_blocks_update_block(self): conn = self._get_ready_miner() self.assertIsNotNone(conn.current_job) self.assertTrue(conn.current_job.is_block) self.assertEqual(0, conn.current_job.height) # Hathor full node returned a new block template. self.client.next_block_template() self.loop.run_until_complete(self.manager.update_block_template()) self._run_all_pending_events() self.assertEqual(1, conn.current_job.height) def test_two_miners_same_submission_1(self): conn1 = self._get_ready_miner() conn2 = self._get_ready_miner() self.assertEqual(0, conn1.current_job.height) self.assertEqual(0, conn2.current_job.height) # First submission: success params = { 'job_id': conn1.current_job.uuid.hex(), 'nonce': '00000000000000000000000000278a7e', } conn1.send_error = MagicMock(return_value=None) conn1.send_result = MagicMock(return_value=None) self.manager.backend.push_tx_or_block = MagicMock(return_value=asyncio.Future()) conn1.method_submit(params=params, msgid=None) conn1.send_error.assert_not_called() conn1.send_result.assert_called_once_with(None, 'ok') self.manager.backend.push_tx_or_block.assert_called_once() # As the main loop is not running, the jobs have not been updated yet. # Second submission: success, but it won't be propagated. conn2.send_error = MagicMock(return_value=None) conn2.send_result = MagicMock(return_value=None) self.manager.backend.push_tx_or_block = MagicMock(return_value=asyncio.Future()) conn2.method_submit(params=params, msgid=None) conn1.send_error.assert_not_called() conn1.send_result.assert_called_once_with(None, 'ok') self.manager.backend.push_tx_or_block.assert_not_called() def test_two_miners_same_submission_2(self): conn1 = self._get_ready_miner() conn2 = self._get_ready_miner() self.assertEqual(0, conn1.current_job.height) self.assertEqual(0, conn2.current_job.height) params1 = { 'job_id': conn1.current_job.uuid.hex(), 'nonce': '00000000000000000000000000278a7e', } params2 = { 'job_id': conn2.current_job.uuid.hex(), 'nonce': '00000000000000000000000000278a7e', } # First submission: success conn1.send_error = MagicMock(return_value=None) conn1.send_result = MagicMock(return_value=None) conn1.method_submit(params=params1, msgid=None) conn1.send_error.assert_not_called() conn1.send_result.assert_called_once_with(None, 'ok') # Run the main loop to update the jobs. self._run_all_pending_events() self.loop.run_until_complete(self.manager.update_block_template()) self.assertEqual(1, conn1.current_job.height) self.assertEqual(1, conn2.current_job.height) # As jobs have been updated, the submission from the second miner will be accepted but not propagated. # Second submission: success and not propagated. conn2.send_error = MagicMock(return_value=None) conn2.send_result = MagicMock(return_value=None) self.manager.backend.push_tx_or_block = MagicMock(return_value=asyncio.Future()) conn2.method_submit(params=params2, msgid=None) conn1.send_error.assert_not_called() conn1.send_result.assert_called_once_with(None, 'ok') self.manager.backend.push_tx_or_block.assert_not_called() def _run_basic_tx_tests(self, conn, tx_data, tx_nonce): job = TxJob(tx_data) ret = self.manager.add_job(job) self.assertFalse(conn.current_job.is_block) self.assertEqual(conn.current_job.tx_job, job) self.assertTrue(ret) # First submission: wrong nonce params = { 'job_id': conn.current_job.uuid.hex(), 'nonce': '84e20000', } conn.send_error = MagicMock(return_value=None) conn.send_result = MagicMock(return_value=None) conn.method_submit(params=params, msgid=None) conn.send_error.assert_called_once_with(None, conn.INVALID_SOLUTION) conn.send_result.assert_not_called() self.assertFalse(conn.current_job.is_block) # Second submission: success params = { 'job_id': conn.current_job.uuid.hex(), 'nonce': tx_nonce, } conn.send_error = MagicMock(return_value=None) conn.send_result = MagicMock(return_value=None) conn.method_submit(params=params, msgid=None) conn.send_error.assert_not_called() conn.send_result.assert_called_once_with(None, 'ok') # Third submission: stale conn.send_error = MagicMock(return_value=None) conn.send_result = MagicMock(return_value=None) conn.method_submit(params=params, msgid=None) conn.send_error.assert_called_once_with(None, conn.STALE_JOB, ANY) conn.send_result.assert_not_called() def test_one_miner_one_tx(self): conn = self._get_ready_miner('HVZjvL1FJ23kH3buGNuttVRsRKq66WHUVZ') self.assertIsNotNone(conn.current_job) self.assertTrue(conn.current_job.is_block) self.assertEqual(0, conn.current_job.height) self._run_basic_tx_tests(conn, TX1_DATA, TX1_NONCE) # Run loop and check that the miner gets a block self._run_all_pending_events() self.assertTrue(conn.current_job.is_block) self.assertEqual(0, conn.current_job.height) def test_one_miner_two_txs(self): conn = self._get_ready_miner('HVZjvL1FJ23kH3buGNuttVRsRKq66WHUVZ') self.assertIsNotNone(conn.current_job) self.assertTrue(conn.current_job.is_block) self.assertEqual(0, conn.current_job.height) job1 = TxJob(TX1_DATA) job2 = TxJob(TX2_DATA) ret1 = self.manager.add_job(job1) ret2 = self.manager.add_job(job2) self.assertFalse(conn.current_job.is_block) self.assertEqual(conn.current_job.tx_job, job1) self.assertTrue(ret1) self.assertTrue(ret2) # First submission: success params = { 'job_id': conn.current_job.uuid.hex(), 'nonce': TX1_NONCE, } conn.send_error = MagicMock(return_value=None) conn.send_result = MagicMock(return_value=None) conn.method_submit(params=params, msgid=None) conn.send_error.assert_not_called() conn.send_result.assert_called_once_with(None, 'ok') # Run loop and check that the miner gets the next tx self._run_all_pending_events() self.assertFalse(conn.current_job.is_block) self.assertEqual(conn.current_job.tx_job, job2) # First submission: success params = { 'job_id': conn.current_job.uuid.hex(), 'nonce': TX2_NONCE, } conn.send_error = MagicMock(return_value=None) conn.send_result = MagicMock(return_value=None) conn.method_submit(params=params, msgid=None) conn.send_error.assert_not_called() conn.send_result.assert_called_once_with(None, 'ok') # Run loop and check that the miner gets a block self._run_all_pending_events() self.assertTrue(conn.current_job.is_block) self.assertEqual(0, conn.current_job.height) def test_mining_tx_connection_lost(self): conn1 = self._get_ready_miner('HVZjvL1FJ23kH3buGNuttVRsRKq66WHUVZ') self.assertIsNotNone(conn1.current_job) self.assertTrue(conn1.current_job.is_block) self.assertEqual(0, conn1.current_job.height) conn2 = self._get_ready_miner('HVZjvL1FJ23kH3buGNuttVRsRKq66WHUVZ') self.assertIsNotNone(conn2.current_job) self.assertTrue(conn2.current_job.is_block) self.assertEqual(0, conn2.current_job.height) job = TxJob(TX1_DATA) ret = self.manager.add_job(job) self.assertTrue(ret) self.assertFalse(conn1.current_job.is_block) self.assertEqual(conn1.current_job.tx_job, job) self.assertEqual(conn2.current_job.tx_job, job) # Miner 1 disconnects. conn1.connection_lost(exc=None) self.assertFalse(conn2.current_job.is_block) self.assertEqual(conn2.current_job.tx_job, job) # Miner 2 disconnects. Tx stays on the queue. conn2.connection_lost(exc=None) self.assertEqual(deque([job]), self.manager.tx_queue) # Miner 3 connects. Tx is sent to the new miner. conn3 = self._get_ready_miner('HVZjvL1FJ23kH3buGNuttVRsRKq66WHUVZ') self.assertFalse(conn3.current_job.is_block) self.assertEqual(conn3.current_job.tx_job, job) def test_token_creation_tx(self): conn = self._get_ready_miner('HVZjvL1FJ23kH3buGNuttVRsRKq66WHUVZ') self.assertIsNotNone(conn.current_job) self.assertTrue(conn.current_job.is_block) self.assertEqual(0, conn.current_job.height) self._run_basic_tx_tests(conn, TOKEN_CREATION_TX_DATA, TOKEN_CREATION_TX_NONCE) # Run loop and check that the miner gets a block self._run_all_pending_events() self.assertTrue(conn.current_job.is_block) self.assertEqual(0, conn.current_job.height) def test_no_miners_at_start(self): from txstratum.constants import DEFAULT_EXPECTED_MINING_TIME expected_queue_time = 0 job1 = TxJob(TX1_DATA) self.assertTrue(self.manager.add_job(job1)) self.assertEqual(DEFAULT_EXPECTED_MINING_TIME, job1.expected_mining_time) self.assertEqual(0, job1.expected_queue_time) self.assertEqual(1, len(self.manager.tx_queue)) if DEFAULT_EXPECTED_MINING_TIME > 0: expected_queue_time += DEFAULT_EXPECTED_MINING_TIME job2 = TxJob(TX2_DATA) self.assertTrue(self.manager.add_job(job2)) self.assertEqual(DEFAULT_EXPECTED_MINING_TIME, job2.expected_mining_time) self.assertEqual(expected_queue_time, job2.expected_queue_time) self.assertEqual(2, len(self.manager.tx_queue)) if DEFAULT_EXPECTED_MINING_TIME > 0: expected_queue_time += DEFAULT_EXPECTED_MINING_TIME job3 = TxJob(TOKEN_CREATION_TX_DATA) self.assertTrue(self.manager.add_job(job3)) self.assertEqual(DEFAULT_EXPECTED_MINING_TIME, job3.expected_mining_time) self.assertEqual(expected_queue_time, job3.expected_queue_time) self.assertEqual(3, len(self.manager.tx_queue)) self.assertEqual([job1, job2, job3], list(self.manager.tx_queue)) # First miner connects and receives job1. conn1 = self._get_ready_miner('HVZjvL1FJ23kH3buGNuttVRsRKq66WHUVZ') self.assertIsNotNone(conn1.current_job) self.assertEqual(job1, conn1.current_job.tx_job) # Second miner connects and receives job1. conn2 = self._get_ready_miner('HVZjvL1FJ23kH3buGNuttVRsRKq66WHUVZ') self.assertIsNotNone(conn2.current_job) self.assertEqual(job1, conn2.current_job.tx_job)