예제 #1
0
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)
예제 #2
0
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})
예제 #3
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)
예제 #4
0
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)
예제 #5
0
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)
예제 #6
0
    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'])
예제 #7
0
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)
예제 #8
0
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)
예제 #9
0
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'])
예제 #11
0
    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)
예제 #12
0
    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'])
예제 #13
0
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'])
예제 #14
0
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__)
예제 #15
0
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'])
예제 #16
0
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'])
예제 #17
0
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)
예제 #18
0
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')
예제 #19
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'])
예제 #21
0
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'])
예제 #22
0
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'])
예제 #23
0
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'])
예제 #24
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 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'])
예제 #25
0
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'])
예제 #26
0
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)
예제 #27
0
    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'])
예제 #28
0
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))
예제 #29
0
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
            })
예제 #30
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)