async def test_subscriptions_without_signing(self):

        val = 761751855997712

        ws_con = await self.websocket_connect(None)
        await ws_con.call("subscribe", [TEST_ID_ADDRESS])

        async with self.pool.acquire() as con:
            row = await con.fetchrow(
                "SELECT COUNT(*) FROM notification_registrations WHERE eth_address = $1",
                TEST_ID_ADDRESS)
        self.assertEqual(row['count'], 1)

        tx_hash = await self.faucet(TEST_ID_ADDRESS, val)

        result = await ws_con.read()
        self.assertIsNotNone(result)
        payment = parse_sofa_message(result['params']['message'])
        self.assertEqual(payment['txHash'], tx_hash)
        ws_con.con.close()

        ws_con = await self.websocket_connect(None)

        await self.faucet(TEST_ID_ADDRESS, val)

        # make sure we don't still get any notifications
        result = await ws_con.read(timeout=1)
        self.assertIsNone(result)

        # make sure subscriptions don't cross over when no toshi id is used
        await ws_con.call("subscribe", [TEST_WALLET_ADDRESS])
        ws_con2 = await self.websocket_connect(None)

        tx_hash = await self.faucet(TEST_WALLET_ADDRESS, val)

        result = await ws_con.read()
        self.assertIsNotNone(result)
        payment = parse_sofa_message(result['params']['message'])
        self.assertEqual(payment['txHash'], tx_hash)

        # check 2nd connection has no notifications
        result = await ws_con2.read(timeout=1)
        self.assertIsNone(result)

        # make sure unsubscribe works
        await ws_con.call("unsubscribe", [TEST_WALLET_ADDRESS])

        await self.faucet(TEST_WALLET_ADDRESS, val)

        result = await ws_con.read(timeout=1)
        # handle case where confirmed comes in before the unsibscribe
        if result:
            payment = parse_sofa_message(result['params']['message'])
            self.assertEqual(payment['txHash'], tx_hash)
            self.assertEqual(payment['status'], 'confirmed')
            result = await ws_con.read(timeout=1)

        self.assertIsNone(result)
예제 #2
0
    async def test_get_balance(self, *, ethminer):

        addr = '0x39bf9e501e61440b4b268d7b2e9aa2458dd201bb'
        val = 8761751855997712
        val2 = int(val / 2)

        await self.wait_on_tx_confirmation(await self.faucet(TEST_ID_ADDRESS, val))

        ws_con = await self.websocket_connect(TEST_ID_KEY)

        result = await ws_con.call("get_balance", [TEST_ID_ADDRESS])

        self.assertEqual(int(result['confirmed_balance'][2:], 16), val)
        self.assertEqual(int(result['unconfirmed_balance'][2:], 16), val)

        # make sure no blocks get mined for a bit
        ethminer.pause()

        tx = create_transaction(nonce=0x100000, gasprice=DEFAULT_GASPRICE, startgas=DEFAULT_STARTGAS,
                                to=addr, value=val2, network_id=self.network_id)
        tx = sign_transaction(tx, TEST_ID_KEY)
        tx = encode_transaction(tx)

        await ws_con.call("subscribe", [addr])

        tx_hash = await ws_con.call("send_transaction", {"tx": tx})

        new_balance = val - (val2 + DEFAULT_STARTGAS * DEFAULT_GASPRICE)

        result = await ws_con.call("get_balance", [TEST_ID_ADDRESS])
        self.assertEqual(int(result['confirmed_balance'][2:], 16), val)
        self.assertEqual(int(result['unconfirmed_balance'][2:], 16), new_balance)

        # check for the unconfirmed notification
        result = await ws_con.read()
        self.assertIsNotNone(result)
        payment = parse_sofa_message(result['params']['message'])
        self.assertEqual(payment['txHash'], tx_hash)
        self.assertEqual(payment['status'], 'unconfirmed')

        # restart mining
        ethminer.start()

        result = await ws_con.read()
        payment = parse_sofa_message(result['params']['message'])
        self.assertEqual(payment['txHash'], tx_hash)
        self.assertEqual(payment['status'], 'confirmed')

        result = await ws_con.call("get_balance", [TEST_ID_ADDRESS])

        self.assertEqual(int(result['confirmed_balance'][2:], 16), new_balance,
                         "int('{}', 16) != {}".format(result['confirmed_balance'], new_balance))
        self.assertEqual(int(result['unconfirmed_balance'][2:], 16), new_balance,
                         "int('{}', 16) != {}".format(result['unconfirmed_balance'], new_balance))
    async def test_subscriptions(self):

        val = 761751855997712

        ws_con = await self.websocket_connect(TEST_ID_KEY)
        await ws_con.call("subscribe", [TEST_ID_ADDRESS])

        async with self.pool.acquire() as con:
            row = await con.fetchrow(
                "SELECT COUNT(*) FROM notification_registrations WHERE toshi_id = $1",
                TEST_ID_ADDRESS)
        self.assertEqual(row['count'], 1)

        tx_hash = await self.faucet(TEST_ID_ADDRESS, val)

        result = await ws_con.read()
        self.assertIsNotNone(result)
        payment = parse_sofa_message(result['params']['message'])
        self.assertEqual(payment['txHash'], tx_hash)
        ws_con.con.close()

        ws_con = await self.websocket_connect(TEST_ID_KEY)

        await self.faucet(TEST_ID_ADDRESS, val)

        # make sure we don't still get any notifications
        result = await ws_con.read(timeout=1)
        self.assertIsNone(result)

        # make sure subscriptions to a different address from the toshi id work
        await ws_con.call("subscribe", [TEST_WALLET_ADDRESS])

        tx_hash = await self.faucet(TEST_WALLET_ADDRESS, val)

        result = await ws_con.read()
        self.assertIsNotNone(result)
        payment = parse_sofa_message(result['params']['message'])
        self.assertEqual(payment['txHash'], tx_hash)

        await ws_con.call("unsubscribe", [TEST_WALLET_ADDRESS])

        await self.faucet(TEST_WALLET_ADDRESS, val)

        result = await ws_con.read(timeout=1)
        # handle case where confirmed comes in before the unsibscribe
        if result:
            payment = parse_sofa_message(result['params']['message'])
            self.assertEqual(payment['txHash'], tx_hash)
            self.assertEqual(payment['status'], 'confirmed')
            result = await ws_con.read(timeout=1)

        self.assertIsNone(result)
    async def test_redis_failures(self, *, redis_server, push_client):

        """Tests that the block monitor recovers correctly after errors
        in the ethereum node"""

        addr = '0x39bf9e501e61440b4b268d7b2e9aa2458dd201bb'
        val = 761751855997712

        body = {
            "registration_id": TEST_GCM_ID,
            "address": TEST_ID_ADDRESS
        }
        resp = await self.fetch_signed("/gcm/register", signing_key=TEST_ID_KEY, method="POST", body=body)
        self.assertResponseCodeEqual(resp, 204, resp.body)

        tx_hash = await self.faucet(TEST_ID_ADDRESS, val * 1000)
        for status in ['unconfirmed', 'confirmed']:
            _, pn = await push_client.get()
            message = parse_sofa_message(pn['message'])
            self.assertIsInstance(message, SofaPayment)
            self.assertEqual(message['txHash'], tx_hash)
            if status == 'unconfirmed' and message['status'] == 'confirmed':
                # this can happen if the monitor sees the confirmed message
                # first as the faucet sends raw txs
                break
            self.assertEqual(message['status'], status)

        # do this 10 times to see if the pool is working as expected as well
        for _ in range(0, 10):

            # kill the server!
            redis_server.pause()

            await asyncio.sleep(0.1)

            # start it again
            redis_server.start()

            # see if everything still works
            tx_hash = await self.send_tx(TEST_ID_KEY, addr, 10 ** 10)
            # wait for 2 messages, as the confirmed one should come
            # from the block monitor
            for status in ['unconfirmed', 'confirmed']:
                _, pn = await push_client.get()
                message = parse_sofa_message(pn['message'])
                self.assertIsInstance(message, SofaPayment)
                self.assertEqual(message['txHash'], tx_hash)
                self.assertEqual(message['status'], status)
예제 #5
0
    async def test_get_sofa_payment(self):

        body = {
            "from": FAUCET_ADDRESS,
            "to": TEST_ADDRESS,
            "value": 10 ** 10
        }

        resp = await self.fetch("/tx/skel", method="POST", body=body)
        self.assertEqual(resp.code, 200)

        body = json_decode(resp.body)
        tx = sign_transaction(body['tx'], FAUCET_PRIVATE_KEY)
        resp = await self.fetch("/tx", method="POST", body={
            "tx": tx
        })
        self.assertEqual(resp.code, 200, resp.body)
        body = json_decode(resp.body)
        tx_hash = body['tx_hash']

        await self.wait_on_tx_confirmation(tx_hash)

        resp = await self.fetch("/tx/{}?format=sofa".format(tx_hash), method="GET")
        self.assertEqual(resp.code, 200, resp.body)

        message = parse_sofa_message(resp.body.decode('utf-8'))
        self.assertEqual(message["txHash"], tx_hash)
        self.assertEqual(message["status"], "confirmed")
예제 #6
0
    async def test_sending_transactions_and_storing_the_hash_correctly(
            self, *, push_client):
        """This test is born out of the fact that `UnsingedTransaction.hash` always
        calculates the hash of the transaction without the signature, even after `.sign`
        has been called on the transaction. This caused errors in what was being stored
        in the database and incorrectly detecting transaction overwrites.

        This test exposed the behaviour correctly so that it could be fixed"""

        # register for GCM PNs
        body = {"registration_id": TEST_GCM_ID, "address": TEST_ID_ADDRESS}
        resp = await self.fetch_signed("/gcm/register",
                                       signing_key=TEST_ID_KEY,
                                       method="POST",
                                       body=body)
        self.assertResponseCodeEqual(resp, 204, resp.body)

        body = {"from": FAUCET_ADDRESS, "to": TEST_ID_ADDRESS, "value": 10**10}

        resp = await self.fetch("/tx/skel", method="POST", body=body)

        self.assertEqual(resp.code, 200)

        body = json_decode(resp.body)
        tx = decode_transaction(body['tx'])
        tx = sign_transaction(tx, FAUCET_PRIVATE_KEY)
        sig = signature_from_transaction(tx)

        body = {"tx": body['tx'], "signature": data_encoder(sig)}

        resp = await self.fetch("/tx", method="POST", body=body)

        self.assertEqual(resp.code, 200, resp.body)

        body = json_decode(resp.body)
        tx_hash = body['tx_hash']

        async def check_db():
            async with self.pool.acquire() as con:
                rows = await con.fetch(
                    "SELECT * FROM transactions WHERE nonce = $1", tx.nonce)
            self.assertEqual(len(rows), 1)
            self.assertEqual(rows[0]['hash'], tx_hash)
            if rows[0]['status'] is not None:
                self.assertEqual(rows[0]['status'], 'unconfirmed')
            self.assertIsNone(rows[0]['error'])

        await self.wait_on_tx_confirmation(tx_hash, check_db)
        while True:
            token, payload = await push_client.get()
            message = parse_sofa_message(payload['message'])

            self.assertIsInstance(message, SofaPayment)
            self.assertEqual(message['txHash'], tx_hash)

            if message['status'] == "confirmed":
                break
예제 #7
0
    async def test_get_multiple_push_notification(self, *, push_client):

        # register for GCM PNs
        body = {"registration_id": TEST_GCM_ID, "address": TEST_WALLET_ADDRESS}
        resp = await self.fetch_signed("/gcm/register",
                                       signing_key=TEST_ID_KEY,
                                       method="POST",
                                       body=body)
        self.assertResponseCodeEqual(resp, 204, resp.body)
        # register 2nd device
        body = {
            "registration_id": TEST_GCM_ID_2,
            "address": TEST_WALLET_ADDRESS
        }
        resp = await self.fetch_signed("/gcm/register",
                                       signing_key=TEST_ID_KEY,
                                       method="POST",
                                       body=body)
        self.assertResponseCodeEqual(resp, 204, resp.body)

        async with self.pool.acquire() as con:
            rows = await con.fetch(
                "SELECT * FROM notification_registrations WHERE toshi_id = $1",
                TEST_ID_ADDRESS)
        self.assertIsNotNone(rows)
        self.assertEqual(len(rows), 2)

        value = 2821181018869341261
        tx_hash = await self.faucet(TEST_WALLET_ADDRESS, value)

        unconfirmed_counts = {TEST_GCM_ID: 0, TEST_GCM_ID_2: 0}
        while True:
            token, payload = await push_client.get()

            self.assertTrue(token in (TEST_GCM_ID, TEST_GCM_ID_2))

            message = parse_sofa_message(payload['message'])

            self.assertIsInstance(message, SofaPayment)
            self.assertEqual(message['value'], hex(value))
            self.assertEqual(message['txHash'], tx_hash)

            if message['status'] == "confirmed":
                break

            self.assertEqual(message['status'], "unconfirmed")
            unconfirmed_counts[token] += 1
            if unconfirmed_counts[token] > 1:
                warnings.warn(
                    "got more than one unconfirmed notification for a single device"
                )
    async def test_eth_nodes_out_of_sync_on_confirm(self, *, monitor,
                                                    push_client):

        # shutdown the monitor so that the confirmed transaction status can be
        # manually triggered before the transaction is actually confirmed
        await monitor.shutdown()

        val = 761751855997712

        body = {"registration_id": TEST_GCM_ID, "address": TEST_ID_ADDRESS}
        resp = await self.fetch_signed("/gcm/register",
                                       signing_key=TEST_ID_KEY,
                                       method="POST",
                                       body=body)
        self.assertResponseCodeEqual(resp, 204, resp.body)

        tx_hash = await self.send_tx(FAUCET_PRIVATE_KEY, TEST_ID_ADDRESS, val)
        tx_id = None
        while tx_id is None:
            async with self.pool.acquire() as con:
                tx_id = await con.fetchval(
                    "SELECT transaction_id FROM transactions WHERE hash = $1",
                    tx_hash)
        # force confirmed, even though it should take some time
        manager_dispatcher.update_transaction(tx_id, 'confirmed')

        _, pn = await push_client.get()
        message = parse_sofa_message(pn['message'])
        self.assertIsInstance(message, SofaPayment)
        self.assertEqual(message['txHash'], tx_hash)
        self.assertEqual(message['status'], 'unconfirmed')
        _, pn = await push_client.get()
        message = parse_sofa_message(pn['message'])
        self.assertIsInstance(message, SofaPayment)
        self.assertEqual(message['txHash'], tx_hash)
        self.assertEqual(message['status'], 'confirmed')
    async def test_erc20_balance_update(self, *, parity, push_client, monitor):
        """Tests that on initial PN registration the user's token cache is updated

        Creates 4 erc20 tokens, gives a test address some of 3 of those tokens,
        registeres that address for PNs, and checks that the balance cache is updated
        """

        token_args = [
            ["TST", "Test Token", 18],
            ["BOB", "Big Old Bucks", 10],
            ["HMM", "Hmmmmmm", 5],
            ["NOT", "Not This One", 20]
        ]
        tokens = {}
        contracts = {}

        for args in token_args:
            contract = await self.deploy_erc20_contract(*args)
            contracts[contract.address] = contract
            tokens[contract.address] = {"symbol": args[0], "name": args[1], "decimals": args[2], "contract": contract}
            args.append(contract.address)

        for token in tokens.values():
            if token['symbol'] == token_args[-1][0]:
                continue
            # give "1" of each token (except NOT)
            contract = token['contract']
            await contract.transfer.set_sender(FAUCET_PRIVATE_KEY)(TEST_ADDRESS, 10 ** token['decimals'])

            result = await contract.balanceOf(TEST_ADDRESS)
            self.assertEquals(result, 10 ** token['decimals'])

        # force block check to clear out txs pre registration
        await monitor.block_check()

        resp = await self.fetch_signed("/apn/register", signing_key=TEST_PRIVATE_KEY, method="POST", body={
            "registration_id": TEST_APN_ID
        })
        self.assertEqual(resp.code, 204)

        # get user's initial token balance
        await asyncio.sleep(0.1)

        resp = await self.fetch("/tokens/{}".format(TEST_ADDRESS))

        self.assertResponseCodeEqual(resp, 200)
        body = json_decode(resp.body)
        self.assertEqual(len(body['tokens']), len(token_args) - 1)

        for balance in body['tokens']:
            self.assertEqual(int(balance['value'], 16), 10 ** tokens[balance['contract_address']]['decimals'])

        await self.send_tx(FAUCET_PRIVATE_KEY, TEST_ADDRESS, 10 ** 18)

        # wait for unconfirmed and confirmed PN, otherwise the contract send will overwrite it (TODO)
        await push_client.get()
        await push_client.get()

        # test that receiving new tokens triggers a PN
        for token in tokens.values():
            contract = token['contract']
            # first test PNs from external transactions
            tx_hash = await contract.transfer.set_sender(FAUCET_PRIVATE_KEY)(TEST_ADDRESS, 10 ** token['decimals'], wait_for_confirmation=False)
            pn = await push_client.get()
            sofa = parse_sofa_message(pn[1]['message'])
            self.assertEqual(sofa['status'], 'confirmed')
            self.assertEqual(sofa['txHash'], tx_hash)
            self.assertEqual(sofa.type, "TokenPayment")
            self.assertEqual(sofa['contractAddress'], contract.address)
            self.assertEqual(sofa['value'], hex(10 ** token['decimals']))
            self.assertEqual(sofa['toAddress'], TEST_ADDRESS)
            # now test PNs from toshi generated transactions
            raw_tx = await contract.transfer.get_raw_tx.set_sender(FAUCET_PRIVATE_KEY)(TEST_ADDRESS, 10 ** token['decimals'])
            tx_hash = await self.send_raw_tx(raw_tx, wait_on_tx_confirmation=False)
            pn = await push_client.get()
            sofa = parse_sofa_message(pn[1]['message'])
            self.assertEqual(sofa['status'], 'confirmed')
            self.assertEqual(sofa['txHash'], tx_hash)
            self.assertEqual(sofa.type, "TokenPayment")
            self.assertEqual(sofa['contractAddress'], contract.address)
            self.assertEqual(sofa['value'], hex(10 ** token['decimals']))
            self.assertEqual(sofa['toAddress'], TEST_ADDRESS)
            await asyncio.sleep(0.1)
            async with self.pool.acquire() as con:
                balance = await con.fetchrow("SELECT * FROM token_balances WHERE eth_address = $1 AND contract_address = $2",
                                             TEST_ADDRESS, contract.address)
            self.assertEqual(int(balance['value'], 16), (10 ** token['decimals']) * (3 if token['symbol'] != token_args[-1][0] else 2),
                             "invalid balance after updating {} token".format(token['symbol']))
    async def test_list_payment_updates(self):

        addr = '0x39bf9e501e61440b4b268d7b2e9aa2458dd201bb'

        thetime = int(time.time())

        # make sure half of the unconfirmed txs
        stime = thetime - 300

        async def store_tx(status, created, updated=None):
            tx_hash = data_encoder(os.urandom(64))
            from_addr = data_encoder(os.urandom(20))
            value = random.randint(10**15, 10**20)
            if updated is None:
                updated = created
            async with self.pool.acquire() as con:
                await con.execute(
                    "INSERT INTO transactions (hash, from_address, to_address, nonce, value, gas, gas_price, status, created, updated) "
                    "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
                    tx_hash, from_addr, addr, 1, hex(value),
                    hex(DEFAULT_STARTGAS), hex(DEFAULT_GASPRICE), status,
                    datetime.utcfromtimestamp(created),
                    datetime.utcfromtimestamp(updated))

        txs_per_state = 5
        # create 5 transactions that were created before the start time, but confirmed after

        created = stime - 30
        updated = stime + 30
        for i in range(0, txs_per_state):
            await store_tx('confirmed', created + i, updated + i)

        # create 5 transactions that were both created and confirmed during the requested period
        created = stime + 30
        updated = stime + 60
        for i in range(0, txs_per_state):
            await store_tx('confirmed', created + i, updated + i)

        # create 5 transactions created but not confirmed during the requested period
        created = stime + 60
        for i in range(0, txs_per_state):
            await store_tx('unconfirmed', created + i)

        # create 5 transactions outside of the requested period
        created = thetime
        for i in range(0, txs_per_state):
            await store_tx('unconfirmed', created + i)

        ws_con = await self.websocket_connect(TEST_ID_KEY)

        result = await ws_con.call("list_payment_updates",
                                   [addr, stime, thetime])

        # expect 5 confirmed payments
        c = 0
        for i in range(0, txs_per_state):
            self.assertEqual(
                parse_sofa_message(result[c + i])['status'], 'confirmed')

        # expect 5 txs with both unconfirmed and confirmed
        c += txs_per_state
        for i in range(0, txs_per_state):
            self.assertEqual(
                parse_sofa_message(result[c + (i * 2)])['status'],
                'unconfirmed')
            self.assertEqual(
                parse_sofa_message(result[c + (i * 2) + 1])['status'],
                'confirmed')

        c += txs_per_state * 2

        # expect 5 unconfirmed txs
        for i in range(0, txs_per_state):
            self.assertEqual(
                parse_sofa_message(result[c + i])['status'], 'unconfirmed')

        # make sure there's no more!
        c += txs_per_state
        self.assertEqual(len(result), c)
    async def test_get_single_push_notification(self, *, push_client):
        """makes sure both source and target of a transaction get a confirmation
        push notification"""

        # register for GCM PNs
        body = {
            "registration_id": TEST_GCM_ID,
            "address": TEST_WALLET_ADDRESS
        }
        resp = await self.fetch_signed("/gcm/register", signing_key=TEST_ID_KEY, method="POST", body=body)
        self.assertResponseCodeEqual(resp, 204, resp.body)

        # register source account for pn
        body = {
            "registration_id": TEST_GCM_ID_2,
            "address": FAUCET_ADDRESS
        }
        resp = await self.fetch_signed("/gcm/register", signing_key=FAUCET_PRIVATE_KEY, method="POST", body=body)
        self.assertResponseCodeEqual(resp, 204, resp.body)

        async with self.pool.acquire() as con:
            rows1 = await con.fetch("SELECT * FROM notification_registrations WHERE toshi_id = $1", TEST_ID_ADDRESS)
            rows2 = await con.fetch("SELECT * FROM notification_registrations WHERE toshi_id = $1", FAUCET_ADDRESS)
        self.assertEqual(len(rows1), 1)
        self.assertEqual(len(rows2), 1)

        value = 2821181018869341261
        tx_hash = await self.send_tx(FAUCET_PRIVATE_KEY, TEST_WALLET_ADDRESS, value)

        unconfirmed_count = 0
        confirmed_1 = None
        unconfirmed_1 = None
        while True:
            token, payload = await push_client.get()

            self.assertIn(token, [TEST_GCM_ID, TEST_GCM_ID_2])

            message = parse_sofa_message(payload['message'])

            self.assertIsInstance(message, SofaPayment)
            self.assertEqual(message['value'], hex(value))
            self.assertEqual(message['txHash'], tx_hash)
            self.assertEqual(message['networkId'], '66')

            if message['status'] == "confirmed":
                # ensures that the 2 expected confirmed pns are
                # meant for the different targets
                if confirmed_1:
                    self.assertNotEqual(token, confirmed_1)
                    break
                else:
                    confirmed_1 = token
                    continue

            self.assertEqual(message['status'], "unconfirmed")
            if unconfirmed_1:
                self.assertNotEqual(token, unconfirmed_1)
            else:
                unconfirmed_1 = token
            unconfirmed_count += 1
            # since this tx came from outside our system there's no guarantee we
            # even see this since the block monitor might miss it before it's
            # actually confirmed
            if unconfirmed_count > 2:
                warnings.warn("got more than one unconfirmed notification for a single transaction")
    async def test_always_get_unconfirmed_push_notification(self, *, ethminer, push_client):
        """Tests that when tx's are send through our systems we always get
        an unconfirmed push notification"""

        # register for GCM PNs
        body = {
            "registration_id": TEST_GCM_ID,
            "address": TEST_WALLET_ADDRESS
        }
        resp = await self.fetch_signed("/gcm/register", signing_key=TEST_ID_KEY, method="POST", body=body)
        self.assertResponseCodeEqual(resp, 204, resp.body)

        async with self.pool.acquire() as con:
            rows = await con.fetch("SELECT * FROM notification_registrations WHERE toshi_id = $1", TEST_ID_ADDRESS)
        self.assertIsNotNone(rows)
        self.assertEqual(len(rows), 1)

        # run this a bunch of times to see if
        # we can expose any race conditions
        for iteration in range(4):

            if iteration > 2:
                ethminer.pause()

            value = 2821181018869341261

            resp = await self.fetch("/tx/skel", method="POST", body={
                "from": FAUCET_ADDRESS,
                "to": TEST_WALLET_ADDRESS,
                "value": value
            })
            self.assertResponseCodeEqual(resp, 200, resp.body)
            body = json_decode(resp.body)
            tx = sign_transaction(body['tx'], FAUCET_PRIVATE_KEY)
            resp = await self.fetch("/tx", method="POST", body={
                "tx": tx
            })
            self.assertResponseCodeEqual(resp, 200, resp.body)
            tx_hash = json_decode(resp.body)['tx_hash']

            tx = decode_transaction(tx)
            self.assertEqual(tx_hash, data_encoder(tx.hash))

            if iteration > 2:
                await asyncio.sleep(5)
                ethminer.start()

            async with self.pool.acquire() as con:
                rows = await con.fetch("SELECT * FROM transactions WHERE nonce = $1", tx.nonce)
            self.assertEqual(len(rows), 1)

            unconfirmed_count = 0
            while True:
                token, payload = await push_client.get()

                self.assertEqual(token, TEST_GCM_ID)

                message = parse_sofa_message(payload['message'])

                self.assertIsInstance(message, SofaPayment)
                self.assertEqual(message['value'], hex(value))
                self.assertEqual(message['txHash'], tx_hash)

                if message['status'] == "confirmed":
                    break

                self.assertEqual(message['status'], "unconfirmed")
                unconfirmed_count += 1
            # when the tx is sent through our systems we should
            # always get one unconfirmed notification, and we
            # should never get more than one
            self.assertEqual(unconfirmed_count, 1)