def setUp(self):
     super().setUp()
     self.web = StubSite(MiningInfoResource(self.manager))
     self.manager.wallet.unlock(b'MYPASS')
class BaseSendTokensTest(_BaseResourceTest._ResourceTest):
    __test__ = False

    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_new_blocks(self.manager, 1, advance_clock=1)  # XXX: adding extra block, not sure why this is needed
        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):
        _set_test_mode(TestMode.DISABLED)
        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")

        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)
Example #3
0
class SendTokensTest(_BaseResourceTest._ResourceTest):
    def setUp(self):
        super().setUp()

        self.network = 'testnet'
        self.manager = self.create_peer(self.network,
                                        unlock_wallet=True,
                                        wallet_index=True)

        sendtokens_resource = SendTokensResource(self.manager)
        sendtokens_resource.sleep_seconds = 0.1

        self.web = StubSite(sendtokens_resource)
        self.web_address_history = StubSite(
            AddressHistoryResource(self.manager))

    @inlineCallbacks
    def test_post(self):
        # Unlocking wallet
        self.manager.wallet.unlock(b'MYPASS')

        blocks = add_new_blocks(self.manager, 3, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        blocks_tokens = [
            sum(txout.value for txout in blk.outputs) for blk in blocks
        ]

        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID].available,
            sum(blocks_tokens))

        # Options
        yield self.web.options('thin_wallet/send_tokens')

        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 = blocks_tokens[0]
        o = TxOutput(value, create_output_script(output_address, None))
        o_invalid_amount = TxOutput(value - 1,
                                    create_output_script(output_address, None))
        i = TxInput(tx_id, 0, b'')

        # wrong weight
        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 = 0

        response = yield self.web.post('thin_wallet/send_tokens',
                                       {'tx_hex': tx.get_struct().hex()})
        data = response.json_value()
        self.assertFalse(data['success'])

        # Error wrong amount
        tx2 = Transaction(inputs=[i], outputs=[o_invalid_amount])

        data_to_sign = tx2.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)
        tx2.inputs = [i]
        tx2.timestamp = int(self.clock.seconds())
        tx2.weight = self.manager.minimum_tx_weight(tx2)

        response_wrong_amount = yield self.web.post(
            'thin_wallet/send_tokens', {'tx_hex': tx2.get_struct().hex()})
        data_wrong_amount = response_wrong_amount.json_value()
        self.assertFalse(data_wrong_amount['success'])

        # successful tx
        tx3 = Transaction(inputs=[i], outputs=[o])

        data_to_sign = tx3.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)
        tx3.inputs = [i]
        tx3.timestamp = int(self.clock.seconds())
        tx3.weight = self.manager.minimum_tx_weight(tx3)

        # Then send tokens
        response = yield self.web.post('thin_wallet/send_tokens',
                                       {'tx_hex': tx3.get_struct().hex()})
        data = response.json_value()
        self.assertTrue(data['success'])

        # Trying to send a double spending will not have success
        self.clock.advance(5)
        tx3.timestamp = int(self.clock.seconds())
        response = yield self.web.post('thin_wallet/send_tokens',
                                       {'tx_hex': tx3.get_struct().hex()})
        data_error = response.json_value()
        self.assertFalse(data_error['success'])
        self.clock.advance(5)

        # Check if tokens were really sent
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID].available,
            sum(blocks_tokens[:-1]))

        response_history = yield self.web_address_history.get(
            'thin_wallet/address_history', {
                b'addresses[]': address.encode(),
            })

        response_data = response_history.json_value()['history']
        self.assertIn(data['tx']['hash'], [x['tx_id'] for x in response_data])

        # Create token tx
        tx4 = create_tokens(self.manager,
                            address,
                            mint_amount=100,
                            propagate=False)
        tx4.nonce = 0
        tx4.timestamp = int(self.clock.seconds())
        response = yield self.web.post('thin_wallet/send_tokens',
                                       {'tx_hex': tx4.get_struct().hex()})
        data = response.json_value()
        self.assertTrue(data['success'])
#
#       TODO these tests were causing timeouts in CI server [yan - 01.04.2019]
#       TODO add to top imports
#       from twisted.internet.defer import CancelledError, inlineCallbacks
#       from twisted.python.failure import Failure
#        def get_new_tx_struct(weight=0):
#            tx = Transaction(inputs=[i], outputs=[o])
#            tx.inputs = tx3.inputs
#            self.clock.advance(5)
#            tx.timestamp = int(self.clock.seconds())
#            if weight == 0:
#                weight = self.manager.minimum_tx_weight(tx)
#            tx.weight = weight
#            return tx.get_struct().hex()
#
#        # Making pow threads full
#        deferreds = []
#        for x in range(settings.MAX_POW_THREADS):
#            d = self.web.post('thin_wallet/send_tokens', {'tx_hex': get_new_tx_struct(50)})
#            d.addErrback(lambda err: None)
#            deferreds.append(d)
#
#        # All threads are in use
#        response = yield self.web.post('thin_wallet/send_tokens', {'tx_hex': get_new_tx_struct(1)})
#        data = response.json_value()
#        self.assertFalse(data['success'])
#
#        # Releasing one thread
#        d = deferreds.pop()
#        d.request.processingFailed(Failure(CancelledError()))
#
#        # Waiting for thread to finish
#        yield d.request.thread_deferred
#
#        # Now you can send
#        response = yield self.web.post('thin_wallet/send_tokens', {'tx_hex': get_new_tx_struct(1)})
#        data = response.json_value()
#        self.assertTrue(data['success'])
#
#        # Releasing all other threads
#        for d in deferreds:
#            d.request.processingFailed(Failure(CancelledError()))
#
#        # Waiting for all threads to finish
#        for d in deferreds:
#            yield d.request.thread_deferred

    def test_error_request(self):
        resource = SendTokensResource(self.manager)
        request = TestDummyRequest('POST', 'thin_wallet/send_tokens', {})

        self.assertIsNotNone(request._finishedDeferreds)
        resource._err_tx_resolve('Error', request)
        self.assertIsNone(request._finishedDeferreds)

    @inlineCallbacks
    def test_token(self):
        self.manager.wallet.unlock(b'MYPASS')
        resource = StubSite(TokenResource(self.manager))

        # test list of tokens empty
        response_list1 = yield resource.get('thin_wallet/token')
        data_list1 = response_list1.json_value()
        self.assertTrue(data_list1['success'])
        self.assertEqual(len(data_list1['tokens']), 0)

        # test invalid token id
        response = yield resource.get('thin_wallet/token',
                                      {b'id': 'vvvv'.encode()})
        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,
                           use_genesis=False)
        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 list of tokens with one token
        response_list2 = yield resource.get('thin_wallet/token')
        data_list2 = response_list2.json_value()
        self.assertTrue(data_list2['success'])
        self.assertEqual(len(data_list2['tokens']), 1)
        self.assertEqual(data_list2['tokens'][0]['name'], token_name)
        self.assertEqual(data_list2['tokens'][0]['symbol'], token_symbol)
        self.assertEqual(data_list2['tokens'][0]['uid'], tx.hash.hex())

        token_name2 = 'New Token'
        token_symbol2 = 'NTK'
        tx2 = create_tokens(self.manager,
                            mint_amount=amount,
                            token_name=token_name2,
                            token_symbol=token_symbol2,
                            use_genesis=False)

        token_name3 = 'Wat Coin'
        token_symbol3 = 'WTC'
        tx3 = create_tokens(self.manager,
                            mint_amount=amount,
                            token_name=token_name3,
                            token_symbol=token_symbol3,
                            use_genesis=False)

        # test list of tokens with 3 tokens
        response_list3 = yield resource.get('thin_wallet/token')
        data_list3 = response_list3.json_value()
        self.assertTrue(data_list3['success'])
        self.assertEqual(len(data_list3['tokens']), 3)
        token1 = {
            'uid': tx.hash.hex(),
            'name': token_name,
            'symbol': token_symbol
        }
        token2 = {
            'uid': tx2.hash.hex(),
            'name': token_name2,
            'symbol': token_symbol2
        }
        token3 = {
            'uid': tx3.hash.hex(),
            'name': token_name3,
            'symbol': token_symbol3
        }
        self.assertIn(token1, data_list3['tokens'])
        self.assertIn(token2, data_list3['tokens'])
        self.assertIn(token3, data_list3['tokens'])

        # 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'])

    @inlineCallbacks
    def test_token_history(self):
        self.manager.wallet.unlock(b'MYPASS')
        resource = StubSite(TokenHistoryResource(self.manager))

        add_new_blocks(self.manager, 1, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        tx = create_tokens(self.manager,
                           mint_amount=100,
                           token_name='Teste',
                           token_symbol='TST')
        token_uid = tx.tokens[0]

        response = yield resource.get('thin_wallet/token_history', {
            b'id': token_uid.hex().encode(),
            b'count': 3
        })
        data = response.json_value()
        # Success returning the token creation tx
        self.assertTrue(data['success'])
        self.assertFalse(data['has_more'])
        self.assertEqual(1, len(data['transactions']))
        self.assertEqual(tx.hash.hex(), data['transactions'][0]['tx_id'])

        response = yield resource.get('thin_wallet/token_history', {
            b'id': b'123',
            b'count': 3
        })
        data = response.json_value()
        # Fail because token is unknown
        self.assertFalse(data['success'])

        # Create a tx with this token, so we can have more tx in the history
        output = tx.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))
        o = TxOutput(100, create_output_script(output_address, None), 1)
        i = TxInput(tx.hash, 0, b'')

        tx2 = Transaction(inputs=[i], outputs=[o], tokens=[token_uid])
        data_to_sign = tx2.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)
        tx2.inputs = [i]
        tx2.timestamp = int(self.clock.seconds())
        tx2.weight = self.manager.minimum_tx_weight(tx2)
        tx2.parents = self.manager.get_new_tx_parents()
        tx2.resolve()
        self.manager.propagate_tx(tx2)

        # Now we have 2 txs with this token
        response = yield resource.get('thin_wallet/token_history', {
            b'id': token_uid.hex().encode(),
            b'count': 3
        })
        data = response.json_value()
        # Success returning the token creation tx and newly created tx
        self.assertTrue(data['success'])
        self.assertFalse(data['has_more'])
        self.assertEqual(2, len(data['transactions']))
        self.assertEqual(tx2.hash.hex(), data['transactions'][0]['tx_id'])
        self.assertEqual(tx.hash.hex(), data['transactions'][1]['tx_id'])

        response = yield resource.get('thin_wallet/token_history', {
            b'id': token_uid.hex().encode(),
            b'count': 1
        })
        data = response.json_value()
        # Testing has_more
        self.assertTrue(data['success'])
        self.assertTrue(data['has_more'])
        self.assertEqual(1, len(data['transactions']))

        response = yield resource.get(
            'thin_wallet/token_history', {
                b'id': token_uid.hex().encode(),
                b'count': 10,
                b'page': b'next',
                b'hash': tx2.hash.hex().encode(),
                b'timestamp': str(tx2.timestamp).encode(),
            })
        data = response.json_value()
        # Testing next
        self.assertTrue(data['success'])
        self.assertFalse(data['has_more'])
        self.assertEqual(1, len(data['transactions']))
        self.assertEqual(tx.hash.hex(), data['transactions'][0]['tx_id'])

        response = yield resource.get(
            'thin_wallet/token_history', {
                b'id': token_uid.hex().encode(),
                b'count': 10,
                b'page': b'previous',
                b'hash': tx.hash.hex().encode(),
                b'timestamp': str(tx.timestamp).encode(),
            })
        data = response.json_value()
        # Testing previous
        self.assertTrue(data['success'])
        self.assertFalse(data['has_more'])
        self.assertEqual(1, len(data['transactions']))
        self.assertEqual(tx2.hash.hex(), data['transactions'][0]['tx_id'])

        response = yield resource.get(
            'thin_wallet/token_history', {
                b'id': token_uid.hex().encode(),
                b'count': 10,
                b'page': b'previous',
                b'hash': tx2.hash.hex().encode(),
                b'timestamp': str(tx2.timestamp).encode(),
            })
        data = response.json_value()
        # Testing previous from first
        self.assertTrue(data['success'])
        self.assertFalse(data['has_more'])
        self.assertEqual(0, len(data['transactions']))
class GraphvizTest(_BaseResourceTest._ResourceTest):
    def setUp(self):
        super().setUp()
        resource = GraphvizLegacyResource(self.manager)
        resource.isLeaf = True
        self.web = StubSite(resource)

        # Unlocking wallet
        self.manager.wallet.unlock(b'MYPASS')

        # Creating blocks, txs and a conflict tx to test graphviz with it
        add_new_blocks(self.manager, 2, advance_clock=2)
        add_blocks_unlock_reward(self.manager)
        txs = add_new_transactions(self.manager, 2, advance_clock=2)
        tx = txs[0]

        self.tx2 = Transaction.create_from_struct(tx.get_struct())
        self.tx2.parents = [tx.parents[1], tx.parents[0]]
        self.tx2.resolve()

        self.manager.propagate_tx(self.tx2)

    @inlineCallbacks
    def test_get(self):
        # With parameters
        response = yield self.web.get('graphviz', {
            b'format': b'dot',
            b'weight': b'true',
            b'acc_weight': b'true'
        })
        data = response.written[0]
        self.assertIsNotNone(data)

        # Without parameters
        response2 = yield self.web.get('graphviz', {})
        data2 = response2.written[0]
        self.assertIsNotNone(data2)

        # Funds graph without parameter
        response3 = yield self.web.get('graphviz', {b'funds': b'true'})
        data3 = response3.written[0]
        self.assertIsNotNone(data3)

        # Funds graph with parameter
        response4 = yield self.web.get('graphviz', {
            b'funds': b'true',
            b'weight': b'true',
            b'acc_weight': b'true'
        })
        data4 = response4.written[0]
        self.assertIsNotNone(data4)

        # Tx neighbor graph
        response5 = yield self.web.get(
            'graphviz', {
                b'tx': self.tx2.hash_hex.encode('utf-8'),
                b'graph_type': b'funds',
                b'max_level': b'2'
            })
        data5 = response5.written[0]
        self.assertIsNotNone(data5)

        # Tx neighbor error
        response6 = yield self.web.get(
            'graphviz', {
                b'tx': self.tx2.hash_hex.encode('utf-8'),
                b'graph_type': b'funds',
                b'max_level': b'20'
            })
        data6 = response6.json_value()
        self.assertFalse(data6['success'])

    def test_parse_arg(self):
        resource = GraphvizLegacyResource(self.manager)

        false_args = ['false', 'False', '0', None, 0, False]
        for arg in false_args:
            self.assertFalse(resource.parseBoolArg(arg))

        true_args = ['true', 'True', '1', 1, True]
        for arg in true_args:
            self.assertTrue(resource.parseBoolArg(arg))

    def test_error_request(self):
        resource = GraphvizLegacyResource(self.manager)
        request = TestDummyRequest('GET', 'graphviz', {})

        self.assertIsNotNone(request._finishedDeferreds)
        resource._err_tx_resolve('Error', request)
        self.assertIsNone(request._finishedDeferreds)
Example #5
0
 def setUp(self):
     super().setUp()
     self.web = StubSite(MiningResource(self.manager))
Example #6
0
 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))
Example #7
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))

    @skip('broken')
    @inlineCallbacks
    def test_get(self):
        response = yield self.web.get('miners')
        data = response.json_value()
        self.assertEqual(data, [])

    @skip('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}
        )
Example #8
0
class BaseTransactionTest(_BaseResourceTest._ResourceTest):
    __test__ = False

    def setUp(self):
        super().setUp()
        self.web = StubSite(CreateTxResource(self.manager))
        self.manager.wallet.unlock(b'MYPASS')
        self.spent_blocks = add_new_blocks(self.manager, 10)
        self.unspent_blocks = add_blocks_unlock_reward(self.manager)
        add_blocks_unlock_reward(self.manager)
        self.unspent_address = self.manager.wallet.get_unused_address()
        self.unspent_tx = add_new_tx(self.manager, self.unspent_address, 100)
        self.unspent_tx2 = add_new_tx(self.manager, self.unspent_address, 200)
        self.unspent_tx3 = add_new_tx(self.manager, self.unspent_address, 300)
        add_blocks_unlock_reward(self.manager)

    # Example from the design:
    #
    # POST request body:
    #
    # {
    #   "inputs": [
    #     {
    #       "tx_id": "000005551d7740fd7d3c0acc50b5677fdd844f1225985aa431e1712af2a2fd89",
    #       "index": 1
    #     }
    #   ],
    #   "outputs": [
    #     {
    #       "address": "HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A",
    #       "value": 5600
    #     }
    #   ]
    # }
    #
    # POST response body:
    #
    # {
    #   "success": true,
    #   "hex_data": "0001000101000005551d7740fd7d3c0acc50b5677fdd844f1225985aa431e171\
    # 2af2a2fd89010000000015e000001976a914afa600556bf43ece9b8e0486baa31\
    # bd46a82c3af88ac40310c373eed982e5f63d94d0200000000b0e8be665f308f1d\
    # 48d2201060846203280062b1cccc4e3d657486e90000000071c0d2cafa192b421\
    # bb5727c84174c999f9400d3be74331e7feba08a00000000",
    #   "data": {
    #     "timestamp": 1600379213,
    #     "version": 1,
    #     "weight": 17.047717984205683,
    #     "parents": [
    #       "00000000b0e8be665f308f1d48d2201060846203280062b1cccc4e3d657486e9",
    #       "0000000071c0d2cafa192b421bb5727c84174c999f9400d3be74331e7feba08a"
    #     ],
    #     "inputs": [
    #       {
    #         "tx_id": "000005551d7740fd7d3c0acc50b5677fdd844f1225985aa431e1712af2a2fd89",
    #         "index": 1,
    #         "data": ""
    #       }
    #     ],
    #     "outputs": [
    #       {
    #         "value": 5600,
    #         "token_data": 0,
    #         "script": "dqkUr6YAVWv0Ps6bjgSGuqMb1GqCw6+IrA=="
    #       }
    #     ],
    #     "tokens": []
    #   }
    # }

    @inlineCallbacks
    def test_spend_block(self):
        block = self.unspent_blocks[0]
        address = 'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A'
        script = create_base_script(address).get_script()
        resp = (yield self.web.post(
            'create_tx', {
                'inputs': [{
                    'tx_id': block.hash_hex,
                    'index': 0,
                }],
                'outputs': [{
                    'address': address,
                    'value': 6400,
                }]
            })).json_value()
        self.assertEqual(resp['success'], True)
        data = resp['data']
        hex_data = resp['hex_data']
        struct_bytes = bytes.fromhex(hex_data)
        tx = Transaction.create_from_struct(struct_bytes)
        tx_data = tx.to_json()
        del tx_data['hash']
        del tx_data['nonce']
        self.assertEqual(data, tx_data)
        self.assertEqual(len(tx.inputs), 1)
        self.assertEqual(tx.inputs[0].tx_id, block.hash)
        self.assertEqual(tx.inputs[0].index, 0)
        self.assertEqual(tx.inputs[0].data, b'')
        self.assertEqual(len(tx.outputs), 1)
        self.assertEqual(tx.outputs[0].value, 6400)
        self.assertEqual(tx.outputs[0].token_data, 0)
        self.assertEqual(tx.outputs[0].script, script)

    @inlineCallbacks
    def test_spend_tx(self):
        src_tx = self.unspent_tx
        address = 'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A'
        script = create_base_script(address).get_script()
        resp = (yield self.web.post(
            'create_tx', {
                'inputs': [{
                    'tx_id': src_tx.hash_hex,
                    'index': 1,
                }],
                'outputs': [{
                    'address': address,
                    'value': 100,
                }]
            })).json_value()
        self.assertEqual(resp['success'], True)
        data = resp['data']
        hex_data = resp['hex_data']
        struct_bytes = bytes.fromhex(hex_data)
        tx = Transaction.create_from_struct(struct_bytes)
        tx_data = tx.to_json()
        del tx_data['hash']
        del tx_data['nonce']
        self.assertEqual(data, tx_data)
        self.assertEqual(len(tx.inputs), 1)
        self.assertEqual(tx.inputs[0].tx_id, src_tx.hash)
        self.assertEqual(tx.inputs[0].index, 1)
        self.assertEqual(tx.inputs[0].data, b'')
        self.assertEqual(len(tx.outputs), 1)
        self.assertEqual(tx.outputs[0].value, 100)
        self.assertEqual(tx.outputs[0].token_data, 0)
        self.assertEqual(tx.outputs[0].script, script)

    @inlineCallbacks
    def test_spend_tx_by_script(self):
        src_tx = self.unspent_tx
        address = 'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A'
        script = create_base_script(address).get_script()
        script_str = base64.b64encode(script).decode('utf-8')
        resp = (yield self.web.post(
            'create_tx', {
                'inputs': [{
                    'tx_id': src_tx.hash_hex,
                    'index': 1,
                }],
                'outputs': [{
                    'script': script_str,
                    'value': 100,
                }]
            })).json_value()
        self.assertEqual(resp['success'], True)
        data = resp['data']
        hex_data = resp['hex_data']
        struct_bytes = bytes.fromhex(hex_data)
        tx = Transaction.create_from_struct(struct_bytes)
        tx_data = tx.to_json()
        del tx_data['hash']
        del tx_data['nonce']
        self.assertEqual(data, tx_data)
        self.assertEqual(len(tx.inputs), 1)
        self.assertEqual(tx.inputs[0].tx_id, src_tx.hash)
        self.assertEqual(tx.inputs[0].index, 1)
        self.assertEqual(tx.inputs[0].data, b'')
        self.assertEqual(len(tx.outputs), 1)
        self.assertEqual(tx.outputs[0].value, 100)
        self.assertEqual(tx.outputs[0].token_data, 0)
        self.assertEqual(tx.outputs[0].script, script)

    @inlineCallbacks
    def test_tx_propagate(self):
        _set_test_mode(
            TestMode.DISABLED)  # disable test_mode so the weight is not 1
        src_tx = self.unspent_tx
        output_address = 'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A'
        resp = (yield self.web.post(
            'create_tx', {
                'inputs': [{
                    'tx_id': src_tx.hash_hex,
                    'index': 1,
                }],
                'outputs': [{
                    'address': output_address,
                    'value': 100,
                }]
            })).json_value()
        self.assertEqual(resp['success'], True)
        data = resp['data']
        hex_data = resp['hex_data']
        struct_bytes = bytes.fromhex(hex_data)
        orig_tx = Transaction.create_from_struct(struct_bytes)
        tx = orig_tx.clone()
        tx_data = tx.to_json()
        del tx_data['hash']
        del tx_data['nonce']
        self.assertEqual(data, tx_data)
        data_to_sign = tx.get_sighash_all()
        private_key = self.manager.wallet.get_private_key(self.unspent_address)
        public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(
            data_to_sign, private_key)
        input_data = P2PKH.create_input_data(public_key_bytes, signature_bytes)
        tx.inputs[0].data = input_data
        # XXX: tx.resolve is a bit CPU intensive, but not so much as to make this test disabled by default
        tx.resolve(False)
        self.assertTrue(self.manager.propagate_tx(tx))

    @inlineCallbacks
    def test_tx_propagate_multiple_inputs(self):
        _set_test_mode(
            TestMode.DISABLED)  # disable test_mode so the weight is not 1
        output_address = 'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A'
        resp = (yield self.web.post(
            'create_tx', {
                'inputs': [
                    {
                        'tx_id': self.unspent_tx.hash_hex,
                        'index': 1,
                    },
                    {
                        'tx_id': self.unspent_tx2.hash_hex,
                        'index': 1,
                    },
                    {
                        'tx_id': self.unspent_tx3.hash_hex,
                        'index': 1,
                    },
                ],
                'outputs': [
                    {
                        'address': output_address,
                        'value': 600,
                    },
                ]
            })).json_value()
        self.assertEqual(resp['success'], True)
        data = resp['data']
        hex_data = resp['hex_data']
        struct_bytes = bytes.fromhex(hex_data)
        orig_tx = Transaction.create_from_struct(struct_bytes)
        tx = orig_tx.clone()
        tx_data = tx.to_json()
        del tx_data['hash']
        del tx_data['nonce']
        self.assertEqual(data, tx_data)
        data_to_sign = tx.get_sighash_all()
        private_key = self.manager.wallet.get_private_key(self.unspent_address)
        public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(
            data_to_sign, private_key)
        input_data = P2PKH.create_input_data(public_key_bytes, signature_bytes)
        tx.inputs[0].data = input_data
        tx.inputs[1].data = input_data
        tx.inputs[2].data = input_data
        # XXX: tx.resolve is a bit CPU intensive, but not so much as to make this test disabled by default
        tx.resolve(False)
        self.assertTrue(self.manager.propagate_tx(tx))

    @inlineCallbacks
    def test_already_spent(self):
        block = self.spent_blocks[0]
        resp = (yield self.web.post(
            'create_tx', {
                'inputs': [{
                    'tx_id': block.hash_hex,
                    'index': 0,
                }],
                'outputs': [{
                    'address': 'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A',
                    'value': 6400,
                }]
            })).json_value()
        self.assertEqual(
            resp, {
                'error': 'At least one of your inputs has already been spent.',
            })

    @inlineCallbacks
    def test_invalid_value(self):
        resp = (yield self.web.post(
            'create_tx', {
                'inputs': [{
                    'tx_id': self.unspent_tx.hash_hex,
                    'index': 1,
                }],
                'outputs': [{
                    'address': 'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A',
                    'value': 101,
                }]
            })).json_value()
        self.assertEqual(
            resp, {
                'error':
                'HTR balance is different than expected. (amount=1, expected=0)'
            })

    @inlineCallbacks
    def test_invalid_value2(self):
        resp = (yield self.web.post(
            'create_tx', {
                'inputs': [{
                    'tx_id': self.unspent_tx.hash_hex,
                    'index': 1,
                }],
                'outputs': [{
                    'address': 'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A',
                    'value': 99,
                }]
            })).json_value()
        self.assertEqual(
            resp, {
                'error':
                'HTR balance is different than expected. (amount=-1, expected=0)'
            })

    @inlineCallbacks
    def test_invalid_address(self):
        resp = (yield self.web.post(
            'create_tx', {
                'inputs': [{
                    'tx_id': self.unspent_tx.hash_hex,
                    'index': 1,
                }],
                'outputs': [{
                    'address': 'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199Aa',
                    'value': 99,
                }]
            })).json_value()
        self.assertEqual(resp, {'error': 'Address size must have 25 bytes'})
class BaseTransactionTest(_BaseResourceTest._ResourceTest):
    __test__ = False

    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',
            })
Example #10
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 = next(x for x in self.manager.tx_storage.get_all_genesis()
                          if x.is_block)
        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()
        dict_test['nonce'] = str(dict_test['nonce'])
        if genesis_tx.is_block:
            dict_test['height'] = genesis_tx.calculate_height()
        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'])

    @inlineCallbacks
    def test_invalid_params(self):
        # Add some blocks and txs
        add_new_blocks(self.manager, 4, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        add_new_transactions(self.manager, 3)

        # invalid count
        response = yield self.web.get("transaction", {
            b'count': b'a',
            b'type': b'block'
        })
        data = response.json_value()
        self.assertFalse(data['success'])

        # missing type
        response = yield self.web.get("transaction", {b'count': b'3'})
        data = response.json_value()
        self.assertFalse(data['success'])

        # invalid type
        response = yield self.web.get("transaction", {
            b'count': b'3',
            b'type': b'block1'
        })
        data = response.json_value()
        self.assertFalse(data['success'])

        # missing timestamp
        response = yield self.web.get(
            "transaction", {
                b'count':
                b'3',
                b'type':
                b'block',
                b'hash':
                bytes(
                    '0000000043bae7193ae512e8e6e6cd666ef3ea46db6df63bd22f201c5fd682ea',
                    'utf-8')
            })
        data = response.json_value()
        self.assertFalse(data['success'])

        # invalid timestamp
        response = yield self.web.get(
            "transaction", {
                b'count':
                b'3',
                b'type':
                b'block',
                b'hash':
                bytes(
                    '0000000043bae7193ae512e8e6e6cd666ef3ea46db6df63bd22f201c5fd682ea',
                    'utf-8'),
                b'timestamp':
                b'aa'
            })
        data = response.json_value()
        self.assertFalse(data['success'])

        # missing page
        response = yield self.web.get(
            "transaction", {
                b'count':
                b'3',
                b'type':
                b'block',
                b'hash':
                bytes(
                    '0000000043bae7193ae512e8e6e6cd666ef3ea46db6df63bd22f201c5fd682ea',
                    'utf-8'),
                b'timestamp':
                b'1579716659'
            })
        data = response.json_value()
        self.assertFalse(data['success'])

        # invalid timestamp
        response = yield self.web.get(
            "transaction", {
                b'count':
                b'3',
                b'type':
                b'block',
                b'hash':
                bytes(
                    '0000000043bae7193ae512e8e6e6cd666ef3ea46db6df63bd22f201c5fd682ea',
                    'utf-8'),
                b'timestamp':
                b'1579716659',
                b'page':
                b'next1'
            })
        data = response.json_value()
        self.assertFalse(data['success'])
Example #11
0
 def setUp(self):
     super().setUp()
     self.web = StubSite(AddPeersResource(self.manager))
Example #12
0
 def setUp(self):
     super().setUp()
     self.web = StubSite(DashboardTransactionResource(self.manager))
Example #13
0
    def setUp(self):
        super().setUp()
        self.web = StubSite(StatusResource(self.manager))

        self.manager2 = self.create_peer('testnet')
        self.conn1 = FakeConnection(self.manager, self.manager2)
Example #14
0
class BaseStatusTest(_BaseResourceTest._ResourceTest):
    __test__ = False

    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'])
Example #15
0
 def setUp(self):
     super().setUp()
     self.web = StubSite(PushTxResource(self.manager))
     self.web_tokens = StubSite(SendTokensResource(self.manager))
 def setUp(self):
     super().setUp()
     self.web = StubSite(TxParentsResource(self.manager))
Example #17
0
class UnlockTest(_BaseResourceTest._ResourceTest):
    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'])
Example #18
0
 def setUp(self):
     super().setUp()
     self.web = StubSite(ProfilerResource(self.manager))
Example #19
0
class TestWebsocket(_BaseResourceTest._ResourceTest):
    def setUp(self):
        super().setUp()

        self.network = 'testnet'
        self.manager = self.create_peer(self.network, wallet_index=True)

        self.factory = HathorAdminWebsocketFactory(self.manager.metrics)
        self.factory.subscribe(self.manager.pubsub)
        self.factory._setup_rate_limit()
        self.factory.openHandshakeTimeout = 0
        self.protocol = self.factory.buildProtocol(None)

        self.transport = proto_helpers.StringTransport()
        self.protocol.makeConnection(self.transport)

        self.web = StubSite(WebsocketStatsResource(self.factory))

    def _decode_value(self, value):
        ret = None
        while value:
            try:
                ret = json.loads(value.decode('utf-8'))
                break
            except (UnicodeDecodeError, json.decoder.JSONDecodeError):
                value = value[1:]

        return ret

    def test_new_tx(self):
        self.factory.connections.add(self.protocol)
        self.protocol.state = HathorAdminWebsocketProtocol.STATE_OPEN
        self.manager.pubsub.publish(HathorEvents.NETWORK_NEW_TX_ACCEPTED,
                                    tx=get_genesis_transactions(
                                        self.manager.tx_storage)[1])
        self.run_to_completion()
        value = self._decode_value(self.transport.value())
        self.assertEqual(value['tx_id'],
                         get_genesis_transactions(None)[1].hash.hex())
        self.assertEqual(value['type'], 'network:new_tx_accepted')

    def test_metric(self):
        self.factory.connections.add(self.protocol)
        self.protocol.state = HathorAdminWebsocketProtocol.STATE_OPEN
        self.factory._schedule_and_send_metric()
        value = self._decode_value(self.transport.value())
        keys = [
            'transactions', 'blocks', 'best_block_height', 'hash_rate',
            'block_hash_rate', 'tx_hash_rate', 'network_hash_rate', 'peers',
            'type', 'time'
        ]
        self.assertEqual(len(value), len(keys))
        for key in keys:
            self.assertTrue(key in value)

    def test_balance(self):
        self.factory.connections.add(self.protocol)
        self.protocol.state = HathorAdminWebsocketProtocol.STATE_OPEN
        self.manager.pubsub.publish(
            HathorEvents.WALLET_BALANCE_UPDATED,
            balance={settings.HATHOR_TOKEN_UID: WalletBalance(10, 20)})
        self.run_to_completion()
        value = self._decode_value(self.transport.value())
        self.assertEqual(value['balance']['locked'], 10)
        self.assertEqual(value['balance']['available'], 20)
        self.assertEqual(value['type'], 'wallet:balance_updated')

    def test_gap_limit(self):
        self.factory.connections.add(self.protocol)
        self.protocol.state = HathorAdminWebsocketProtocol.STATE_OPEN
        self.manager.pubsub.publish(HathorEvents.WALLET_GAP_LIMIT, limit=10)
        self.run_to_completion()
        value = self._decode_value(self.transport.value())
        self.assertEqual(value['limit'], 10)
        self.assertEqual(value['type'], 'wallet:gap_limit')

    def test_output_received(self):
        self.factory.connections.add(self.protocol)
        self.protocol.state = HathorAdminWebsocketProtocol.STATE_OPEN
        gen_tx = get_genesis_transactions(None)[0]
        output = UnspentTx(gen_tx.hash, 0, 10, gen_tx.timestamp, '',
                           gen_tx.outputs[0].token_data)
        self.manager.pubsub.publish(HathorEvents.WALLET_OUTPUT_RECEIVED,
                                    total=10,
                                    output=output)
        self.run_to_completion()
        value = self._decode_value(self.transport.value())
        self.assertEqual(value['total'], 10)
        self.assertEqual(value['type'], 'wallet:output_received')
        self.assertEqual(value['output']['tx_id'], gen_tx.hash.hex())

    def test_input_spent_received(self):
        self.factory.connections.add(self.protocol)
        self.protocol.state = HathorAdminWebsocketProtocol.STATE_OPEN
        gen_tx = get_genesis_transactions(None)[0]
        gen_tx2 = get_genesis_transactions(None)[1]
        spent = SpentTx(gen_tx2.hash, gen_tx.hash, 0, 10, gen_tx.timestamp + 1)
        self.manager.pubsub.publish(HathorEvents.WALLET_INPUT_SPENT,
                                    output_spent=spent)
        self.run_to_completion()
        value = self._decode_value(self.transport.value())
        self.assertEqual(value['type'], 'wallet:output_spent')
        self.assertEqual(value['output_spent']['tx_id'], gen_tx2.hash.hex())
        self.assertEqual(value['output_spent']['from_tx_id'],
                         gen_tx.hash.hex())

    def test_invalid_publish(self):
        self.factory.connections.add(self.protocol)
        self.protocol.state = HathorAdminWebsocketProtocol.STATE_OPEN
        self.manager.pubsub.publish(HathorEvents.NETWORK_PEER_CONNECTED)
        self.run_to_completion()
        value = self._decode_value(self.transport.value())
        self.assertIsNone(value)

        with self.assertRaises(ValueError):
            kwargs = {}
            args = EventArguments(**kwargs)
            self.factory.serialize_message_data(
                HathorEvents.NETWORK_PEER_CONNECTED, args)

    def test_ping(self):
        self.protocol.state = HathorAdminWebsocketProtocol.STATE_OPEN
        payload = json.dumps({'type': 'ping'}).encode('utf-8')
        self.protocol.onMessage(payload, True)
        value = self._decode_value(self.transport.value())
        self.assertEqual(value['type'], 'pong')

    def test_ping_str(self):
        self.protocol.state = HathorAdminWebsocketProtocol.STATE_OPEN
        payload = json.dumps({'type': 'ping'})
        self.protocol.onMessage(payload, False)
        value = self._decode_value(self.transport.value())
        self.assertEqual(value['type'], 'pong')

    def test_subscribe_address(self):
        self.assertEqual(len(self.factory.address_connections), 0)
        self.protocol.state = HathorAdminWebsocketProtocol.STATE_OPEN
        # Subscribe to address
        address = '1Q4qyTjhpUXUZXzwKs6Yvh2RNnF5J1XN9a'
        payload = json.dumps({
            'type': 'subscribe_address',
            'address': address
        }).encode('utf-8')
        self.protocol.onMessage(payload, True)
        self.assertEqual(len(self.factory.address_connections), 1)

        block_genesis = [
            tx for tx in get_genesis_transactions(self.manager.tx_storage)
            if tx.is_block
        ][0]

        # Test publish address history
        # First clean the transport to make sure the value comes from this execution
        self.transport.clear()
        element = block_genesis.to_json_extended()
        self.manager.pubsub.publish(HathorEvents.WALLET_ADDRESS_HISTORY,
                                    address=address,
                                    history=element)
        self.run_to_completion()
        value = self._decode_value(self.transport.value())
        self.assertEqual(value['type'], 'wallet:address_history')
        self.assertEqual(value['address'], address)
        self.assertEqual(value['history']['tx_id'], block_genesis.hash_hex)
        self.assertEqual(value['history']['timestamp'],
                         block_genesis.timestamp)

        # Publishing with address that was not subscribed must not generate any value in the ws
        # First clean the transport to make sure the value comes from this execution
        self.transport.clear()
        wrong_address = '1Q4qyTjhpUXUZXzwKs6Yvh2RNnF5J1XN9b'
        self.manager.pubsub.publish(HathorEvents.WALLET_ADDRESS_HISTORY,
                                    address=wrong_address,
                                    history=element)
        self.run_to_completion()
        value = self._decode_value(self.transport.value())
        self.assertIsNone(value)

    def test_connections(self):
        self.protocol.state = HathorAdminWebsocketProtocol.STATE_OPEN
        request_mock = Mock(peer=None)
        self.protocol.onConnect(request_mock)
        self.assertEqual(len(self.factory.connections), 0)
        self.protocol.onOpen()
        self.assertEqual(len(self.factory.connections), 1)
        self.protocol.onClose(True, 1, 'Closed')
        self.assertEqual(len(self.factory.connections), 0)

    def test_invalid_metric_key(self):
        kwargs = {'test': False}
        arg = EventArguments(**kwargs)
        with self.assertRaises(ValueError):
            self.manager.metrics.handle_publish('invalid_key', arg)

        metrics = Metrics(
            pubsub=self.manager.pubsub,
            avg_time_between_blocks=self.manager.avg_time_between_blocks,
            tx_storage=self.manager.tx_storage,
        )

        self.assertNotEqual(metrics.reactor, self.manager.reactor)

        hash_rate = metrics.get_current_hash_rate(
            metrics.weight_block_deque, metrics.total_block_weight,
            metrics.set_current_block_hash_rate,
            metrics.block_hash_store_interval)

        self.assertEqual(hash_rate, 0)

    @inlineCallbacks
    def test_get_stats(self):
        response = yield self.web.get('websocket_stats')
        data = response.json_value()
        self.assertEqual(data['connections'], 0)
        self.assertEqual(data['subscribed_addresses'], 0)

        # Add one connection
        self.protocol.state = HathorAdminWebsocketProtocol.STATE_OPEN
        request_mock = Mock(peer=None)
        self.protocol.onConnect(request_mock)
        self.protocol.onOpen()

        # Add two addresses
        self.assertEqual(len(self.factory.address_connections), 0)
        self.protocol.state = HathorAdminWebsocketProtocol.STATE_OPEN
        # Subscribe to address
        address1 = '1Q4qyTjhpUXUZXzwKs6Yvh2RNnF5J1XN9a'
        payload = json.dumps({
            'type': 'subscribe_address',
            'address': address1
        }).encode('utf-8')
        self.protocol.onMessage(payload, True)

        address2 = '1Q4qyTjhpUXUZXzwKs6Yvh2RNnF5J1XN9b'
        payload = json.dumps({
            'type': 'subscribe_address',
            'address': address2
        }).encode('utf-8')
        self.protocol.onMessage(payload, True)

        # Test get again
        response = yield self.web.get('websocket_stats')
        data = response.json_value()
        self.assertEqual(data['connections'], 1)
        self.assertEqual(data['subscribed_addresses'], 2)
Example #20
0
    def test_token_history(self):
        self.manager.wallet.unlock(b'MYPASS')
        resource = StubSite(TokenHistoryResource(self.manager))

        add_new_blocks(self.manager, 1, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        tx = create_tokens(self.manager,
                           mint_amount=100,
                           token_name='Teste',
                           token_symbol='TST')
        token_uid = tx.tokens[0]

        response = yield resource.get('thin_wallet/token_history', {
            b'id': token_uid.hex().encode(),
            b'count': b'3'
        })
        data = response.json_value()
        # Success returning the token creation tx
        self.assertTrue(data['success'])
        self.assertFalse(data['has_more'])
        self.assertEqual(1, len(data['transactions']))
        self.assertEqual(tx.hash.hex(), data['transactions'][0]['tx_id'])

        response = yield resource.get('thin_wallet/token_history', {
            b'id': b'123',
            b'count': b'3'
        })
        data = response.json_value()
        # Fail because token is unknown
        self.assertFalse(data['success'])

        # Create a tx with this token, so we can have more tx in the history
        output = tx.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))
        o = TxOutput(100, create_output_script(output_address, None), 1)
        i = TxInput(tx.hash, 0, b'')

        tx2 = Transaction(inputs=[i], outputs=[o], tokens=[token_uid])
        data_to_sign = tx2.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)
        tx2.inputs = [i]
        tx2.timestamp = int(self.clock.seconds())
        tx2.weight = self.manager.minimum_tx_weight(tx2)
        tx2.parents = self.manager.get_new_tx_parents()
        tx2.resolve()
        self.manager.propagate_tx(tx2)

        # Now we have 2 txs with this token
        response = yield resource.get('thin_wallet/token_history', {
            b'id': token_uid.hex().encode(),
            b'count': b'3'
        })
        data = response.json_value()
        # Success returning the token creation tx and newly created tx
        self.assertTrue(data['success'])
        self.assertFalse(data['has_more'])
        self.assertEqual(2, len(data['transactions']))
        self.assertEqual(tx2.hash.hex(), data['transactions'][0]['tx_id'])
        self.assertEqual(tx.hash.hex(), data['transactions'][1]['tx_id'])

        response = yield resource.get('thin_wallet/token_history', {
            b'id': token_uid.hex().encode(),
            b'count': b'1'
        })
        data = response.json_value()
        # Testing has_more
        self.assertTrue(data['success'])
        self.assertTrue(data['has_more'])
        self.assertEqual(1, len(data['transactions']))

        response = yield resource.get(
            'thin_wallet/token_history', {
                b'id': token_uid.hex().encode(),
                b'count': b'10',
                b'page': b'next',
                b'hash': tx2.hash.hex().encode(),
                b'timestamp': str(tx2.timestamp).encode(),
            })
        data = response.json_value()
        # Testing next
        self.assertTrue(data['success'])
        self.assertFalse(data['has_more'])
        self.assertEqual(1, len(data['transactions']))
        self.assertEqual(tx.hash.hex(), data['transactions'][0]['tx_id'])

        response = yield resource.get(
            'thin_wallet/token_history', {
                b'id': token_uid.hex().encode(),
                b'count': b'10',
                b'page': b'previous',
                b'hash': tx.hash.hex().encode(),
                b'timestamp': str(tx.timestamp).encode(),
            })
        data = response.json_value()
        # Testing previous
        self.assertTrue(data['success'])
        self.assertFalse(data['has_more'])
        self.assertEqual(1, len(data['transactions']))
        self.assertEqual(tx2.hash.hex(), data['transactions'][0]['tx_id'])

        response = yield resource.get(
            'thin_wallet/token_history', {
                b'id': token_uid.hex().encode(),
                b'count': b'10',
                b'page': b'previous',
                b'hash': tx2.hash.hex().encode(),
                b'timestamp': str(tx2.timestamp).encode(),
            })
        data = response.json_value()
        # Testing previous from first
        self.assertTrue(data['success'])
        self.assertFalse(data['has_more'])
        self.assertEqual(0, len(data['transactions']))
Example #21
0
 def setUp(self):
     super().setUp()
     self.web = StubSite(TipsHistogramResource(self.manager))
     self.manager.wallet.unlock(b'MYPASS')
     self.manager.reactor.advance(time.time())
Example #22
0
    def test_token_history_invalid_params(self):
        resource = StubSite(TokenHistoryResource(self.manager))

        # invalid count
        response = yield resource.get(
            'thin_wallet/token_history', {
                b'id':
                b'000003a3b261e142d3dfd84970d3a50a93b5bc3a66a3b6ba973956148a3eb824',
                b'count': b'a'
            })
        data = response.json_value()
        self.assertFalse(data['success'])

        # missing token uid
        response = yield resource.get('thin_wallet/token_history',
                                      {b'count': b'3'})
        data = response.json_value()
        self.assertFalse(data['success'])

        # invalid token uid
        response = yield resource.get('thin_wallet/token_history', {
            b'id': b'000',
            b'count': b'3'
        })
        data = response.json_value()
        self.assertFalse(data['success'])

        # missing timestamp
        response = yield resource.get(
            'thin_wallet/token_history', {
                b'id':
                b'000003a3b261e142d3dfd84970d3a50a93b5bc3a66a3b6ba973956148a3eb824',
                b'count':
                b'3',
                b'hash':
                b'0000b1448893eb7efdd3c71b97b74d934a4ecaaf8a6b52f6cb5b60fdaf21497b',
            })
        data = response.json_value()
        self.assertFalse(data['success'])

        # invalid timestamp
        response = yield resource.get(
            'thin_wallet/token_history', {
                b'id':
                b'000003a3b261e142d3dfd84970d3a50a93b5bc3a66a3b6ba973956148a3eb824',
                b'count': b'3',
                b'hash':
                b'0000b1448893eb7efdd3c71b97b74d934a4ecaaf8a6b52f6cb5b60fdaf21497b',
                b'timestamp': b'a'
            })
        data = response.json_value()
        self.assertFalse(data['success'])

        # invalid hash
        response = yield resource.get(
            'thin_wallet/token_history', {
                b'id':
                b'000003a3b261e142d3dfd84970d3a50a93b5bc3a66a3b6ba973956148a3eb824',
                b'count': b'3',
                b'timestamp': b'1578118186',
                b'page': b'next',
                b'hash': b'000',
            })
        data = response.json_value()
        self.assertFalse(data['success'])

        # invalid page
        response = yield resource.get(
            'thin_wallet/token_history', {
                b'id':
                b'000003a3b261e142d3dfd84970d3a50a93b5bc3a66a3b6ba973956148a3eb824',
                b'count':
                b'3',
                b'timestamp':
                b'1578118186',
                b'page':
                b'nextYY',
                b'hash':
                b'0000b1448893eb7efdd3c71b97b74d934a4ecaaf8a6b52f6cb5b60fdaf21497b',
            })
        data = response.json_value()
        self.assertFalse(data['success'])
Example #23
0
 def setUp(self):
     super().setUp()
     self.web = StubSite(DecodeTxResource(self.manager))
Example #24
0
 def setUp(self):
     super().setUp()
     self.web = StubSite(ValidateAddressResource(self.manager))
Example #25
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')

    @inlineCallbacks
    def test_post_invalid_data(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')

        # missing post data
        response_post = yield self.web.post('mining')
        self.assertEqual(response_post.written[0], b'0')

        # invalid block bytes
        response_post = yield self.web.post('mining', {'block_bytes': base64.b64encode(b'aaa').decode('ascii')})
        self.assertEqual(response_post.written[0], b'0')

        # invalid base64
        response_post = yield self.web.post('mining', {'block_bytes': 'YWFha'})
        self.assertEqual(response_post.written[0], b'0')
Example #26
0
 def setUp(self):
     super().setUp()
     self.web = StubSite(TransactionAccWeightResource(self.manager))
     self.manager.wallet.unlock(b'MYPASS')
 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))
Example #28
0
class BasePushTxTest(_BaseResourceTest._ResourceTest):
    __test__ = False

    is_post: Optional[bool] = None

    # XXX: we will get a "two instances of the same tx in memory" otherwise
    use_memory_storage = True

    def setUp(self):
        super().setUp()
        self.web = StubSite(PushTxResource(self.manager))
        self.web_tokens = StubSite(SendTokensResource(self.manager))

    def get_tx(
            self,
            inputs: Optional[List[WalletInputInfo]] = None,
            outputs: Optional[List[WalletOutputInfo]] = None) -> Transaction:
        if not outputs:
            address = self.get_address(0)
            assert address is not None
            outputs = [
                WalletOutputInfo(address=decode_address(address),
                                 value=1,
                                 timelock=None),
                WalletOutputInfo(address=decode_address(address),
                                 value=1,
                                 timelock=None)
            ]
        if inputs:
            tx = self.manager.wallet.prepare_transaction(
                Transaction, inputs, outputs)
        else:
            tx = self.manager.wallet.prepare_transaction_compute_inputs(
                Transaction, outputs, self.manager.tx_storage)

        tx.storage = self.manager.tx_storage
        tx.weight = 1
        max_ts_spent_tx = max(
            tx.get_spent_tx(txin).timestamp for txin in tx.inputs)
        tx.timestamp = max(max_ts_spent_tx + 1,
                           int(self.manager.reactor.seconds()))
        tx.parents = self.manager.get_new_tx_parents(tx.timestamp)
        tx.resolve()
        return tx

    def push_tx(self, data=None):
        if self.is_post is None:
            raise Exception(
                'You must set self.is_push before calling this method.')

        if self.is_post:
            body = data
            return self.web.post('push_tx', body)

        if data is None:
            args = None
        else:
            args = {}
            for k, v in data.items():
                nk = k.encode()
                if isinstance(v, str):
                    nv = v.encode()
                elif isinstance(v, bool):
                    nv = b'true' if v else b'false'
                else:
                    raise NotImplementedError
                args[nk] = nv
        return self.web.get('push_tx', args)

    @inlineCallbacks
    def test_push_tx(self) -> Generator:
        self.manager.wallet.unlock(b'MYPASS')
        blocks = add_new_blocks(self.manager, 5, advance_clock=15)
        add_blocks_unlock_reward(self.manager)
        tx = self.get_tx()

        tx_hex = tx.get_struct().hex()
        response = yield self.push_tx({'hex_tx': tx_hex})
        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()
        response_success = yield self.push_tx({'hex_tx': tx_hex})
        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()
        response = yield self.push_tx({'hex_tx': tx_hex})
        data = response.json_value()
        self.assertFalse(data['success'])

        # force
        tx_hex = tx.get_struct().hex()
        response = yield self.push_tx({'hex_tx': tx_hex, 'force': 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()
        response_genesis = yield self.push_tx({'tx_hex': genesis_hex})
        data_genesis = response_genesis.json_value()
        self.assertFalse(data_genesis['success'])

        # Token creation tx
        script_type_out = parse_address_script(blocks[0].outputs[0].script)
        assert script_type_out is not None
        address = script_type_out.address
        tx2 = create_tokens(self.manager,
                            address,
                            mint_amount=100,
                            propagate=False)
        tx2_hex = tx2.get_struct().hex()
        response = yield self.push_tx({'hex_tx': tx2_hex})
        data = response.json_value()
        self.assertTrue(data['success'])

    @inlineCallbacks
    def test_push_nft(self) -> Generator:
        self.manager.wallet.unlock(b'MYPASS')
        blocks = add_new_blocks(self.manager, 5, advance_clock=15)
        add_blocks_unlock_reward(self.manager)
        # NFT creation tx
        script_type_out = parse_address_script(blocks[0].outputs[0].script)
        assert script_type_out is not None
        address = script_type_out.address
        tx3 = create_tokens(self.manager,
                            address,
                            mint_amount=100,
                            propagate=False,
                            nft_data='test')
        tx3_hex = tx3.get_struct().hex()
        response = yield self.push_tx({'hex_tx': tx3_hex})
        data = response.json_value()
        self.assertTrue(data['success'])

    @inlineCallbacks
    def test_invalid_params(self) -> Generator:
        # Missing hex
        response = yield self.push_tx()
        data = response.json_value()
        self.assertFalse(data['success'])

        # Missing hex 2
        response = yield self.push_tx({})
        data = response.json_value()
        self.assertFalse(data['success'])

        # Invalid hex
        response = yield self.push_tx({'hex_tx': 'XXXX'})
        data = response.json_value()
        self.assertFalse(data['success'])

        # Invalid tx hex
        response_error2 = yield self.push_tx({'hex_tx': 'a12c'})
        data_error2 = response_error2.json_value()
        self.assertFalse(data_error2['success'])

    @inlineCallbacks
    def test_script_too_big(self) -> Generator:
        self.manager.wallet.unlock(b'MYPASS')
        add_new_blocks(self.manager, 5, advance_clock=15)
        add_blocks_unlock_reward(self.manager)
        tx = self.get_tx()

        # Invalid tx (output script is too long)
        tx.outputs[0].script = b'*' * (settings.PUSHTX_MAX_OUTPUT_SCRIPT_SIZE +
                                       1)
        tx.resolve()
        tx_hex = tx.get_struct().hex()
        response = yield self.push_tx({'hex_tx': tx_hex})
        data = response.json_value()
        self.assertFalse(data['success'])
        self.assertEqual('Transaction is non standard.', data['message'])

    @inlineCallbacks
    def test_non_standard_script(self) -> Generator:
        self.manager.wallet.unlock(b'MYPASS')
        add_new_blocks(self.manager, 5, advance_clock=15)
        add_blocks_unlock_reward(self.manager)
        tx = self.get_tx()

        # Invalid tx (output script is too long)
        tx.outputs[0].script = b'*' * 5
        tx.resolve()
        tx_hex = tx.get_struct().hex()
        response = yield self.push_tx({'hex_tx': tx_hex})
        data = response.json_value()
        self.assertFalse(data['success'])
        expected = 'Transaction is non standard.'
        self.assertEqual(expected, data['message'])

    @inlineCallbacks
    def test_spending_voided(self) -> Generator:
        self.manager.wallet.unlock(b'MYPASS')
        add_new_blocks(self.manager, 5, advance_clock=15)
        add_blocks_unlock_reward(self.manager)

        # Push a first tx
        tx = self.get_tx()
        tx_hex = tx.get_struct().hex()
        response = yield self.push_tx({'hex_tx': tx_hex})
        data = response.json_value()
        self.assertTrue(data['success'])

        wallet = self.manager.wallet

        # Pushing a tx that spends this first tx works
        txout = tx.outputs[0]
        p2pkh = parse_address_script(txout.script)
        assert p2pkh is not None
        private_key = wallet.get_private_key(p2pkh.address)
        assert tx.hash is not None
        inputs = [
            WalletInputInfo(tx_id=tx.hash, index=0, private_key=private_key)
        ]
        outputs = [
            WalletOutputInfo(address=decode_address(p2pkh.address),
                             value=txout.value,
                             timelock=None),
        ]
        tx2 = self.get_tx(inputs, outputs)
        tx2_hex = tx2.get_struct().hex()
        response = yield self.push_tx({'hex_tx': tx2_hex})
        data = response.json_value()
        self.assertTrue(data['success'])

        # Now we set this tx2 as voided and try to push a tx3 that spends tx2
        tx_meta = tx2.get_metadata()
        assert tx2.hash is not None
        tx_meta.voided_by = {tx2.hash}
        self.manager.tx_storage.save_transaction(tx2, only_metadata=True)

        inputs = [
            WalletInputInfo(tx_id=tx2.hash, index=0, private_key=private_key)
        ]
        outputs = [
            WalletOutputInfo(address=decode_address(p2pkh.address),
                             value=txout.value,
                             timelock=None),
        ]
        tx3 = self.get_tx(inputs, outputs)
        tx3_hex = tx3.get_struct().hex()
        response = yield self.push_tx({'hex_tx': tx3_hex})
        data = response.json_value()
        self.assertFalse(data['success'])

        # Now we set this tx2 as voided and try to push a tx3 that spends tx2
        tx_meta = tx2.get_metadata()
        tx_meta.voided_by = {settings.SOFT_VOIDED_ID}
        self.manager.tx_storage.save_transaction(tx2, only_metadata=True)

        # Try to push again with soft voided id as voided by
        response = yield self.push_tx({'hex_tx': tx3_hex})
        data = response.json_value()
        self.assertFalse(data['success'])

        # Now without voided_by the push tx must succeed
        tx_meta = tx2.get_metadata()
        tx_meta.voided_by = None
        self.manager.tx_storage.save_transaction(tx2, only_metadata=True)

        response = yield self.push_tx({'hex_tx': tx3_hex})
        data = response.json_value()
        self.assertTrue(data['success'])
Example #29
0
    def test_token(self):
        self.manager.wallet.unlock(b'MYPASS')
        resource = StubSite(TokenResource(self.manager))

        # test list of tokens empty
        response_list1 = yield resource.get('thin_wallet/token')
        data_list1 = response_list1.json_value()
        self.assertTrue(data_list1['success'])
        self.assertEqual(len(data_list1['tokens']), 0)

        # test invalid token id
        response = yield resource.get('thin_wallet/token',
                                      {b'id': 'vvvv'.encode()})
        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,
                           use_genesis=False)
        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 list of tokens with one token
        response_list2 = yield resource.get('thin_wallet/token')
        data_list2 = response_list2.json_value()
        self.assertTrue(data_list2['success'])
        self.assertEqual(len(data_list2['tokens']), 1)
        self.assertEqual(data_list2['tokens'][0]['name'], token_name)
        self.assertEqual(data_list2['tokens'][0]['symbol'], token_symbol)
        self.assertEqual(data_list2['tokens'][0]['uid'], tx.hash.hex())

        token_name2 = 'New Token'
        token_symbol2 = 'NTK'
        tx2 = create_tokens(self.manager,
                            mint_amount=amount,
                            token_name=token_name2,
                            token_symbol=token_symbol2,
                            use_genesis=False)

        token_name3 = 'Wat Coin'
        token_symbol3 = 'WTC'
        tx3 = create_tokens(self.manager,
                            mint_amount=amount,
                            token_name=token_name3,
                            token_symbol=token_symbol3,
                            use_genesis=False)

        # test list of tokens with 3 tokens
        response_list3 = yield resource.get('thin_wallet/token')
        data_list3 = response_list3.json_value()
        self.assertTrue(data_list3['success'])
        self.assertEqual(len(data_list3['tokens']), 3)
        token1 = {
            'uid': tx.hash.hex(),
            'name': token_name,
            'symbol': token_symbol
        }
        token2 = {
            'uid': tx2.hash.hex(),
            'name': token_name2,
            'symbol': token_symbol2
        }
        token3 = {
            'uid': tx3.hash.hex(),
            'name': token_name3,
            'symbol': token_symbol3
        }
        self.assertIn(token1, data_list3['tokens'])
        self.assertIn(token2, data_list3['tokens'])
        self.assertIn(token3, data_list3['tokens'])

        # 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'])
Example #30
0
 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)