class TipsTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(TipsResource(self.manager)) @inlineCallbacks def test_get_tips(self): genesis_txs = [tx for tx in get_genesis_transactions(self.manager.tx_storage) if not tx.is_block] # Tips are only the genesis response1 = yield self.web.get("tips") data1 = response1.json_value() self.assertEqual(len(data1), len(genesis_txs)) self.manager.wallet.unlock(b'MYPASS') # Add blocks to have funds add_new_blocks(self.manager, 2, advance_clock=1) add_blocks_unlock_reward(self.manager) # Add one tx, now you have only one tip tx = add_new_transactions(self.manager, 1)[0] response2 = yield self.web.get("tips") data2 = response2.json_value() self.assertEqual(len(data2), 1) # Getting tips sending timestamp as parameter response3 = yield self.web.get("tips", {b'timestamp': tx.timestamp - 1}) data3 = response3.json_value() self.assertEqual(len(data3), 2)
class BaseBalanceTest(_BaseResourceTest._ResourceTest): __test__ = False def setUp(self): super().setUp() self.web = StubSite(BalanceResource(self.manager)) self.web_mining = StubSite(MiningResource(self.manager)) @inlineCallbacks def test_get(self): response = yield self.web.get("wallet/balance") data = response.json_value() self.assertTrue(data['success']) self.assertEqual(data['balance'], {'available': 0, 'locked': 0}) # Mining new block response_mining = yield self.web_mining.get("mining") data_mining = response_mining.json_value() block_bytes = resolve_block_bytes( block_bytes=data_mining['block_bytes']) yield self.web_mining.post( "mining", {'block_bytes': base64.b64encode(block_bytes).decode('utf-8')}) # Get new balance after block response2 = yield self.web.get("wallet/balance") data2 = response2.json_value() self.assertTrue(data2['success']) tokens = self.manager.get_tokens_issued_per_block(1) self.assertEqual(data2['balance'], {'available': tokens, 'locked': 0})
class HistoryTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(HistoryResource(self.manager)) self.web_mining = StubSite(MiningResource(self.manager)) @inlineCallbacks def test_get(self): # Mining new block response_mining = yield self.web_mining.get("mining") data_mining = response_mining.json_value() block_bytes = resolve_block_bytes( block_bytes=data_mining['block_bytes']) yield self.web_mining.post( "mining", {'block_bytes': base64.b64encode(block_bytes).decode('utf-8')}) # Getting wallet history response = yield self.web.get("wallet/history", { b'page': 1, b'count': 10 }) data = response.json_value() self.assertEqual(len(data['history']), 1) self.assertEqual(data['total_pages'], 1)
class GetMiningInfoTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(MiningInfoResource(self.manager)) self.manager.wallet.unlock(b'MYPASS') @inlineCallbacks def test_get_many(self): response1 = yield self.web.get("getmininginfo") data1 = response1.json_value() self.assertTrue(data1['success']) # No blocks self.assertEqual(data1['blocks'], 0) # Difficulty is 1 self.assertEqual(data1['difficulty'], 1) # Add 10 blocks add_new_blocks(self.manager, 10, advance_clock=1) response2 = yield self.web.get("getmininginfo") data2 = response2.json_value() self.assertTrue(data2['success']) # 10 blocks self.assertEqual(data2['blocks'], 10) # Difficulty is 1 self.assertEqual(data2['difficulty'], 1) # Hashrate < 1 because of low weight and many blocks added fast self.assertLess(data2['hashrate'], 1)
class AddressTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(AddressResource(self.manager)) @inlineCallbacks def test_get(self): response = yield self.web.get("wallet/address", {b'new': b'true'}) data = response.json_value() new_address1 = data['address'] response_same = yield self.web.get("wallet/address", {b'new': b'false'}) data_same = response_same.json_value() same_address = data_same['address'] # Default has to be new: false response_same2 = yield self.web.get("wallet/address") data_same2 = response_same2.json_value() same_address2 = data_same2['address'] response_new = yield self.web.get("wallet/address", {b'new': b'true'}) data_new = response_new.json_value() new_address2 = data_new['address'] self.assertEqual(new_address1, same_address) self.assertEqual(same_address, same_address2) self.assertNotEqual(new_address1, new_address2)
def test_address_balance(self): resource = StubSite(AddressBalanceResource(self.manager)) # Invalid address response_error = yield resource.get('thin_wallet/address_search', { b'address': 'vvvv'.encode(), b'count': 3 }) data_error = response_error.json_value() self.assertFalse(data_error['success']) response = yield resource.get('thin_wallet/address_balance', {b'address': self.address.encode()}) data = response.json_value() self.assertTrue(data['success']) # Genesis - token deposit + blocks mined HTR_value = settings.GENESIS_TOKENS - 1 + ( settings.INITIAL_TOKENS_PER_BLOCK * 5) self.assertEqual(data['total_transactions'], 6) # 5 blocks mined + token creation tx self.assertIn(settings.HATHOR_TOKEN_UID.hex(), data['tokens_data']) self.assertIn(self.token_uid.hex(), data['tokens_data']) self.assertEqual( HTR_value, data['tokens_data'][settings.HATHOR_TOKEN_UID.hex()]['received']) self.assertEqual( 0, data['tokens_data'][settings.HATHOR_TOKEN_UID.hex()]['spent']) self.assertEqual(100, data['tokens_data'][self.token_uid.hex()]['received']) self.assertEqual(0, data['tokens_data'][self.token_uid.hex()]['spent'])
class BaseMempoolTest(_BaseResourceTest._ResourceTest): __test__ = False def setUp(self): super().setUp() self.web = StubSite(MempoolResource(self.manager)) self.manager.wallet.unlock(b'MYPASS') add_new_blocks(self.manager, 4, advance_clock=1) add_blocks_unlock_reward(self.manager) @inlineCallbacks def test_get(self): # Success empty mempool response1 = yield self.web.get("mempool") data1 = response1.json_value() self.assertTrue(data1['success']) self.assertEqual(data1['transactions'], []) # Success mempool with single TX txs2 = add_new_transactions(self.manager, 1, advance_clock=1) response2 = yield self.web.get("mempool") data2 = response2.json_value() self.assertTrue(data2['success']) self.assertEqual(data2['transactions'], list(map(lambda t: t.hash.hex(), txs2))) # Success mempool with multiple TX txs3 = add_new_transactions(self.manager, 2, advance_clock=1) response3 = yield self.web.get("mempool") data3 = response3.json_value() self.assertTrue(data3['success']) self.assertEqual(data3['transactions'], list(map(lambda t: t.hash.hex(), txs2 + txs3))) # add block to confirm previous txs add_new_blocks(self.manager, 1, advance_clock=1) # and next call will not have previous mempool txs4 = add_new_transactions(self.manager, 2, advance_clock=1) response4 = yield self.web.get("mempool") data4 = response4.json_value() self.assertTrue(data4['success']) self.assertEqual(data4['transactions'], list(map(lambda t: t.hash.hex(), txs4))) # add block to confirm previous txs add_new_blocks(self.manager, 1, advance_clock=1) # Add more than api limit and check truncated return add_new_transactions(self.manager, settings.MEMPOOL_API_TX_LIMIT + 1, advance_clock=1) response5 = yield self.web.get("mempool") data5 = response5.json_value() self.assertTrue(data5['success']) # default limit is 100 self.assertEqual(len(data5['transactions']), settings.MEMPOOL_API_TX_LIMIT)
class BaseGetMiningInfoTest(_BaseResourceTest._ResourceTest): __test__ = False def setUp(self): super().setUp() self.web = StubSite(MiningInfoResource(self.manager)) self.manager.wallet.unlock(b'MYPASS') @inlineCallbacks def test_get_many(self): response1 = yield self.web.get("getmininginfo") data1 = response1.json_value() self.assertTrue(data1['success']) # No blocks self.assertEqual(data1['blocks'], 0) # Difficulty is 1 self.assertEqual(data1['difficulty'], 1) # Add 10 blocks add_new_blocks(self.manager, 10, advance_clock=1) response2 = yield self.web.get("getmininginfo") data2 = response2.json_value() self.assertTrue(data2['success']) # 10 blocks self.assertEqual(data2['blocks'], 10) # Difficulty is 1 self.assertEqual(data2['difficulty'], 1) # Hashrate < 1 because of low weight and many blocks added fast self.assertLess(data2['hashrate'], 1) @inlineCallbacks def test_mined_tokens(self): self.manager.wallet.unlock(b'MYPASS') response = yield self.web.get("mined_tokens") data = response.json_value() self.assertEqual(data['blocks'], 0) self.assertEqual(data['mined_tokens'], 0) add_new_blocks(self.manager, 5, advance_clock=1) response = yield self.web.get("mined_tokens") data = response.json_value() self.assertEqual(data['blocks'], 5) self.assertEqual(data['mined_tokens'], 5 * settings.INITIAL_TOKENS_PER_BLOCK) add_new_blocks(self.manager, settings.BLOCKS_PER_HALVING + 15, advance_clock=1) mined_tokens = ( settings.BLOCKS_PER_HALVING * settings.INITIAL_TOKENS_PER_BLOCK + 20 * settings.INITIAL_TOKENS_PER_BLOCK // 2) response = yield self.web.get("mined_tokens") data = response.json_value() self.assertEqual(data['blocks'], settings.BLOCKS_PER_HALVING + 20) self.assertEqual(data['mined_tokens'], mined_tokens)
class TransactionTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(ValidateAddressResource(self.manager)) # Example from the design: # # ❯ curl localhost:9080/v1a/validate_address/HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A -s | jq # { # "valid": true, # "script": "dqkUr6YAVWv0Ps6bjgSGuqMb1GqCw6+IrA==", # "address": "HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A", # "type": "p2pkh" # } @inlineCallbacks def test_simple(self): address = b'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A' response_success = yield self.web.get(address) data_success = response_success.json_value() self.assertEqual(data_success, { 'valid': True, 'script': 'dqkUr6YAVWv0Ps6bjgSGuqMb1GqCw6+IrA==', 'address': address.decode('ascii'), 'type': 'p2pkh', }) @inlineCallbacks def test_invalid_network(self): # this address is valid on the testnet response_success = yield self.web.get(b'WTPcVyGjo9tSet8QAH7qudW2LwtkgubZGU') data_success = response_success.json_value() self.assertEqual(data_success, { 'valid': False, 'error': 'ScriptError', 'msg': 'The address is not valid', }) @inlineCallbacks def test_wrong_size(self): address = b'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199Aa' response_success = yield self.web.get(address) data_success = response_success.json_value() self.assertEqual(data_success, { 'valid': False, 'error': 'InvalidAddress', 'msg': 'Address size must have 25 bytes', }) @inlineCallbacks def test_gibberish(self): # this isn't remotely what an address looks like response_success = yield self.web.get(b'ahl8sfyoiuh23$%!!dfads') data_success = response_success.json_value() self.assertEqual(data_success, { 'valid': False, 'error': 'InvalidAddress', 'msg': 'Invalid base58 address', })
class BaseTransactionTest(_BaseResourceTest._ResourceTest): __test__ = False def setUp(self): super().setUp() self.web = StubSite(TransactionAccWeightResource(self.manager)) self.manager.wallet.unlock(b'MYPASS') @inlineCallbacks def test_get_data(self): genesis_tx = next(x for x in self.manager.tx_storage.get_all_genesis() if x.is_transaction) response_success = yield self.web.get( "transaction_acc_weight", {b'id': bytes(genesis_tx.hash.hex(), 'utf-8')}) data_success = response_success.json_value() self.assertTrue(data_success['success']) self.assertEqual(data_success['accumulated_weight'], genesis_tx.weight) self.assertEqual(data_success['confirmation_level'], 0) # Adding blocks to have funds add_new_blocks(self.manager, 2, advance_clock=1) add_blocks_unlock_reward(self.manager) tx = add_new_transactions(self.manager, 5)[0] add_new_blocks(self.manager, 2, advance_clock=1) add_blocks_unlock_reward(self.manager) response_success2 = yield self.web.get( "transaction_acc_weight", {b'id': bytes(tx.hash.hex(), 'utf-8')}) data_success2 = response_success2.json_value() self.assertGreater(data_success2['accumulated_weight'], tx.weight) self.assertEqual(data_success2['confirmation_level'], 1) # Test sending hash that does not exist response_error1 = yield self.web.get( "transaction_acc_weight", { b'id': b'000000831cff82fa730cbdf8640fae6c130aab1681336e2f8574e314a5533848' }) data_error1 = response_error1.json_value() self.assertFalse(data_error1['success']) # Test sending invalid hash response_error2 = yield self.web.get( "transaction_acc_weight", { b'id': b'000000831cff82fa730cbdf8640fae6c130aab1681336e2f8574e314a553384' }) data_error2 = response_error2.json_value() self.assertFalse(data_error2['success']) @inlineCallbacks def test_blocks_are_blocked(self): genesis_tx = next(x for x in self.manager.tx_storage.get_all_genesis() if x.is_block) response_success = yield self.web.get( "transaction_acc_weight", {b'id': bytes(genesis_tx.hash.hex(), 'utf-8')}) data_success = response_success.json_value() self.assertFalse(data_success['success'])
def test_search(self): resource = StubSite(AddressSearchResource(self.manager)) # Invalid address response_error = yield resource.get('thin_wallet/address_search', { b'address': 'vvvv'.encode(), b'count': 3 }) data_error = response_error.json_value() self.assertFalse(data_error['success']) # Get address search first page success response = yield resource.get('thin_wallet/address_search', { b'address': self.address.encode(), b'count': 3 }) data = response.json_value() self.assertTrue(data['success']) self.assertEqual(len(data['transactions']), 3) self.assertTrue(data['has_more']) # Getting next page response2 = yield resource.get( 'thin_wallet/address_search', { b'address': self.address.encode(), b'count': 3, b'page': b'next', b'hash': data['transactions'][-1]['tx_id'].encode() }) data2 = response2.json_value() self.assertTrue(data2['success']) self.assertEqual(len(data2['transactions']), 3) self.assertFalse(data2['has_more']) # Testing that no tx in data is also in data2 tx_ids_data = [tx['tx_id'] for tx in data['transactions']] for tx in data2['transactions']: self.assertNotIn(tx['tx_id'], tx_ids_data) # Getting previous page from third element response3 = yield resource.get( 'thin_wallet/address_search', { b'address': self.address.encode(), b'count': 3, b'page': b'previous', b'hash': data['transactions'][-1]['tx_id'].encode() }) data3 = response3.json_value() self.assertTrue(data3['success']) self.assertEqual(len(data3['transactions']), 2) self.assertFalse(data3['has_more']) # Testing that no tx in data3 is also in data2 tx_ids_data = [tx['tx_id'] for tx in data3['transactions']] for tx in data2['transactions']: self.assertNotIn(tx['tx_id'], tx_ids_data)
def test_token(self): resource = StubSite(TokenResource(self.manager)) # test invalid token id response = yield resource.get('thin_wallet/token', {b'id': 'vvvv'.encode()}) data = response.json_value() self.assertFalse(data['success']) # test missing token id response = yield resource.get('thin_wallet/token') data = response.json_value() self.assertFalse(data['success']) # test unknown token id unknown_uid = '00000000228ed1dd74a2e1b920c1d64bf81dc63875dce4fac486001073b45a27'.encode( ) response = yield resource.get('thin_wallet/token', {b'id': unknown_uid}) data = response.json_value() self.assertFalse(data['success']) # test success case add_new_blocks(self.manager, 1, advance_clock=1) add_blocks_unlock_reward(self.manager) token_name = 'MyTestToken' token_symbol = 'MTT' amount = 150 tx = create_tokens(self.manager, mint_amount=amount, token_name=token_name, token_symbol=token_symbol) token_uid = tx.tokens[0] response = yield resource.get('thin_wallet/token', {b'id': token_uid.hex().encode()}) data = response.json_value() self.assertTrue(data['success']) self.assertEqual(len(data['mint']), 1) self.assertEqual(len(data['melt']), 1) self.assertEqual(data['mint'][0]['tx_id'], tx.hash_hex) self.assertEqual(data['melt'][0]['tx_id'], tx.hash_hex) self.assertEqual(data['mint'][0]['index'], 1) self.assertEqual(data['melt'][0]['index'], 2) self.assertEqual(data['total'], amount) self.assertEqual(data['name'], token_name) self.assertEqual(data['symbol'], token_symbol) # test no wallet index manager2 = self.create_peer(self.network, unlock_wallet=True) resource2 = StubSite(TokenResource(manager2)) response2 = yield resource2.get('thin_wallet/token') data2 = response2.json_value() self.assertEqual(response2.responseCode, 503) self.assertFalse(data2['success'])
class DecodeTxTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(DecodeTxResource(self.manager)) @inlineCallbacks def test_get(self): genesis = list(self.manager.tx_storage.get_all_genesis()) genesis.sort(key=lambda x: x.timestamp) self.assertTrue(genesis[0].is_block) for tx in genesis[1:]: self.assertTrue(tx.is_transaction) genesis_tx = genesis[1] response_success = yield self.web.get( "decode_tx", {b'hex_tx': bytes(genesis_tx.get_struct().hex(), 'utf-8')}) data_success = response_success.json_value() self.assertTrue(data_success['success']) data_genesis = genesis_tx.to_json(decode_script=True) data_genesis['raw'] = genesis_tx.get_struct().hex() data_genesis['nonce'] = str(data_genesis['nonce']) self.assertEqual(data_success['tx'], data_genesis) self.assertTrue('meta' in data_success) self.assertTrue('spent_outputs' in data_success) # Invalid hex response_error1 = yield self.web.get("decode_tx", {b'hex_tx': b'XXXX'}) data_error1 = response_error1.json_value() self.assertFalse(data_error1['success']) # Invalid tx hex response_error2 = yield self.web.get("decode_tx", {b'hex_tx': b'a12c'}) data_error2 = response_error2.json_value() self.assertFalse(data_error2['success']) # Token creation tx script_type_out = parse_address_script(genesis[0].outputs[0].script) address = script_type_out.address add_blocks_unlock_reward(self.manager) tx2 = create_tokens(self.manager, address, mint_amount=100, propagate=True) response = yield self.web.get( 'decode_tx', {b'hex_tx': bytes(tx2.get_struct().hex(), 'utf-8')}) data = response.json_value() self.assertTrue(data['success'])
class VersionTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(VersionResource(self.manager)) @inlineCallbacks def test_get(self): response = yield self.web.get("version") data = response.json_value() self.assertEqual(data['version'], hathor.__version__)
class DashboardTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(DashboardTransactionResource(self.manager)) @inlineCallbacks def test_get(self): tx_count = block_count = 6 response = yield self.web.get("dashboard_tx", { b'block': str(block_count).encode(), b'tx': str(tx_count).encode() }) data = response.json_value() self.assertLessEqual(len(data['transactions']), tx_count) self.assertLessEqual(len(data['blocks']), block_count) @inlineCallbacks def test_invalid_parameters(self): # wrong type block response = yield self.web.get("dashboard_tx", { b'block': b'a', b'tx': b'6' }) data = response.json_value() self.assertFalse(data['success']) # missing block param response = yield self.web.get("dashboard_tx", {b'tx': b'6'}) data = response.json_value() self.assertFalse(data['success']) # wrong type tx response = yield self.web.get("dashboard_tx", { b'block': b'6', b'tx': b'a' }) data = response.json_value() self.assertFalse(data['success']) # missing tx param response = yield self.web.get("dashboard_tx", {b'block': b'6'}) data = response.json_value() self.assertFalse(data['success'])
class BaseLockTest(_BaseResourceTest._ResourceTest): __test__ = False def setUp(self): super().setUp() self.web = StubSite(LockWalletResource(self.manager)) self.web_unlock = StubSite(UnlockWalletResource(self.manager)) self.web_state = StubSite(StateWalletResource(self.manager)) @inlineCallbacks def test_locking(self): # Wallet is locked response = yield self.web_state.get('wallet/state') data = response.json_value() self.assertTrue(data['is_locked']) # Unlock it response_success = yield self.web_unlock.post('wallet/unlock', {'password': '******'}) data_success = response_success.json_value() self.assertTrue(data_success['success']) # Wallet is unlocked response_unlocked = yield self.web_state.get('wallet/state') data_unlocked = response_unlocked.json_value() self.assertFalse(data_unlocked['is_locked']) # Test locking the wallet with resource # Options yield self.web.options("wallet/lock") response_test = yield self.web.post('wallet/lock') data_test = response_test.json_value() self.assertTrue(data_test['success']) # Validate wallet is locked response_locked = yield self.web_state.get('wallet/state') data_locked = response_locked.json_value() self.assertTrue(data_locked['is_locked'])
class DashboardTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(DashboardTransactionResource(self.manager)) @inlineCallbacks def test_get(self): tx_count = block_count = 6 response = yield self.web.get("dashboard_tx", {b'block': block_count, b'tx': tx_count}) data = response.json_value() self.assertLessEqual(len(data['transactions']), tx_count) self.assertLessEqual(len(data['blocks']), block_count)
class MiningTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(MiningResource(self.manager)) @inlineCallbacks def test_get(self): response = yield self.web.get('mining') data = response.json_value() self.assertGreater(len(data['parents']), 0) self.assertIsNotNone(data.get('block_bytes')) @inlineCallbacks def test_post(self): response_get = yield self.web.get('mining') data_get = response_get.json_value() block_bytes_str = data_get.get('block_bytes') block_bytes = base64.b64decode(block_bytes_str) block = Block.create_from_struct(block_bytes) block.weight = 4 block.resolve() block_bytes = bytes(block) block_bytes_str = base64.b64encode(block_bytes).decode('ascii') response_post = yield self.web.post('mining', {'block_bytes': block_bytes_str}) self.assertEqual(response_post.written[0], b'1') block.weight = 100 block_bytes = bytes(block) block_bytes_str = base64.b64encode(block_bytes).decode('ascii') response_post = yield self.web.post('mining', {'block_bytes': block_bytes_str}) # Probability 2^(100 - 256) of failing self.assertEqual(response_post.written[0], b'0')
class TipsTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(TipsHistogramResource(self.manager)) self.manager.wallet.unlock(b'MYPASS') self.manager.reactor.advance(time.time()) @inlineCallbacks def test_get_tips_histogram(self): # Add blocks to have funds add_new_blocks(self.manager, 2, 2) add_blocks_unlock_reward(self.manager) txs = add_new_transactions(self.manager, 10, 2) response1 = yield self.web.get("tips-histogram", { b'begin': txs[0].timestamp, b'end': txs[0].timestamp }) data1 = response1.json_value() self.assertEqual(len(data1), 1) self.assertEqual([txs[0].timestamp, 1], data1[0]) response2 = yield self.web.get("tips-histogram", { b'begin': txs[0].timestamp, b'end': txs[0].timestamp + 1 }) data2 = response2.json_value() self.assertEqual(len(data2), 2) self.assertEqual([txs[0].timestamp, 1], data2[0]) self.assertEqual([txs[0].timestamp + 1, 1], data2[1]) response3 = yield self.web.get("tips-histogram", { b'begin': txs[0].timestamp, b'end': txs[-1].timestamp }) data3 = response3.json_value() self.assertEqual(len(data3), 19)
class BaseDecodeTxTest(_BaseResourceTest._ResourceTest): __test__ = False def setUp(self): super().setUp() self.web = StubSite(TxParentsResource(self.manager)) @inlineCallbacks def test_get_success(self): resp = yield self.web.get('tx_parents') data = resp.json_value() self.assertTrue(data['success']) self.assertEqual(2, len(data['tx_parents'])) @inlineCallbacks def test_get_syncing(self): self.manager._allow_mining_without_peers = False resp = yield self.web.get('tx_parents') data = resp.json_value() self.assertFalse(data['success'])
class BaseBlockAtHeightTest(_BaseResourceTest._ResourceTest): __test__ = False def setUp(self): super().setUp() self.web = StubSite(BlockAtHeightResource(self.manager)) self.manager.wallet.unlock(b'MYPASS') @inlineCallbacks def test_get(self): blocks = add_new_blocks(self.manager, 4, advance_clock=1) # Error1: No parameter response1 = yield self.web.get("block_at_height") data1 = response1.json_value() self.assertFalse(data1['success']) # Error2: Invalid parameter response2 = yield self.web.get("block_at_height", {b'height': b'c'}) data2 = response2.json_value() self.assertFalse(data2['success']) # Success genesis genesis_block = next( x for x in self.manager.tx_storage.get_all_genesis() if x.is_block) response3 = yield self.web.get("block_at_height", {b'height': b'0'}) data3 = response3.json_value() self.assertTrue(data3['success']) self.assertEqual(data3['block']['tx_id'], genesis_block.hash.hex()) # Success height 1 response4 = yield self.web.get("block_at_height", {b'height': b'1'}) data4 = response4.json_value() self.assertTrue(data4['success']) self.assertEqual(data4['block']['tx_id'], blocks[0].hash.hex()) # Success height 5 response5 = yield self.web.get("block_at_height", {b'height': b'4'}) data5 = response5.json_value() self.assertTrue(data5['success']) self.assertEqual(data5['block']['tx_id'], blocks[3].hash.hex()) # Error 3: height 5 (does not have this block) response6 = yield self.web.get("block_at_height", {b'height': b'5'}) data6 = response6.json_value() self.assertFalse(data6['success'])
class BaseUnlockTest(_BaseResourceTest._ResourceTest): __test__ = False def setUp(self): super().setUp() self.web = StubSite(UnlockWalletResource(self.manager)) self.web_lock = StubSite(LockWalletResource(self.manager)) self.web_state = StubSite(StateWalletResource(self.manager)) @inlineCallbacks def test_unlocking(self): # Wallet is locked response = yield self.web_state.get("wallet/state") data = response.json_value() self.assertTrue(data['is_locked']) # Try to unlock with wrong password # Options yield self.web.options("wallet/unlock") response_error = yield self.web.post("wallet/unlock", {'password': '******'}) data_error = response_error.json_value() self.assertFalse(data_error['success']) # Try to unlock with correct password response_success = yield self.web.post("wallet/unlock", {'password': '******'}) data_success = response_success.json_value() self.assertTrue(data_success['success']) # Wallet is unlocked response_unlocked = yield self.web_state.get("wallet/state") data_unlocked = response_unlocked.json_value() self.assertFalse(data_unlocked['is_locked']) @inlineCallbacks def test_unlocking_hd_wallet(self): self.manager.wallet = HDWallet() self.manager.wallet._manually_initialize() self.manager.wallet.unlock(tx_storage=self.manager.tx_storage) # Wallet is not locked response = yield self.web_state.get("wallet/state") data = response.json_value() self.assertFalse(data['is_locked']) # Lock the wallet response_lock = yield self.web_lock.post("wallet/lock") data_lock = response_lock.json_value() self.assertTrue(data_lock['success']) # Wallet is locked response_locked = yield self.web_state.get("wallet/state") data_locked = response_locked.json_value() self.assertTrue(data_locked['is_locked']) # Unlock wallet invalid words response_invalid = yield self.web.post("wallet/unlock", {'words': 'abc def', 'passphrase': ''}) data_invalid = response_invalid.json_value() self.assertFalse(data_invalid['success']) # Unlock wallet response_success = yield self.web.post("wallet/unlock", {'passphrase': ''}) data_success = response_success.json_value() self.assertTrue(data_success['success']) # Wallet is unlocked response_unlocked = yield self.web_state.get("wallet/state") data_unlocked = response_unlocked.json_value() self.assertFalse(data_unlocked['is_locked']) # Lock the wallet and unlock with same words self.manager.wallet.lock() response_words = yield self.web.post("wallet/unlock", {'words': data_success['words'], 'passphrase': ''}) data_words = response_words.json_value() self.assertTrue(data_words['success'])
class StatusTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(StatusResource(self.manager)) self.manager2 = self.create_peer('testnet') self.conn1 = FakeConnection(self.manager, self.manager2) @inlineCallbacks def test_get(self): response = yield self.web.get("status") data = response.json_value() server_data = data.get('server') self.assertEqual(server_data['app_version'], 'Hathor v{}'.format(hathor.__version__)) self.assertEqual(server_data['network'], 'testnet') self.assertGreater(server_data['uptime'], 0) @inlineCallbacks def test_handshaking(self): response = yield self.web.get("status") data = response.json_value() server_data = data.get('server') known_peers = data.get('known_peers') connections = data.get('connections') self.assertEqual(server_data['app_version'], 'Hathor v{}'.format(hathor.__version__)) self.assertEqual(server_data['network'], 'testnet') self.assertGreater(server_data['uptime'], 0) handshake_peer = self.conn1.proto1.transport.getPeer() handshake_address = '{}:{}'.format(handshake_peer.host, handshake_peer.port) self.assertEqual(len(known_peers), 0) self.assertEqual(len(connections['connected_peers']), 0) self.assertEqual(len(connections['handshaking_peers']), 1) self.assertEqual(connections['handshaking_peers'][0]['address'], handshake_address) @inlineCallbacks def test_get_with_one_peer(self): self.conn1.run_one_step() # HELLO self.conn1.run_one_step() # PEER-ID self.conn1.run_one_step() # READY self.conn1.run_one_step() # BOTH PEERS ARE READY NOW response = yield self.web.get("status") data = response.json_value() server_data = data.get('server') known_peers = data.get('known_peers') connections = data.get('connections') self.assertEqual(server_data['app_version'], 'Hathor v{}'.format(hathor.__version__)) self.assertEqual(server_data['network'], 'testnet') self.assertGreater(server_data['uptime'], 0) self.assertEqual(len(known_peers), 1) self.assertEqual(known_peers[0]['id'], self.manager2.my_peer.id) self.assertEqual(len(connections['connected_peers']), 1) self.assertEqual(connections['connected_peers'][0]['id'], self.manager2.my_peer.id) @inlineCallbacks def test_connecting_peers(self): address = '192.168.1.1:54321' endpoint = endpoints.clientFromString(self.manager.reactor, 'tcp:{}'.format(address)) deferred = endpoint.connect self.manager.connections.connecting_peers[endpoint] = deferred response = yield self.web.get("status") data = response.json_value() connecting = data['connections']['connecting_peers'] self.assertEqual(len(connecting), 1) self.assertEqual(connecting[0]['address'], address) self.assertIsNotNone(connecting[0]['deferred'])
class DecodeTxTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(PushTxResource(self.manager)) self.web_tokens = StubSite(SendTokensResource(self.manager)) self.web_mining = StubSite(MiningResource(self.manager)) self.web_balance = StubSite(BalanceResource(self.manager)) self.web_history = StubSite(HistoryResource(self.manager)) @inlineCallbacks def test_get(self): # Mining new block response_mining = yield self.web_mining.get('mining') data_mining = response_mining.json_value() block_bytes = resolve_block_bytes( block_bytes=data_mining['block_bytes']) yield self.web_mining.post( 'mining', {'block_bytes': base64.b64encode(block_bytes).decode('utf-8')}) # Unlocking wallet self.manager.wallet.unlock(b'MYPASS') # Creating a valid transaction to be pushed to the network blocks = add_new_blocks(self.manager, 3, advance_clock=2) add_blocks_unlock_reward(self.manager) tx_id = blocks[0].hash output = blocks[0].outputs[0] script_type_out = parse_address_script(output.script) address = script_type_out.address private_key = self.manager.wallet.get_private_key(address) output_address = decode_address(self.get_address(0)) value = self.manager.get_tokens_issued_per_block(1) o = TxOutput(value, create_output_script(output_address, None)) i = TxInput(tx_id, 0, b'') tx = Transaction(inputs=[i], outputs=[o]) data_to_sign = tx.get_sighash_all() public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data( data_to_sign, private_key) i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes) tx.inputs = [i] tx.timestamp = int(self.clock.seconds()) tx.weight = self.manager.minimum_tx_weight(tx) tx.parents = self.manager.get_new_tx_parents(tx.timestamp) tx.resolve() response = yield self.web.get( 'push_tx', {b'hex_tx': bytes(tx.get_struct().hex(), 'utf-8')}) data = response.json_value() self.assertTrue(data['success']) # Sending token to random address without input data_json = { 'outputs': [{ 'address': self.get_address(0), 'value': 5 }], 'inputs': [] } yield self.web_tokens.post('wallet/send_tokens', {'data': data_json}) # modify tx so it will be a double spending, then rejected tx.weight += 0.1 tx.resolve() response_success = yield self.web.get( 'push_tx', {b'hex_tx': bytes(tx.get_struct().hex(), 'utf-8')}) data_success = response_success.json_value() self.assertFalse(data_success['success']) # Invalid tx (don't have inputs) genesis_tx = get_genesis_transactions(self.manager.tx_storage)[1] response_genesis = yield self.web.get( 'push_tx', {b'hex_tx': bytes(genesis_tx.get_struct().hex(), 'utf-8')}) data_genesis = response_genesis.json_value() self.assertFalse(data_genesis['success']) # Invalid hex response_error1 = yield self.web.get('push_tx', {b'hex_tx': b'XXXX'}) data_error1 = response_error1.json_value() self.assertFalse(data_error1['success']) # Invalid tx hex response_error2 = yield self.web.get('push_tx', {b'hex_tx': b'a12c'}) data_error2 = response_error2.json_value() self.assertFalse(data_error2['success']) # Token creation tx tx2 = create_tokens(self.manager, address, mint_amount=100, propagate=False) response = yield self.web.get( 'push_tx', {b'hex_tx': bytes(tx2.get_struct().hex(), 'utf-8')}) data = response.json_value() self.assertTrue(data['success'])
class TransactionTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(TransactionResource(self.manager)) self.manager.wallet.unlock(b'MYPASS') @inlineCallbacks def test_get_one(self): genesis_tx = get_genesis_transactions(self.manager.tx_storage)[0] response_success = yield self.web.get( "transaction", {b'id': bytes(genesis_tx.hash.hex(), 'utf-8')}) data_success = response_success.json_value() self.assertTrue(data_success['success']) dict_test = genesis_tx.to_json(decode_script=True) dict_test['raw'] = genesis_tx.get_struct().hex() self.assertEqual(data_success['tx'], dict_test) # Test sending hash that does not exist response_error1 = yield self.web.get( "transaction", { b'id': b'000000831cff82fa730cbdf8640fae6c130aab1681336e2f8574e314a5533848' }) data_error1 = response_error1.json_value() self.assertFalse(data_error1['success']) # Test sending invalid hash response_error2 = yield self.web.get( "transaction", { b'id': b'000000831cff82fa730cbdf8640fae6c130aab1681336e2f8574e314a553384' }) data_error2 = response_error2.json_value() self.assertFalse(data_error2['success']) # Adding blocks to have funds add_new_blocks(self.manager, 2, advance_clock=1) add_blocks_unlock_reward(self.manager) tx = add_new_transactions(self.manager, 1)[0] tx2 = Transaction.create_from_struct(tx.get_struct()) tx2.parents = [tx.parents[1], tx.parents[0]] tx2.resolve() self.manager.propagate_tx(tx2) # Now we get a tx with conflict, voided_by and twin response_conflict = yield self.web.get( "transaction", {b'id': bytes(tx2.hash.hex(), 'utf-8')}) data_conflict = response_conflict.json_value() self.assertTrue(data_conflict['success']) @inlineCallbacks def test_get_many(self): # Add some blocks and txs and get them in timestamp order blocks = add_new_blocks(self.manager, 4, advance_clock=1) _blocks = add_blocks_unlock_reward(self.manager) txs = sorted(add_new_transactions(self.manager, 25), key=lambda x: (x.timestamp, x.hash)) blocks.extend(_blocks) blocks = sorted(blocks, key=lambda x: (x.timestamp, x.hash)) # Get last 2 blocks expected1 = blocks[-2:] expected1.reverse() response1 = yield self.web.get("transaction", { b'count': b'2', b'type': b'block' }) data1 = response1.json_value() for expected, result in zip(expected1, data1['transactions']): self.assertEqual(expected.timestamp, result['timestamp']) self.assertEqual(expected.hash.hex(), result['tx_id']) self.assertTrue(data1['has_more']) # Get last 8 txs expected2 = txs[-8:] expected2.reverse() response2 = yield self.web.get("transaction", { b'count': b'8', b'type': b'tx' }) data2 = response2.json_value() for expected, result in zip(expected2, data2['transactions']): self.assertEqual(expected.timestamp, result['timestamp']) self.assertEqual(expected.hash.hex(), result['tx_id']) self.assertTrue(data2['has_more']) # Get older blocks with hash reference expected3 = blocks[:2] expected3.reverse() response3 = yield self.web.get( "transaction", { b'count': b'3', b'type': b'block', b'timestamp': bytes(str(blocks[2].timestamp), 'utf-8'), b'hash': bytes(blocks[2].hash.hex(), 'utf-8'), b'page': b'next' }) data3 = response3.json_value() for expected, result in zip(expected3, data3['transactions']): self.assertEqual(expected.timestamp, result['timestamp']) self.assertEqual(expected.hash.hex(), result['tx_id']) self.assertFalse(data3['has_more']) # Get newer txs with hash reference response4 = yield self.web.get( "transaction", { b'count': b'16', b'type': b'tx', b'timestamp': bytes(str(txs[-9].timestamp), 'utf-8'), b'hash': bytes(txs[-9].hash.hex(), 'utf-8'), b'page': b'previous' }) data4 = response4.json_value() for expected, result in zip(expected2, data4['transactions']): self.assertEqual(expected.timestamp, result['timestamp']) self.assertEqual(expected.hash.hex(), result['tx_id']) self.assertFalse(data4['has_more']) # Get newer blocks with hash reference expected5 = blocks[-2:] expected5.reverse() response5 = yield self.web.get( "transaction", { b'count': b'3', b'type': b'block', b'timestamp': bytes(str(expected1[-1].timestamp), 'utf-8'), b'hash': bytes(expected1[-1].hash.hex(), 'utf-8'), b'page': b'previous' }) data5 = response5.json_value() for expected, result in zip(expected5, data5['transactions']): self.assertEqual(expected.timestamp, result['timestamp']) self.assertEqual(expected.hash.hex(), result['tx_id']) self.assertFalse(data5['has_more']) # Get txs with hash reference expected6 = txs[:8] expected6.reverse() response6 = yield self.web.get( "transaction", { b'count': b'8', b'type': b'tx', b'timestamp': bytes(str(txs[8].timestamp), 'utf-8'), b'hash': bytes(txs[8].hash.hex(), 'utf-8'), b'page': b'next' }) data6 = response6.json_value() for expected, result in zip(expected6, data6['transactions']): self.assertEqual(expected.timestamp, result['timestamp']) self.assertEqual(expected.hash.hex(), result['tx_id']) self.assertTrue(data6['has_more'])
class SendTokensTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(SendTokensResource(self.manager)) self.web_mining = StubSite(MiningResource(self.manager)) self.web_balance = StubSite(BalanceResource(self.manager)) self.web_history = StubSite(HistoryResource(self.manager)) @inlineCallbacks def test_post(self): # Mining new block response_mining = yield self.web_mining.get("mining") data_mining = response_mining.json_value() block_bytes = resolve_block_bytes( block_bytes=data_mining['block_bytes']) yield self.web_mining.post( "mining", {'block_bytes': base64.b64encode(block_bytes).decode('utf-8')}) add_blocks_unlock_reward(self.manager) self.reactor.advance(10) # Unlocking wallet self.manager.wallet.unlock(b"MYPASS") # Sending token to random address without input # Options yield self.web.options("wallet/send_tokens") data_json = { "outputs": [{ "address": self.get_address(0), "value": 505 }], "inputs": [] } response = yield self.web.post("wallet/send_tokens", {'data': data_json}) data = response.json_value() self.assertTrue(data['success']) self.reactor.advance(10) # Asserting new balance response_balance = yield self.web_balance.get("wallet/balance") data_balance = response_balance.json_value() tokens_per_block = self.manager.get_tokens_issued_per_block(1) self.assertEqual(data_balance['balance'], { 'available': tokens_per_block - 505, 'locked': 0 }) # Getting history, so we can get the input response_history = yield self.web_history.get("wallet/history", { b'page': 1, b'count': 10 }) data_history = response_history.json_value() input_hash = data_history['history'][0]['tx_id'] # Sending token to random address with input wrong amount data_json = { "outputs": [{ "address": self.get_address(0), "value": 500 }], "inputs": [{ "tx_id": input_hash, "index": 0 }] } response2 = yield self.web.post("wallet/send_tokens", {'data': data_json}) data2 = response2.json_value() self.assertFalse(data2['success']) self.reactor.advance(10) # Sending duplicate input data_json_duplicate = { "outputs": [{ "address": self.get_address(0), "value": 19000 }], "inputs": [{ "tx_id": input_hash, "index": 0 }, { "tx_id": input_hash, "index": 0 }] } response_duplicate = yield self.web.post("wallet/send_tokens", {'data': data_json_duplicate}) data_duplicate = response_duplicate.json_value() self.assertFalse(data_duplicate['success']) # Sending token to random address with input right amount data_json2 = { "outputs": [{ "address": self.get_address(0), "value": self.manager.get_tokens_issued_per_block(1) - 505 }], "inputs": [{ "tx_id": input_hash, "index": 0 }] } response3 = yield self.web.post("wallet/send_tokens", {'data': data_json2}) data3 = response3.json_value() self.assertTrue(data3['success']) # Sending token to invalid addresses data_json3 = { "outputs": [{ "address": self.get_address(1), "value": 500 }], "inputs": [] } response_error1 = yield self.web.post("wallet/send_tokens", {'data': data_json3}) data_error1 = response_error1.json_value() self.assertFalse(data_error1['success']) data_json4 = { "outputs": [{ "address": "1234", "value": 500 }], "inputs": [] } response_error2 = yield self.web.post("wallet/send_tokens", {'data': data_json4}) data_error2 = response_error2.json_value() self.assertFalse(data_error2['success']) # Error insuficient funds data_json5 = { "outputs": [{ "address": self.get_address(0), "value": 5000000 }], "inputs": [] } response_error3 = yield self.web.post("wallet/send_tokens", {'data': data_json5}) data_error3 = response_error3.json_value() self.assertFalse(data_error3['success']) add_new_blocks(self.manager, 1, advance_clock=1) add_blocks_unlock_reward(self.manager) # Sending token with timelock data_timelock = { "outputs": [{ "address": self.get_address(0), "value": 505, "timelock": 1542995660 }], "inputs": [] } response_timelock = yield self.web.post("wallet/send_tokens", {'data': data_timelock}) data_response_timelock = response_timelock.json_value() self.assertTrue(data_response_timelock['success']) self.reactor.advance(5) # Sending token with timestamp data_timestamp = { "outputs": [{ "address": self.get_address(0), "value": 5 }], "inputs": [], "timestamp": int(self.reactor.seconds()) } response_timestamp = yield self.web.post("wallet/send_tokens", {'data': data_timestamp}) data_response_timestamp = response_timestamp.json_value() self.assertTrue(data_response_timestamp['success']) self.reactor.advance(5) # Sending token with timestamp=0 data_timestamp = { "outputs": [{ "address": self.get_address(0), "value": 5 }], "inputs": [], "timestamp": 0 } response_timestamp = yield self.web.post("wallet/send_tokens", {'data': data_timestamp}) data_response_timestamp = response_timestamp.json_value() self.assertTrue(data_response_timestamp['success']) @inlineCallbacks def test_tx_weight(self): add_new_blocks(self.manager, 3, advance_clock=1) add_blocks_unlock_reward(self.manager) self.reactor.advance(3) # Unlocking wallet self.manager.wallet.unlock(b"MYPASS") self.manager.test_mode = TestMode.DISABLED data_json = { "outputs": [{ "address": self.get_address(0), "value": 505 }], "inputs": [], "weight": 1 } response = yield self.web.post("wallet/send_tokens", {'data': data_json}) data = response.json_value() self.assertFalse(data['success']) def test_error_request(self): resource = SendTokensResource(self.manager) request = TestDummyRequest('POST', 'wallet/send_tokens', {}) self.assertIsNotNone(request._finishedDeferreds) resource._err_tx_resolve('Error', request) self.assertIsNone(request._finishedDeferreds)
def test_match_values(self): decode_resource = StubSite(NanoContractDecodeResource(self.manager)) execute_resource = StubSite(NanoContractExecuteResource(self.manager)) match_value_resource = StubSite( NanoContractMatchValueResource(self.manager)) pushtx_resource = StubSite( PushTxResource(self.manager, allow_non_standard_script=True)) signtx_resource = StubSite(SignTxResource(self.manager)) decodetx_resource = StubSite(DecodeTxResource(self.manager)) add_new_blocks(self.manager, 3) add_blocks_unlock_reward(self.manager) self.reactor.advance(3) # Options yield match_value_resource.options( "wallet/nano_contracts/match_values") total_value = self.manager.get_tokens_issued_per_block(1) address1 = self.get_address(0) data_post = { 'oracle_data_id': 'some_id', 'total_value': total_value, 'input_value': total_value, 'min_timestamp': 1, 'fallback_address': self.get_address(1), 'values': [{ 'address': address1, 'value': 300 }] } # Error missing parameter response_error = yield match_value_resource.post( "wallet/nano_contracts/match_value", data_post) data_error = response_error.json_value() self.assertFalse(data_error['success']) self.assertEqual(data_error['message'], 'Missing parameter: oracle_pubkey_hash') # create nano contract data_post['oracle_pubkey_hash'] = '6o6ul2c+sqAariBVW+CwNaSJb9w=' response = yield match_value_resource.post( "wallet/nano_contracts/match_value", data_post) data = response.json_value() self.assertTrue(data['success']) self.assertIsNotNone(data['hex_tx']) nano_contract_hex = data['hex_tx'] # Error missing parameter response_error = yield decode_resource.get( "wallet/nano_contracts/decode", {}) data_error = response_error.json_value() self.assertFalse(data_error['success']) self.assertEqual(data_error['message'], 'Missing parameter: hex_tx') # Error invalid hex response_error2 = yield decode_resource.get( "wallet/nano_contracts/decode", {b'hex_tx': b'123'}) data_error2 = response_error2.json_value() self.assertFalse(data_error2['success']) # Error valid hex but invalid tx struct response_error3 = yield decode_resource.get( "wallet/nano_contracts/decode", {b'hex_tx': b'1334'}) data_error3 = response_error3.json_value() self.assertFalse(data_error3['success']) # decode genesis_output = [ tx for tx in self.manager.tx_storage.get_all_genesis() if tx.is_block ][0].outputs[0] partial_tx = Transaction.create_from_struct( bytes.fromhex(nano_contract_hex)) partial_tx.outputs.append(genesis_output) response_decode = yield decode_resource.get( "wallet/nano_contracts/decode", {b'hex_tx': bytes(partial_tx.get_struct().hex(), 'utf-8')}) data = response_decode.json_value() self.assertTrue(data['success']) nano_contract = data['nano_contract'] self.assertIsNotNone(nano_contract) self.assertEqual(nano_contract['type'], 'NanoContractMatchValues') self.assertEqual(len(data['other_inputs']), 0) self.assertEqual(len(data['my_inputs']), 1) self.assertEqual(len(data['outputs']), 1) self.assertEqual(data['outputs'][0], genesis_output.to_human_readable()) address2 = self.get_address(2) data_put = { 'new_values': [{ 'address': address2, 'value': 500 }], 'input_value': total_value } # Error missing parameter response_error = yield match_value_resource.put( "wallet/nano_contracts/match_value", data_put) data_error = response_error.json_value() self.assertFalse(data_error['success']) self.assertEqual(data_error['message'], 'Missing parameter: hex_tx') # update data_put['hex_tx'] = partial_tx.get_struct().hex() response = yield match_value_resource.put( "wallet/nano_contracts/match_value", data_put) data = response.json_value() self.assertTrue(data['success']) self.assertIsNotNone(data['hex_tx']) # Error nano contract not found new_tx = Transaction.create_from_struct(partial_tx.get_struct()) new_tx.outputs = [] data_put['hex_tx'] = new_tx.get_struct().hex() response = yield match_value_resource.put( "wallet/nano_contracts/match_value", data_put) data = response.json_value() self.assertFalse(data['success']) # Error missing parameter response_error = yield signtx_resource.get("wallet/sign_tx", {}) data_error = response_error.json_value() self.assertFalse(data_error['success']) self.assertEqual(data_error['message'], 'Missing parameter: hex_tx') # Error wrong parameter value response_error2 = yield signtx_resource.get("wallet/sign_tx", { b'hex_tx': b'123', b'prepare_to_send': b'true' }) data_error2 = response_error2.json_value() self.assertFalse(data_error2['success']) # Error valid hex but wrong tx struct value response_error3 = yield signtx_resource.get("wallet/sign_tx", { b'hex_tx': b'1334', b'prepare_to_send': b'true' }) data_error3 = response_error3.json_value() self.assertFalse(data_error3['success']) # sign tx response = yield signtx_resource.get( "wallet/sign_tx", { b'hex_tx': bytes(nano_contract_hex, 'utf-8'), b'prepare_to_send': b'true' }) data = response.json_value() self.assertTrue(data['success']) nano_contract_hex = data['hex_tx'] # sign tx without preparing response2 = yield signtx_resource.get( "wallet/sign_tx", {b'hex_tx': bytes(nano_contract_hex, 'utf-8')}) data2 = response2.json_value() self.assertTrue(data2['success']) self.assertIsNotNone(data2['hex_tx']) # propagate tx response = yield pushtx_resource.get( "push_tx", {b'hex_tx': bytes(nano_contract_hex, 'utf-8')}) data = response.json_value() self.assertTrue(data['success']) self.reactor.advance(3) # get tx hash response = yield decodetx_resource.get( "decode_tx", {b'hex_tx': bytes(nano_contract_hex, 'utf-8')}) data = response.json_value() self.assertTrue(data['success']) hash_hex = data['tx']['hash'] # Options yield execute_resource.options("wallet/nano_contracts/execute") # Error no data response_error = yield execute_resource.post( "wallet/nano_contracts/execute") data_error = response_error.json_value() self.assertFalse(data_error['success']) # Error missing parameter data = { 'spent_tx_index': 0, 'oracle_data': 'B3NvbWVfaWQEW/xjGQIBLA==', 'oracle_signature': 'MEUCIGeqbmLRI6lrgXMy4sQEgK94F5m14oVL5Z7oLLVII7BUAiEApKTMuWlwvws574' '+jtqKW5/AuH+ICD0u+HyMyHe0aric=', 'oracle_pubkey': 'Awmloohhey8WhajdDURgvbk1z3JHX2vxDSBjz9uG9wEp', 'address': address1, 'value': total_value, } response_error2 = yield execute_resource.post( "wallet/nano_contracts/execute", data) data_error2 = response_error2.json_value() self.assertFalse(data_error2['success']) self.assertEqual(data_error2['message'], 'Missing parameter: spent_tx_id') # execute nano contract data['spent_tx_id'] = hash_hex response = yield execute_resource.post("wallet/nano_contracts/execute", data) data = response.json_value() self.assertTrue(data['success'])
class BaseMiningApiTest(_BaseResourceTest._ResourceTest): __test__ = False def setUp(self): super().setUp() self.get_block_template = StubSite( mining.GetBlockTemplateResource(self.manager)) self.submit_block = StubSite(mining.SubmitBlockResource(self.manager)) @inlineCallbacks def test_get_block_template_with_address(self): resp = yield self.get_block_template.get( '', {b'address': b'HC7w4j7mPet49BBN5a2An3XUiPvK6C1TL7'}) data = resp.json_value() self.assertEqual(len(data['parents']), 3) del data['parents'] del data['timestamp'] self.assertEqual( data, { 'version': 0, 'weight': 1.0, 'outputs': [{ 'value': 6400, 'token_data': 0, 'script': 'dqkUPW28v25nssvMMiWZR1alal4tOieIrA==' }], 'metadata': { 'hash': None, 'spent_outputs': [], 'received_by': [], 'children': [], 'conflict_with': [], 'voided_by': [], 'twins': [], 'validation': 'initial', 'accumulated_weight': 1.0, 'score': 0, 'height': 1, 'first_block': None, }, 'tokens': [], 'data': '', }) @inlineCallbacks def test_get_block_template_without_address(self): resp = yield self.get_block_template.get('') data = resp.json_value() self.assertEqual(len(data['parents']), 3) del data['parents'] del data['timestamp'] self.assertEqual( data, { 'version': 0, 'weight': 1.0, 'outputs': [{ 'value': 6400, 'token_data': 0, 'script': '' }], 'metadata': { 'hash': None, 'spent_outputs': [], 'received_by': [], 'children': [], 'conflict_with': [], 'voided_by': [], 'twins': [], 'validation': 'initial', # FIXME: change to 'full' when validations are enabled 'accumulated_weight': 1.0, 'score': 0, 'height': 1, 'first_block': None, }, 'tokens': [], 'data': '', }) @inlineCallbacks def test_get_block_template_while_node_syncing(self): self.manager._allow_mining_without_peers = False resp = yield self.get_block_template.get('') data = resp.json_value() self.assertEqual(data, { 'error': 'Node syncing', }) @inlineCallbacks def test_get_block_template_and_submit_block(self): from hathor.client import create_tx_from_dict resp = yield self.get_block_template.get( '', {b'address': b'HC7w4j7mPet49BBN5a2An3XUiPvK6C1TL7'}) data = resp.json_value() block = create_tx_from_dict(data) block.resolve(False) self.assertTrue(self.manager.propagate_tx(block))
class StratumResourceTest(_BaseResourceTest._ResourceTest): def _manager_kwargs(self): kwargs = super()._manager_kwargs() kwargs['stratum_port'] = 8123 return kwargs def setUp(self): super().setUp() self.web = StubSite(MiningStatsResource(self.manager)) @pytest.mark.skip(reason='broken') @inlineCallbacks def test_get(self): response = yield self.web.get('miners') data = response.json_value() self.assertEqual(data, []) @pytest.mark.skip(reason='broken') @inlineCallbacks def test_subscribe_and_mine(self): import json from hashlib import sha256 # boilerplate needed to exchange bytes transport = StringTransportWithDisconnection() protocol = self.manager.stratum_factory.buildProtocol( IPv4Address('TCP', '127.0.0.1', 8123)) transport.protocol = protocol protocol.makeConnection(transport) # subscribe req = {'jsonrpc': '2.0', 'id': '1', 'method': 'subscribe'} protocol.lineReceived(json.dumps(req).encode()) res = transport.value().split(JSONRPC.delimiter) self.assertEqual(len(res), 3) # check if we have subscribed response = yield self.web.get('miners') data = response.json_value() self.assertIsInstance(data, list) self.assertEqual(len(data), 1) res = data[0] del res['connection_start_time'] del res['miner_id'] self.assertEqual( res, { 'address': '127.0.0.1:8123', 'blocks_found': 0, 'completed_jobs': 0, 'estimated_hash_rate': 0.0 }) # mine a block # TODO: use predictable work instead of always repeating this work job = json.loads(res[1])['params'] job_id = job['job_id'] job_hash1 = sha256(bytes.fromhex(job['data'])) job_nonce_size = job['nonce_size'] job_max_nonce = 1 << (8 * job_nonce_size) job_target = 2**(256 - job['weight']) - 1 nonce = 0 while nonce < job_max_nonce: job_hash2 = job_hash1.copy() job_hash2.update(nonce.to_bytes(job_nonce_size, 'big')) if int(sha256(job_hash2.digest()).digest()[::-1].hex(), 16) < job_target: break nonce += 1 # FIXME: assuming nonce was found: exited loop through break # submit our work req = {'job_id': job_id, 'nonce': nonce} protocol.lineReceived(json.dumps(req).encode()) res = transport.value().split(JSONRPC.delimiter) self.assertEqual(len(res), 4) self.assertTrue(False) # check if our work has updated the stats response = yield self.web.get('miners') data = response.json_value() self.assertIsInstance(data, list) self.assertEqual(len(data), 1) res = data[0] del res['connection_start_time'] del res['miner_id'] self.assertEqual( res, # TODO: what is the actual estimated hash rate? should we test it? { 'address': '127.0.0.1:8123', 'blocks_found': 1, 'completed_jobs': 1, 'estimated_hash_rate': 0.0 })
class DecodeTxTest(_BaseResourceTest._ResourceTest): def setUp(self): super().setUp() self.web = StubSite(PushTxResource(self.manager)) self.web_tokens = StubSite(SendTokensResource(self.manager)) self.web_mining = StubSite(MiningResource(self.manager)) self.web_balance = StubSite(BalanceResource(self.manager)) self.web_history = StubSite(HistoryResource(self.manager)) @inlineCallbacks def _run_push_tx_test(self, is_post: bool) -> Generator: # Mining new block response_mining = yield self.web_mining.get('mining') data_mining = response_mining.json_value() block_bytes = resolve_block_bytes(block_bytes=data_mining['block_bytes']) yield self.web_mining.post('mining', {'block_bytes': base64.b64encode(block_bytes).decode('utf-8')}) # Unlocking wallet self.manager.wallet.unlock(b'MYPASS') # Creating a valid transaction to be pushed to the network blocks = add_new_blocks(self.manager, 3, advance_clock=2) add_blocks_unlock_reward(self.manager) tx_id = blocks[0].hash output = blocks[0].outputs[0] script_type_out = parse_address_script(output.script) assert script_type_out is not None address = script_type_out.address private_key = self.manager.wallet.get_private_key(address) script_out_addr = self.get_address(0) assert script_out_addr is not None output_address = decode_address(script_out_addr) value = self.manager.get_tokens_issued_per_block(1) o = TxOutput(value, create_output_script(output_address, None)) i = TxInput(tx_id, 0, b'') tx = Transaction(inputs=[i], outputs=[o]) data_to_sign = tx.get_sighash_all() public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(data_to_sign, private_key) i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes) tx.inputs = [i] tx.timestamp = int(self.clock.seconds()) tx.weight = self.manager.minimum_tx_weight(tx) tx.parents = self.manager.get_new_tx_parents(tx.timestamp) tx.resolve() push_tx_fn = self.web.post if is_post else self.web.get hex_param = 'hex_tx' if is_post else b'hex_tx' force_param = 'force' if is_post else b'force' tx_hex = tx.get_struct().hex() hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8') response = yield push_tx_fn('push_tx', {hex_param: hex_data}) data = response.json_value() self.assertTrue(data['success']) # Sending token to random address without input data_json = {'outputs': [{'address': self.get_address(0), 'value': 5}], 'inputs': []} yield self.web_tokens.post('wallet/send_tokens', {'data': data_json}) # modify tx so it will be a double spending, then rejected tx.weight += 0.1 tx.resolve() tx_hex = tx.get_struct().hex() hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8') response_success = yield push_tx_fn('push_tx', {hex_param: hex_data}) data_success = response_success.json_value() self.assertFalse(data_success['success']) # invalid transaction, without forcing tx.timestamp = 5 tx.inputs = [TxInput(blocks[1].hash, 0, b'')] script_type_out = parse_address_script(blocks[1].outputs[0].script) assert script_type_out is not None private_key = self.manager.wallet.get_private_key(script_type_out.address) data_to_sign = tx.get_sighash_all() public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(data_to_sign, private_key) tx.inputs[0].data = P2PKH.create_input_data(public_key_bytes, signature_bytes) tx_hex = tx.get_struct().hex() hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8') response = yield push_tx_fn('push_tx', {hex_param: hex_data}) data = response.json_value() self.assertFalse(data['success']) # force tx_hex = tx.get_struct().hex() hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8') response = yield push_tx_fn('push_tx', {hex_param: hex_data, force_param: True if is_post else b'true'}) data = response.json_value() self.assertFalse(data['success']) # Invalid tx (don't have inputs) genesis_tx = next(x for x in self.manager.tx_storage.get_all_genesis() if x.is_transaction) genesis_hex = genesis_tx.get_struct().hex() hex_data = genesis_hex if is_post else bytes(genesis_hex, 'utf-8') response_genesis = yield push_tx_fn('push_tx', {hex_param: hex_data}) data_genesis = response_genesis.json_value() self.assertFalse(data_genesis['success']) # Invalid tx hex invalid_hex_data = 'a12c' if is_post else b'a12c' response_error2 = yield push_tx_fn('push_tx', {hex_param: invalid_hex_data}) data_error2 = response_error2.json_value() self.assertFalse(data_error2['success']) # Token creation tx tx2 = create_tokens(self.manager, address, mint_amount=100, propagate=False) tx2_hex = tx2.get_struct().hex() hex_data = tx2_hex if is_post else bytes(tx2_hex, 'utf-8') response = yield push_tx_fn('push_tx', {hex_param: hex_data}) data = response.json_value() self.assertTrue(data['success']) @inlineCallbacks def _run_invalid_params_test(self, is_post: bool) -> Generator: push_tx_fn = self.web.post if is_post else self.web.get hex_param = 'hex_tx' if is_post else b'hex_tx' # Missing hex response = yield push_tx_fn('push_tx') data = response.json_value() self.assertFalse(data['success']) # Missing hex 2 response = yield push_tx_fn('push_tx', {}) data = response.json_value() self.assertFalse(data['success']) # Invalid hex invalid_hex_data = 'XXXX' if is_post else b'XXXX' response = yield push_tx_fn('push_tx', {hex_param: invalid_hex_data}) data = response.json_value() self.assertFalse(data['success']) def test_push_tx_get(self): self._run_push_tx_test(False) def test_push_tx_post(self): self._run_push_tx_test(True) def test_invalid_params_get(self): self._run_invalid_params_test(False) def test_invalid_params_post(self): self._run_invalid_params_test(True)