예제 #1
0
class BaseAddPeerTest(_BaseResourceTest._ResourceTest):
    __test__ = False

    def setUp(self):
        super().setUp()
        self.web = StubSite(AddPeersResource(self.manager))

    @inlineCallbacks
    def test_connecting_peers(self):
        response = yield self.web.post('p2p/peers', ['tcp://localhost:8006'])
        data = response.json_value()
        self.assertTrue(data['success'])

        # test when we send a peer we're already connected to
        peer = PeerId()
        peer.entrypoints = ['tcp://localhost:8006']
        self.manager.connections.peer_storage.add(peer)
        response = yield self.web.post(
            'p2p/peers', ['tcp://localhost:8006', 'tcp://localhost:8007'])
        data = response.json_value()
        self.assertTrue(data['success'])
        self.assertEqual(data['peers'], ['tcp://localhost:8007'])

    @inlineCallbacks
    def test_invalid_data(self):
        # no data
        response = yield self.web.post('p2p/peers')
        data = response.json_value()
        self.assertFalse(data['success'])

        # invalid type
        response = yield self.web.post('p2p/peers',
                                       {'a': 'tcp://localhost:8006'})
        data = response.json_value()
        self.assertFalse(data['success'])
예제 #2
0
class BaseProfilerTest(_BaseResourceTest._ResourceTest):
    __test__ = False

    def setUp(self):
        super().setUp()
        self.web = StubSite(ProfilerResource(self.manager))

    @pytest.mark.skipif(sys.platform == 'win32',
                        reason='shutil.rmtree fails on Windows')
    @inlineCallbacks
    def test_post(self):
        # Options
        yield self.web.options("profiler")

        tmpdir = tempfile.mkdtemp()
        tmpfile = tempfile.NamedTemporaryFile(dir=tmpdir,
                                              suffix='.prof',
                                              delete=False)
        filename = os.path.basename(tmpfile.name)
        full_path = os.path.join(tmpdir, filename)

        # Start profiler
        response_start = yield self.web.post("profiler", {'start': True})
        data_start = response_start.json_value()
        self.assertTrue(data_start['success'])

        with open(full_path, 'r') as f:
            # In the start the file must be empty
            self.assertEqual(len(f.read()), 0)

        # Stop profiler
        response_stop = yield self.web.post("profiler", {
            'stop': True,
            'filepath': full_path
        })
        data_stop = response_stop.json_value()
        self.assertTrue(data_stop['success'])

        with open(full_path, 'rb') as f:
            # After stop profiler file must have something
            self.assertGreater(len(f.read()), 0)

        # Success false
        response_error = yield self.web.post("profiler")
        data_error = response_error.json_value()
        self.assertFalse(data_error['success'])

        # Removing tmpdir
        shutil.rmtree(tmpdir)

    def test_dump_file(self):
        resource = ProfilerResource(self.manager)
        filename = resource.gen_dump_filename()

        filename_arr = filename.split('/')
        self.assertEqual(filename_arr[0], 'profiles')
        self.assertTrue(re.search(r'^profile\d{3}\.prof$', filename_arr[1]))
예제 #3
0
class BaseBalanceTest(_BaseResourceTest._ResourceTest):
    __test__ = False

    def setUp(self):
        super().setUp()
        self.web = StubSite(BalanceResource(self.manager))
        self.web_mining = StubSite(MiningResource(self.manager))

    @inlineCallbacks
    def test_get(self):
        response = yield self.web.get("wallet/balance")
        data = response.json_value()
        self.assertTrue(data['success'])
        self.assertEqual(data['balance'], {'available': 0, 'locked': 0})

        # Mining new block
        response_mining = yield self.web_mining.get("mining")
        data_mining = response_mining.json_value()
        block_bytes = resolve_block_bytes(
            block_bytes=data_mining['block_bytes'])
        yield self.web_mining.post(
            "mining",
            {'block_bytes': base64.b64encode(block_bytes).decode('utf-8')})

        # Get new balance after block
        response2 = yield self.web.get("wallet/balance")
        data2 = response2.json_value()
        self.assertTrue(data2['success'])
        tokens = self.manager.get_tokens_issued_per_block(1)
        self.assertEqual(data2['balance'], {'available': tokens, 'locked': 0})
예제 #4
0
class HistoryTest(_BaseResourceTest._ResourceTest):
    def setUp(self):
        super().setUp()
        self.web = StubSite(HistoryResource(self.manager))
        self.web_mining = StubSite(MiningResource(self.manager))

    @inlineCallbacks
    def test_get(self):
        # Mining new block
        response_mining = yield self.web_mining.get("mining")
        data_mining = response_mining.json_value()
        block_bytes = resolve_block_bytes(
            block_bytes=data_mining['block_bytes'])
        yield self.web_mining.post(
            "mining",
            {'block_bytes': base64.b64encode(block_bytes).decode('utf-8')})

        # Getting wallet history
        response = yield self.web.get("wallet/history", {
            b'page': 1,
            b'count': 10
        })
        data = response.json_value()
        self.assertEqual(len(data['history']), 1)
        self.assertEqual(data['total_pages'], 1)
예제 #5
0
class BaseLockTest(_BaseResourceTest._ResourceTest):
    __test__ = False

    def setUp(self):
        super().setUp()
        self.web = StubSite(LockWalletResource(self.manager))
        self.web_unlock = StubSite(UnlockWalletResource(self.manager))
        self.web_state = StubSite(StateWalletResource(self.manager))

    @inlineCallbacks
    def test_locking(self):
        # Wallet is locked
        response = yield self.web_state.get('wallet/state')
        data = response.json_value()
        self.assertTrue(data['is_locked'])

        # Unlock it
        response_success = yield self.web_unlock.post('wallet/unlock',
                                                      {'password': '******'})
        data_success = response_success.json_value()
        self.assertTrue(data_success['success'])

        # Wallet is unlocked
        response_unlocked = yield self.web_state.get('wallet/state')
        data_unlocked = response_unlocked.json_value()
        self.assertFalse(data_unlocked['is_locked'])

        # Test locking the wallet with resource

        # Options
        yield self.web.options("wallet/lock")

        response_test = yield self.web.post('wallet/lock')
        data_test = response_test.json_value()
        self.assertTrue(data_test['success'])

        # Validate wallet is locked
        response_locked = yield self.web_state.get('wallet/state')
        data_locked = response_locked.json_value()
        self.assertTrue(data_locked['is_locked'])
예제 #6
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')
예제 #7
0
class BaseUnlockTest(_BaseResourceTest._ResourceTest):
    __test__ = False

    def setUp(self):
        super().setUp()
        self.web = StubSite(UnlockWalletResource(self.manager))
        self.web_lock = StubSite(LockWalletResource(self.manager))
        self.web_state = StubSite(StateWalletResource(self.manager))

    @inlineCallbacks
    def test_unlocking(self):
        # Wallet is locked
        response = yield self.web_state.get("wallet/state")
        data = response.json_value()
        self.assertTrue(data['is_locked'])

        # Try to unlock with wrong password

        # Options
        yield self.web.options("wallet/unlock")

        response_error = yield self.web.post("wallet/unlock", {'password': '******'})
        data_error = response_error.json_value()
        self.assertFalse(data_error['success'])

        # Try to unlock with correct password
        response_success = yield self.web.post("wallet/unlock", {'password': '******'})
        data_success = response_success.json_value()
        self.assertTrue(data_success['success'])

        # Wallet is unlocked
        response_unlocked = yield self.web_state.get("wallet/state")
        data_unlocked = response_unlocked.json_value()
        self.assertFalse(data_unlocked['is_locked'])

    @inlineCallbacks
    def test_unlocking_hd_wallet(self):
        self.manager.wallet = HDWallet()
        self.manager.wallet._manually_initialize()
        self.manager.wallet.unlock(tx_storage=self.manager.tx_storage)

        # Wallet is not locked
        response = yield self.web_state.get("wallet/state")
        data = response.json_value()
        self.assertFalse(data['is_locked'])

        # Lock the wallet
        response_lock = yield self.web_lock.post("wallet/lock")
        data_lock = response_lock.json_value()
        self.assertTrue(data_lock['success'])

        # Wallet is locked
        response_locked = yield self.web_state.get("wallet/state")
        data_locked = response_locked.json_value()
        self.assertTrue(data_locked['is_locked'])

        # Unlock wallet invalid words
        response_invalid = yield self.web.post("wallet/unlock", {'words': 'abc def', 'passphrase': ''})
        data_invalid = response_invalid.json_value()
        self.assertFalse(data_invalid['success'])

        # Unlock wallet
        response_success = yield self.web.post("wallet/unlock", {'passphrase': ''})
        data_success = response_success.json_value()
        self.assertTrue(data_success['success'])

        # Wallet is unlocked
        response_unlocked = yield self.web_state.get("wallet/state")
        data_unlocked = response_unlocked.json_value()
        self.assertFalse(data_unlocked['is_locked'])

        # Lock the wallet and unlock with same words
        self.manager.wallet.lock()
        response_words = yield self.web.post("wallet/unlock", {'words': data_success['words'], 'passphrase': ''})
        data_words = response_words.json_value()
        self.assertTrue(data_words['success'])
예제 #8
0
    def test_match_values(self):
        decode_resource = StubSite(NanoContractDecodeResource(self.manager))
        execute_resource = StubSite(NanoContractExecuteResource(self.manager))
        match_value_resource = StubSite(
            NanoContractMatchValueResource(self.manager))
        pushtx_resource = StubSite(
            PushTxResource(self.manager, allow_non_standard_script=True))
        signtx_resource = StubSite(SignTxResource(self.manager))
        decodetx_resource = StubSite(DecodeTxResource(self.manager))
        add_new_blocks(self.manager, 3)
        add_blocks_unlock_reward(self.manager)
        self.reactor.advance(3)
        # Options
        yield match_value_resource.options(
            "wallet/nano_contracts/match_values")

        total_value = self.manager.get_tokens_issued_per_block(1)

        address1 = self.get_address(0)
        data_post = {
            'oracle_data_id': 'some_id',
            'total_value': total_value,
            'input_value': total_value,
            'min_timestamp': 1,
            'fallback_address': self.get_address(1),
            'values': [{
                'address': address1,
                'value': 300
            }]
        }
        # Error missing parameter
        response_error = yield match_value_resource.post(
            "wallet/nano_contracts/match_value", data_post)
        data_error = response_error.json_value()
        self.assertFalse(data_error['success'])
        self.assertEqual(data_error['message'],
                         'Missing parameter: oracle_pubkey_hash')

        # create nano contract
        data_post['oracle_pubkey_hash'] = '6o6ul2c+sqAariBVW+CwNaSJb9w='
        response = yield match_value_resource.post(
            "wallet/nano_contracts/match_value", data_post)
        data = response.json_value()
        self.assertTrue(data['success'])
        self.assertIsNotNone(data['hex_tx'])
        nano_contract_hex = data['hex_tx']

        # Error missing parameter
        response_error = yield decode_resource.get(
            "wallet/nano_contracts/decode", {})
        data_error = response_error.json_value()
        self.assertFalse(data_error['success'])
        self.assertEqual(data_error['message'], 'Missing parameter: hex_tx')

        # Error invalid hex
        response_error2 = yield decode_resource.get(
            "wallet/nano_contracts/decode", {b'hex_tx': b'123'})
        data_error2 = response_error2.json_value()
        self.assertFalse(data_error2['success'])

        # Error valid hex but invalid tx struct
        response_error3 = yield decode_resource.get(
            "wallet/nano_contracts/decode", {b'hex_tx': b'1334'})
        data_error3 = response_error3.json_value()
        self.assertFalse(data_error3['success'])

        # decode
        genesis_output = [
            tx for tx in self.manager.tx_storage.get_all_genesis()
            if tx.is_block
        ][0].outputs[0]
        partial_tx = Transaction.create_from_struct(
            bytes.fromhex(nano_contract_hex))
        partial_tx.outputs.append(genesis_output)
        response_decode = yield decode_resource.get(
            "wallet/nano_contracts/decode",
            {b'hex_tx': bytes(partial_tx.get_struct().hex(), 'utf-8')})
        data = response_decode.json_value()
        self.assertTrue(data['success'])
        nano_contract = data['nano_contract']
        self.assertIsNotNone(nano_contract)
        self.assertEqual(nano_contract['type'], 'NanoContractMatchValues')
        self.assertEqual(len(data['other_inputs']), 0)
        self.assertEqual(len(data['my_inputs']), 1)
        self.assertEqual(len(data['outputs']), 1)
        self.assertEqual(data['outputs'][0],
                         genesis_output.to_human_readable())

        address2 = self.get_address(2)
        data_put = {
            'new_values': [{
                'address': address2,
                'value': 500
            }],
            'input_value': total_value
        }
        # Error missing parameter
        response_error = yield match_value_resource.put(
            "wallet/nano_contracts/match_value", data_put)
        data_error = response_error.json_value()
        self.assertFalse(data_error['success'])
        self.assertEqual(data_error['message'], 'Missing parameter: hex_tx')

        # update
        data_put['hex_tx'] = partial_tx.get_struct().hex()
        response = yield match_value_resource.put(
            "wallet/nano_contracts/match_value", data_put)
        data = response.json_value()
        self.assertTrue(data['success'])
        self.assertIsNotNone(data['hex_tx'])

        # Error nano contract not found
        new_tx = Transaction.create_from_struct(partial_tx.get_struct())
        new_tx.outputs = []
        data_put['hex_tx'] = new_tx.get_struct().hex()
        response = yield match_value_resource.put(
            "wallet/nano_contracts/match_value", data_put)
        data = response.json_value()
        self.assertFalse(data['success'])

        # Error missing parameter
        response_error = yield signtx_resource.get("wallet/sign_tx", {})
        data_error = response_error.json_value()
        self.assertFalse(data_error['success'])
        self.assertEqual(data_error['message'], 'Missing parameter: hex_tx')

        # Error wrong parameter value
        response_error2 = yield signtx_resource.get("wallet/sign_tx", {
            b'hex_tx': b'123',
            b'prepare_to_send': b'true'
        })
        data_error2 = response_error2.json_value()
        self.assertFalse(data_error2['success'])

        # Error valid hex but wrong tx struct value
        response_error3 = yield signtx_resource.get("wallet/sign_tx", {
            b'hex_tx': b'1334',
            b'prepare_to_send': b'true'
        })
        data_error3 = response_error3.json_value()
        self.assertFalse(data_error3['success'])

        # sign tx
        response = yield signtx_resource.get(
            "wallet/sign_tx", {
                b'hex_tx': bytes(nano_contract_hex, 'utf-8'),
                b'prepare_to_send': b'true'
            })
        data = response.json_value()
        self.assertTrue(data['success'])
        nano_contract_hex = data['hex_tx']

        # sign tx without preparing
        response2 = yield signtx_resource.get(
            "wallet/sign_tx", {b'hex_tx': bytes(nano_contract_hex, 'utf-8')})
        data2 = response2.json_value()
        self.assertTrue(data2['success'])
        self.assertIsNotNone(data2['hex_tx'])

        # propagate tx
        response = yield pushtx_resource.get(
            "push_tx", {b'hex_tx': bytes(nano_contract_hex, 'utf-8')})
        data = response.json_value()
        self.assertTrue(data['success'])

        self.reactor.advance(3)

        # get tx hash
        response = yield decodetx_resource.get(
            "decode_tx", {b'hex_tx': bytes(nano_contract_hex, 'utf-8')})
        data = response.json_value()
        self.assertTrue(data['success'])
        hash_hex = data['tx']['hash']

        # Options
        yield execute_resource.options("wallet/nano_contracts/execute")

        # Error no data
        response_error = yield execute_resource.post(
            "wallet/nano_contracts/execute")
        data_error = response_error.json_value()
        self.assertFalse(data_error['success'])

        # Error missing parameter
        data = {
            'spent_tx_index':
            0,
            'oracle_data':
            'B3NvbWVfaWQEW/xjGQIBLA==',
            'oracle_signature':
            'MEUCIGeqbmLRI6lrgXMy4sQEgK94F5m14oVL5Z7oLLVII7BUAiEApKTMuWlwvws574'
            '+jtqKW5/AuH+ICD0u+HyMyHe0aric=',
            'oracle_pubkey':
            'Awmloohhey8WhajdDURgvbk1z3JHX2vxDSBjz9uG9wEp',
            'address':
            address1,
            'value':
            total_value,
        }
        response_error2 = yield execute_resource.post(
            "wallet/nano_contracts/execute", data)
        data_error2 = response_error2.json_value()
        self.assertFalse(data_error2['success'])
        self.assertEqual(data_error2['message'],
                         'Missing parameter: spent_tx_id')

        # execute nano contract
        data['spent_tx_id'] = hash_hex
        response = yield execute_resource.post("wallet/nano_contracts/execute",
                                               data)
        data = response.json_value()
        self.assertTrue(data['success'])
예제 #9
0
class SendTokensTest(_BaseResourceTest._ResourceTest):
    def setUp(self):
        super().setUp()
        self.web = StubSite(SendTokensResource(self.manager))
        self.web_mining = StubSite(MiningResource(self.manager))
        self.web_balance = StubSite(BalanceResource(self.manager))
        self.web_history = StubSite(HistoryResource(self.manager))

    @inlineCallbacks
    def test_post(self):
        # Mining new block
        response_mining = yield self.web_mining.get("mining")
        data_mining = response_mining.json_value()
        block_bytes = resolve_block_bytes(
            block_bytes=data_mining['block_bytes'])
        yield self.web_mining.post(
            "mining",
            {'block_bytes': base64.b64encode(block_bytes).decode('utf-8')})
        add_blocks_unlock_reward(self.manager)
        self.reactor.advance(10)

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

        # Sending token to random address without input

        # Options
        yield self.web.options("wallet/send_tokens")

        data_json = {
            "outputs": [{
                "address": self.get_address(0),
                "value": 505
            }],
            "inputs": []
        }
        response = yield self.web.post("wallet/send_tokens",
                                       {'data': data_json})
        data = response.json_value()
        self.assertTrue(data['success'])
        self.reactor.advance(10)

        # Asserting new balance
        response_balance = yield self.web_balance.get("wallet/balance")
        data_balance = response_balance.json_value()
        tokens_per_block = self.manager.get_tokens_issued_per_block(1)
        self.assertEqual(data_balance['balance'], {
            'available': tokens_per_block - 505,
            'locked': 0
        })

        # Getting history, so we can get the input
        response_history = yield self.web_history.get("wallet/history", {
            b'page': 1,
            b'count': 10
        })
        data_history = response_history.json_value()
        input_hash = data_history['history'][0]['tx_id']

        # Sending token to random address with input wrong amount
        data_json = {
            "outputs": [{
                "address": self.get_address(0),
                "value": 500
            }],
            "inputs": [{
                "tx_id": input_hash,
                "index": 0
            }]
        }
        response2 = yield self.web.post("wallet/send_tokens",
                                        {'data': data_json})
        data2 = response2.json_value()
        self.assertFalse(data2['success'])
        self.reactor.advance(10)

        # Sending duplicate input
        data_json_duplicate = {
            "outputs": [{
                "address": self.get_address(0),
                "value": 19000
            }],
            "inputs": [{
                "tx_id": input_hash,
                "index": 0
            }, {
                "tx_id": input_hash,
                "index": 0
            }]
        }
        response_duplicate = yield self.web.post("wallet/send_tokens",
                                                 {'data': data_json_duplicate})
        data_duplicate = response_duplicate.json_value()
        self.assertFalse(data_duplicate['success'])

        # Sending token to random address with input right amount
        data_json2 = {
            "outputs": [{
                "address":
                self.get_address(0),
                "value":
                self.manager.get_tokens_issued_per_block(1) - 505
            }],
            "inputs": [{
                "tx_id": input_hash,
                "index": 0
            }]
        }
        response3 = yield self.web.post("wallet/send_tokens",
                                        {'data': data_json2})
        data3 = response3.json_value()
        self.assertTrue(data3['success'])

        # Sending token to invalid addresses
        data_json3 = {
            "outputs": [{
                "address": self.get_address(1),
                "value": 500
            }],
            "inputs": []
        }
        response_error1 = yield self.web.post("wallet/send_tokens",
                                              {'data': data_json3})
        data_error1 = response_error1.json_value()
        self.assertFalse(data_error1['success'])

        data_json4 = {
            "outputs": [{
                "address": "1234",
                "value": 500
            }],
            "inputs": []
        }
        response_error2 = yield self.web.post("wallet/send_tokens",
                                              {'data': data_json4})
        data_error2 = response_error2.json_value()
        self.assertFalse(data_error2['success'])

        # Error insuficient funds
        data_json5 = {
            "outputs": [{
                "address": self.get_address(0),
                "value": 5000000
            }],
            "inputs": []
        }
        response_error3 = yield self.web.post("wallet/send_tokens",
                                              {'data': data_json5})
        data_error3 = response_error3.json_value()
        self.assertFalse(data_error3['success'])

        add_new_blocks(self.manager, 1, advance_clock=1)
        add_blocks_unlock_reward(self.manager)

        # Sending token with timelock
        data_timelock = {
            "outputs": [{
                "address": self.get_address(0),
                "value": 505,
                "timelock": 1542995660
            }],
            "inputs": []
        }
        response_timelock = yield self.web.post("wallet/send_tokens",
                                                {'data': data_timelock})
        data_response_timelock = response_timelock.json_value()
        self.assertTrue(data_response_timelock['success'])

        self.reactor.advance(5)
        # Sending token with timestamp
        data_timestamp = {
            "outputs": [{
                "address": self.get_address(0),
                "value": 5
            }],
            "inputs": [],
            "timestamp": int(self.reactor.seconds())
        }
        response_timestamp = yield self.web.post("wallet/send_tokens",
                                                 {'data': data_timestamp})
        data_response_timestamp = response_timestamp.json_value()
        self.assertTrue(data_response_timestamp['success'])

        self.reactor.advance(5)
        # Sending token with timestamp=0
        data_timestamp = {
            "outputs": [{
                "address": self.get_address(0),
                "value": 5
            }],
            "inputs": [],
            "timestamp": 0
        }
        response_timestamp = yield self.web.post("wallet/send_tokens",
                                                 {'data': data_timestamp})
        data_response_timestamp = response_timestamp.json_value()
        self.assertTrue(data_response_timestamp['success'])

    @inlineCallbacks
    def test_tx_weight(self):
        add_new_blocks(self.manager, 3, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        self.reactor.advance(3)

        # Unlocking wallet
        self.manager.wallet.unlock(b"MYPASS")
        self.manager.test_mode = TestMode.DISABLED

        data_json = {
            "outputs": [{
                "address": self.get_address(0),
                "value": 505
            }],
            "inputs": [],
            "weight": 1
        }
        response = yield self.web.post("wallet/send_tokens",
                                       {'data': data_json})
        data = response.json_value()
        self.assertFalse(data['success'])

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

        self.assertIsNotNone(request._finishedDeferreds)
        resource._err_tx_resolve('Error', request)
        self.assertIsNone(request._finishedDeferreds)
예제 #10
0
class DecodeTxTest(_BaseResourceTest._ResourceTest):
    def setUp(self):
        super().setUp()
        self.web = StubSite(PushTxResource(self.manager))
        self.web_tokens = StubSite(SendTokensResource(self.manager))
        self.web_mining = StubSite(MiningResource(self.manager))
        self.web_balance = StubSite(BalanceResource(self.manager))
        self.web_history = StubSite(HistoryResource(self.manager))

    @inlineCallbacks
    def test_get(self):
        # Mining new block
        response_mining = yield self.web_mining.get('mining')
        data_mining = response_mining.json_value()
        block_bytes = resolve_block_bytes(
            block_bytes=data_mining['block_bytes'])
        yield self.web_mining.post(
            'mining',
            {'block_bytes': base64.b64encode(block_bytes).decode('utf-8')})

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

        # Creating a valid transaction to be pushed to the network
        blocks = add_new_blocks(self.manager, 3, advance_clock=2)
        add_blocks_unlock_reward(self.manager)
        tx_id = blocks[0].hash
        output = blocks[0].outputs[0]
        script_type_out = parse_address_script(output.script)
        address = script_type_out.address
        private_key = self.manager.wallet.get_private_key(address)

        output_address = decode_address(self.get_address(0))
        value = self.manager.get_tokens_issued_per_block(1)
        o = TxOutput(value, create_output_script(output_address, None))
        i = TxInput(tx_id, 0, b'')
        tx = Transaction(inputs=[i], outputs=[o])

        data_to_sign = tx.get_sighash_all()
        public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(
            data_to_sign, private_key)
        i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes)
        tx.inputs = [i]
        tx.timestamp = int(self.clock.seconds())
        tx.weight = self.manager.minimum_tx_weight(tx)
        tx.parents = self.manager.get_new_tx_parents(tx.timestamp)
        tx.resolve()

        response = yield self.web.get(
            'push_tx', {b'hex_tx': bytes(tx.get_struct().hex(), 'utf-8')})
        data = response.json_value()
        self.assertTrue(data['success'])

        # Sending token to random address without input
        data_json = {
            'outputs': [{
                'address': self.get_address(0),
                'value': 5
            }],
            'inputs': []
        }
        yield self.web_tokens.post('wallet/send_tokens', {'data': data_json})

        # modify tx so it will be a double spending, then rejected
        tx.weight += 0.1
        tx.resolve()
        response_success = yield self.web.get(
            'push_tx', {b'hex_tx': bytes(tx.get_struct().hex(), 'utf-8')})
        data_success = response_success.json_value()
        self.assertFalse(data_success['success'])

        # Invalid tx (don't have inputs)
        genesis_tx = get_genesis_transactions(self.manager.tx_storage)[1]
        response_genesis = yield self.web.get(
            'push_tx',
            {b'hex_tx': bytes(genesis_tx.get_struct().hex(), 'utf-8')})
        data_genesis = response_genesis.json_value()
        self.assertFalse(data_genesis['success'])

        # Invalid hex
        response_error1 = yield self.web.get('push_tx', {b'hex_tx': b'XXXX'})
        data_error1 = response_error1.json_value()

        self.assertFalse(data_error1['success'])

        # Invalid tx hex
        response_error2 = yield self.web.get('push_tx', {b'hex_tx': b'a12c'})
        data_error2 = response_error2.json_value()

        self.assertFalse(data_error2['success'])

        # Token creation tx
        tx2 = create_tokens(self.manager,
                            address,
                            mint_amount=100,
                            propagate=False)
        response = yield self.web.get(
            'push_tx', {b'hex_tx': bytes(tx2.get_struct().hex(), 'utf-8')})
        data = response.json_value()
        self.assertTrue(data['success'])
예제 #11
0
class DecodeTxTest(_BaseResourceTest._ResourceTest):
    def setUp(self):
        super().setUp()
        self.web = StubSite(PushTxResource(self.manager))
        self.web_tokens = StubSite(SendTokensResource(self.manager))
        self.web_mining = StubSite(MiningResource(self.manager))
        self.web_balance = StubSite(BalanceResource(self.manager))
        self.web_history = StubSite(HistoryResource(self.manager))

    @inlineCallbacks
    def _run_push_tx_test(self, is_post: bool) -> Generator:
        # Mining new block
        response_mining = yield self.web_mining.get('mining')
        data_mining = response_mining.json_value()
        block_bytes = resolve_block_bytes(block_bytes=data_mining['block_bytes'])
        yield self.web_mining.post('mining', {'block_bytes': base64.b64encode(block_bytes).decode('utf-8')})

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

        # Creating a valid transaction to be pushed to the network
        blocks = add_new_blocks(self.manager, 3, advance_clock=2)
        add_blocks_unlock_reward(self.manager)
        tx_id = blocks[0].hash
        output = blocks[0].outputs[0]
        script_type_out = parse_address_script(output.script)
        assert script_type_out is not None
        address = script_type_out.address
        private_key = self.manager.wallet.get_private_key(address)

        script_out_addr = self.get_address(0)
        assert script_out_addr is not None
        output_address = decode_address(script_out_addr)
        value = self.manager.get_tokens_issued_per_block(1)
        o = TxOutput(value, create_output_script(output_address, None))
        i = TxInput(tx_id, 0, b'')
        tx = Transaction(inputs=[i], outputs=[o])

        data_to_sign = tx.get_sighash_all()
        public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(data_to_sign, private_key)
        i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes)
        tx.inputs = [i]
        tx.timestamp = int(self.clock.seconds())
        tx.weight = self.manager.minimum_tx_weight(tx)
        tx.parents = self.manager.get_new_tx_parents(tx.timestamp)
        tx.resolve()

        push_tx_fn = self.web.post if is_post else self.web.get
        hex_param = 'hex_tx' if is_post else b'hex_tx'
        force_param = 'force' if is_post else b'force'

        tx_hex = tx.get_struct().hex()
        hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8')

        response = yield push_tx_fn('push_tx', {hex_param: hex_data})
        data = response.json_value()
        self.assertTrue(data['success'])

        # Sending token to random address without input
        data_json = {'outputs': [{'address': self.get_address(0), 'value': 5}], 'inputs': []}
        yield self.web_tokens.post('wallet/send_tokens', {'data': data_json})

        # modify tx so it will be a double spending, then rejected
        tx.weight += 0.1
        tx.resolve()

        tx_hex = tx.get_struct().hex()
        hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8')
        response_success = yield push_tx_fn('push_tx', {hex_param: hex_data})
        data_success = response_success.json_value()
        self.assertFalse(data_success['success'])

        # invalid transaction, without forcing
        tx.timestamp = 5
        tx.inputs = [TxInput(blocks[1].hash, 0, b'')]
        script_type_out = parse_address_script(blocks[1].outputs[0].script)
        assert script_type_out is not None
        private_key = self.manager.wallet.get_private_key(script_type_out.address)
        data_to_sign = tx.get_sighash_all()
        public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(data_to_sign, private_key)
        tx.inputs[0].data = P2PKH.create_input_data(public_key_bytes, signature_bytes)

        tx_hex = tx.get_struct().hex()
        hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8')
        response = yield push_tx_fn('push_tx', {hex_param: hex_data})
        data = response.json_value()
        self.assertFalse(data['success'])

        # force
        tx_hex = tx.get_struct().hex()
        hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8')
        response = yield push_tx_fn('push_tx', {hex_param: hex_data, force_param: True if is_post else b'true'})
        data = response.json_value()
        self.assertFalse(data['success'])

        # Invalid tx (don't have inputs)
        genesis_tx = next(x for x in self.manager.tx_storage.get_all_genesis() if x.is_transaction)
        genesis_hex = genesis_tx.get_struct().hex()
        hex_data = genesis_hex if is_post else bytes(genesis_hex, 'utf-8')
        response_genesis = yield push_tx_fn('push_tx', {hex_param: hex_data})
        data_genesis = response_genesis.json_value()
        self.assertFalse(data_genesis['success'])

        # Invalid tx hex
        invalid_hex_data = 'a12c' if is_post else b'a12c'
        response_error2 = yield push_tx_fn('push_tx', {hex_param: invalid_hex_data})
        data_error2 = response_error2.json_value()
        self.assertFalse(data_error2['success'])

        # Token creation tx
        tx2 = create_tokens(self.manager, address, mint_amount=100, propagate=False)
        tx2_hex = tx2.get_struct().hex()
        hex_data = tx2_hex if is_post else bytes(tx2_hex, 'utf-8')
        response = yield push_tx_fn('push_tx', {hex_param: hex_data})
        data = response.json_value()
        self.assertTrue(data['success'])

    @inlineCallbacks
    def _run_invalid_params_test(self, is_post: bool) -> Generator:
        push_tx_fn = self.web.post if is_post else self.web.get
        hex_param = 'hex_tx' if is_post else b'hex_tx'

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

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

        # Invalid hex
        invalid_hex_data = 'XXXX' if is_post else b'XXXX'
        response = yield push_tx_fn('push_tx', {hex_param: invalid_hex_data})
        data = response.json_value()
        self.assertFalse(data['success'])

    def test_push_tx_get(self):
        self._run_push_tx_test(False)

    def test_push_tx_post(self):
        self._run_push_tx_test(True)

    def test_invalid_params_get(self):
        self._run_invalid_params_test(False)

    def test_invalid_params_post(self):
        self._run_invalid_params_test(True)
예제 #12
0
class TransactionTest(_BaseResourceTest._ResourceTest):
    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):
        self.manager.test_mode = 0  # 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):
        self.manager.test_mode = 0  # 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'
        })
예제 #13
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']))
예제 #14
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'])