def test_session_topup(
        doggo_proxy,
        session: Session,
        http_doggo_url: str
):

    # Create a channel that has just enough capacity for one transfer.
    session.initial_deposit = lambda x: 0
    check_response(session.get(http_doggo_url))

    client = session.client
    open_channels = client.get_open_channels()
    assert len(open_channels) == 1
    channel1 = open_channels[0]
    assert channel1 == session.channel
    assert channel1.balance_sig
    assert channel1.balance == channel1.deposit

    # Do another payment. Topup should occur.
    check_response(session.get(http_doggo_url))
    open_channels = client.get_open_channels()
    assert len(open_channels) == 1
    channel2 = open_channels[0]
    assert channel2 == session.channel
    assert channel2.balance_sig
    assert channel2.balance < channel2.deposit
    assert channel1 == channel2
Exemple #2
0
def run(private_key: str,
        password_path: str,
        resource: str,
        channel_manager_address: str = None,
        web3: Web3 = None,
        retry_interval: float = 5,
        endpoint_url: str = 'http://localhost:5000'):
    # Create the client session.
    session = Session(endpoint_url=endpoint_url,
                      private_key=private_key,
                      key_password_path=password_path,
                      channel_manager_address=channel_manager_address,
                      web3=web3,
                      retry_interval=retry_interval)
    # Get the resource. If payment is required, client will attempt to create
    # a channel or will use existing one.
    response = session.get('{}/{}'.format(endpoint_url, resource))

    if response.status_code == requests.codes.OK:
        if re.match('^text/', response.headers['Content-Type']):
            logging.info("Got the resource {} type={}:\n{}".format(
                resource, response.headers.get('Content-Type', '???'),
                response.text))
        else:
            logging.info("Got the resource {} type={} (not echoed)".format(
                resource, response.headers.get('Content-Type', '???')))
    else:
        logging.error("Error getting the resource. Code={} body={}".format(
            response.status_code, response.text))
    return response
Exemple #3
0
class ETHTickerClient(ttk.Frame):
    def __init__(
            self,
            sender_privkey: str,
            session: Session = None,
            poll_interval: float = 5
    ) -> None:
        self.poll_interval = poll_interval

        self.root = tkinter.Tk()
        ttk.Frame.__init__(self, self.root)
        self.root.title('µRaiden ETH Ticker')
        self.root.protocol('WM_DELETE_WINDOW', self.close)
        self.pack()
        self.pricevar = tkinter.StringVar(value='0.00 USD')
        ttk.Label(self, textvariable=self.pricevar, font=('Helvetica', '72')).pack()

        if session is None:
            self.session = Session(
                private_key=sender_privkey,
                close_channel_on_exit=True,
                endpoint_url='http://localhost:5000'
            )
        else:
            self.session = session

        self.active_query = False
        self.running = False

    def run(self):
        self.running = True
        self.root.after(0, self.query_price)
        self.root.mainloop()

    def query_price(self):
        if not self.running:
            return
        self.active_query = True

        response = self.session.get('http://localhost:5000/ETHUSD')
        if response:
            price = float(response.json()['last_price'])
            log.info('New price received: {:.2f} USD'.format(price))
            self.pricevar.set('{:.2f} USD'.format(price))
        else:
            log.warning('No response.')

        if self.running:
            self.root.after(int(self.poll_interval * 1000), self.query_price)
        self.active_query = False

    def close(self):
        log.info('Shutting down gracefully.')
        self.running = False
        self.root.destroy()
        # Sloppy handling of thread joining but works for this small demo.
        while self.active_query:
            gevent.sleep(1)

        self.session.close()
Exemple #4
0
    def __init__(
            self,
            sender_privkey: str,
            session: Session = None,
            poll_interval: float = 5
    ) -> None:
        self.poll_interval = poll_interval

        self.root = tkinter.Tk()
        ttk.Frame.__init__(self, self.root)
        self.root.title('µRaiden ETH Ticker')
        self.root.protocol('WM_DELETE_WINDOW', self.close)
        self.pack()
        self.pricevar = tkinter.StringVar(value='0.00 USD')
        ttk.Label(self, textvariable=self.pricevar, font=('Helvetica', '72')).pack()

        if session is None:
            self.session = Session(
                private_key=sender_privkey,
                close_channel_on_exit=True,
                endpoint_url='http://localhost:5000'
            )
        else:
            self.session = session

        self.active_query = False
        self.running = False
Exemple #5
0
def test_cheating_client(
    doggo_proxy,
    web3,
    session: Session,
    wait_for_blocks,
    http_doggo_url: str,
):
    patch_on_http_response(session, abort_on=[502])
    balance = web3.eth.getBalance(doggo_proxy.channel_manager.receiver)
    assert balance > 0
    # remove all receiver's eth
    web3.eth.sendTransaction({
        'from': doggo_proxy.channel_manager.receiver,
        'to': FAUCET_ADDRESS,
        'value': balance - 90000
    })
    wait_for_blocks(1)
    session.get(http_doggo_url)
    # proxy is expected to return 502 - it has no funds
    assert session.last_response.status_code == 502
    web3.eth.sendTransaction({
        'from': FAUCET_ADDRESS,
        'to': doggo_proxy.channel_manager.receiver,
        'value': balance
    })
    wait_for_blocks(1)
    session.get(http_doggo_url)
    # now it should proceed normally
    assert session.last_response.status_code == 200
Exemple #6
0
def test_session_topup(
        doggo_proxy,
        session: Session,
        http_doggo_url: str
):

    # Create a channel that has just enough capacity for one transfer.
    session.initial_deposit = lambda x: 0
    check_response(session.get(http_doggo_url))

    client = session.client
    open_channels = client.get_open_channels()
    assert len(open_channels) == 1
    channel1 = open_channels[0]
    assert channel1 == session.channel
    assert channel1.balance_sig
    assert channel1.balance == channel1.deposit

    # Do another payment. Topup should occur.
    check_response(session.get(http_doggo_url))
    open_channels = client.get_open_channels()
    assert len(open_channels) == 1
    channel2 = open_channels[0]
    assert channel2 == session.channel
    assert channel2.balance_sig
    assert channel2.balance < channel2.deposit
    assert channel1 == channel2
class ETHTickerClient(ttk.Frame):
    def __init__(
            self,
            sender_privkey: str,
            session: Session = None,
            poll_interval: float = 5
    ) -> None:
        self.poll_interval = poll_interval

        self.root = tkinter.Tk()
        ttk.Frame.__init__(self, self.root)
        self.root.title('µRaiden ETH Ticker')
        self.root.protocol('WM_DELETE_WINDOW', self.close)
        self.pack()
        self.pricevar = tkinter.StringVar(value='0.00 USD')
        ttk.Label(self, textvariable=self.pricevar, font=('Helvetica', '72')).pack()

        if session is None:
            self.session = Session(
                private_key=sender_privkey,
                close_channel_on_exit=True,
                endpoint_url='http://localhost:5000'
            )
        else:
            self.session = session

        self.active_query = False
        self.running = False

    def run(self):
        self.running = True
        self.root.after(0, self.query_price)
        self.root.mainloop()

    def query_price(self):
        if not self.running:
            return
        self.active_query = True

        response = self.session.get('http://localhost:5000/ETHUSD')
        if response:
            price = float(response.json()['last_price'])
            log.info('New price received: {:.2f} USD'.format(price))
            self.pricevar.set('{:.2f} USD'.format(price))
        else:
            log.warning('No response.')

        if self.running:
            self.root.after(int(self.poll_interval * 1000), self.query_price)
        self.active_query = False

    def close(self):
        log.info('Shutting down gracefully.')
        self.running = False
        self.root.destroy()
        # Sloppy handling of thread joining but works for this small demo.
        while self.active_query:
            gevent.sleep(1)

        self.session.close()
Exemple #8
0
def test_full_cycle_error_500(session: Session, api_endpoint_address: str,
                              token_address: str, channel_manager_address: str,
                              receiver_address: str):
    session.initial_deposit = lambda x: x

    with requests_mock.mock() as server_mock:
        headers1 = Munch()
        headers1.token_address = token_address
        headers1.contract_address = channel_manager_address
        headers1.receiver_address = receiver_address
        headers1.price = '3'

        headers2 = Munch()
        headers2.cost = '3'

        headers1 = HTTPHeaders.serialize(headers1)
        headers2 = HTTPHeaders.serialize(headers2)

        url = 'http://{}/something'.format(api_endpoint_address)
        server_mock.get(url, [{
            'status_code': 402,
            'headers': headers1
        }, {
            'status_code': 500,
            'headers': {}
        }, {
            'status_code': 200,
            'headers': headers2,
            'text': 'success'
        }])
        response = session.get(url)

    # First cycle, request price.
    request = server_mock.request_history[0]
    assert request.path == '/something'
    assert request.method == 'GET'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address

    # Second cycle, pay price but receive error.
    request = server_mock.request_history[1]
    assert request.path == '/something'
    assert request.method == 'GET'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address
    assert request.headers['RDN-Balance'] == '3'

    assert session.channel.balance == 3
    balance_sig_hex = encode_hex(session.channel.balance_sig)
    assert request.headers['RDN-Balance-Signature'] == balance_sig_hex

    # Third cycle, retry naively.
    request = server_mock.request_history[2]
    assert request.path == '/something'
    assert request.method == 'GET'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address
    assert request.headers['RDN-Balance'] == '3'
    assert session.channel.balance == 3
    assert request.headers['RDN-Balance-Signature'] == balance_sig_hex
    assert session.channel.balance_sig
    assert response.text == 'success'
 def patched_on_invalid_amount(self, method: str, url: str, response: Response, **kwargs):
     self.invalid_amount_received += 1
     price = int(response.headers[HTTPHeaders.PRICE])
     Session.on_invalid_amount(self, method, url, response, **kwargs)
     # on_invalid_amount will already prepare the next payment which we don't execute anymore,
     # so revert that.
     self.channel.update_balance(self.channel.balance - price)
     return False
def test_ssl_client(
        doggo_proxy,
        session: Session,
        https_doggo_url: str
):
    with pytest.raises(SSLError):
        check_response(session.get(https_doggo_url))
    check_response(session.get(https_doggo_url, verify=False))
Exemple #11
0
 def patched_on_invalid_amount(self, method: str, url: str, response: Response, **kwargs):
     self.invalid_amount_received += 1
     price = int(response.headers[HTTPHeaders.PRICE])
     Session.on_invalid_amount(self, method, url, response, **kwargs)
     # on_invalid_amount will already prepare the next payment which we don't execute anymore,
     # so revert that.
     self.channel.update_balance(self.channel.balance - price)
     return False
Exemple #12
0
def test_ssl_client(
        doggo_proxy,
        session: Session,
        https_doggo_url: str
):
    with pytest.raises(SSLError):
        check_response(session.get(https_doggo_url))
    check_response(session.get(https_doggo_url, verify=False))
def test_status_codes(
        doggo_proxy,
        session: Session,
        http_doggo_url: str
):
    response = session.get(http_doggo_url)
    assert response.status_code == 200
    response = session.get(http_doggo_url[:-1])
    assert response.status_code == 404
Exemple #14
0
def test_status_codes(
        doggo_proxy,
        session: Session,
        http_doggo_url: str
):
    response = session.get(http_doggo_url)
    assert response.status_code == 200
    response = session.get(http_doggo_url[:-1])
    assert response.status_code == 404
Exemple #15
0
def test_custom_headers(
        session: Session,
        api_endpoint_address: str,
        token_address: str,
        channel_manager_address: str,
        receiver_address: str
):
    session.initial_deposit = lambda x: x

    with requests_mock.mock(real_http=True) as server_mock:
        headers1 = Munch()
        headers1.token_address = token_address
        headers1.contract_address = channel_manager_address
        headers1.receiver_address = receiver_address
        headers1.price = '7'

        headers2 = Munch()
        headers2.cost = '7'

        headers1 = HTTPHeaders.serialize(headers1)
        headers2 = HTTPHeaders.serialize(headers2)

        url = 'http://{}/something'.format(api_endpoint_address)
        server_mock.get(url, [
            {'status_code': 402, 'headers': headers1},
            {'status_code': 200, 'headers': headers2, 'text': 'success'}
        ])
        response = session.get(url, headers={
            'someheader': 'somevalue',
            # This should override the actual balance but doesn't actually make sense.
            'RDN-Balance': '5'
        })

    # Filter out any requests made to the ethereum node.
    request_history = [request for request in server_mock.request_history if request.port == 5000]

    # First cycle, request price.
    request = request_history[0]
    assert request.path == '/something'
    assert request.method == 'GET'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address
    assert request.headers['RDN-Balance'] == '5'
    assert request.headers['someheader'] == 'somevalue'

    # Second cycle, pay price.
    request = request_history[1]
    assert request.path == '/something'
    assert request.method == 'GET'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address
    assert request.headers['RDN-Balance'] == '5'
    assert request.headers['someheader'] == 'somevalue'

    assert session.channel.balance == 7
    balance_sig_hex = encode_hex(session.channel.balance_sig)
    assert request.headers['RDN-Balance-Signature'] == balance_sig_hex
    assert session.channel.balance_sig
    assert response.text == 'success'
Exemple #16
0
def test_session_existing_channel_topup(doggo_proxy, session: Session,
                                        receiver_address: str,
                                        http_doggo_url: str):
    client = session.client
    session.topup_deposit = lambda x: 13
    channel = client.open_channel(receiver_address, 1)
    check_response(session.get(http_doggo_url))
    assert channel.balance == 2
    assert channel.deposit == 13
def test_custom_headers(
        session: Session,
        api_endpoint_address: str,
        token_address: str,
        channel_manager_address: str,
        receiver_address: str
):
    session.initial_deposit = lambda x: x

    with requests_mock.mock(real_http=True) as server_mock:
        headers1 = Munch()
        headers1.token_address = token_address
        headers1.contract_address = channel_manager_address
        headers1.receiver_address = receiver_address
        headers1.price = '7'

        headers2 = Munch()
        headers2.cost = '7'

        headers1 = HTTPHeaders.serialize(headers1)
        headers2 = HTTPHeaders.serialize(headers2)

        url = 'http://{}/something'.format(api_endpoint_address)
        server_mock.get(url, [
            {'status_code': 402, 'headers': headers1},
            {'status_code': 200, 'headers': headers2, 'text': 'success'}
        ])
        response = session.get(url, headers={
            'someheader': 'somevalue',
            # This should override the actual balance but doesn't actually make sense.
            'RDN-Balance': '5'
        })

    # Filter out any requests made to the ethereum node.
    request_history = [request for request in server_mock.request_history if request.port == 5000]

    # First cycle, request price.
    request = request_history[0]
    assert request.path == '/something'
    assert request.method == 'GET'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address
    assert request.headers['RDN-Balance'] == '5'
    assert request.headers['someheader'] == 'somevalue'

    # Second cycle, pay price.
    request = request_history[1]
    assert request.path == '/something'
    assert request.method == 'GET'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address
    assert request.headers['RDN-Balance'] == '5'
    assert request.headers['someheader'] == 'somevalue'

    assert session.channel.balance == 7
    balance_sig_hex = encode_hex(session.channel.balance_sig)
    assert request.headers['RDN-Balance-Signature'] == balance_sig_hex
    assert session.channel.balance_sig
    assert response.text == 'success'
def test_session_close(
        doggo_proxy,
        session: Session,
        http_doggo_url: str
):
    client = session.client
    check_response(session.get(http_doggo_url))
    session.close_channel()
    open_channels = client.get_open_channels()
    assert len(open_channels) == 0
Exemple #19
0
def test_session_close(
        doggo_proxy,
        session: Session,
        http_doggo_url: str
):
    client = session.client
    check_response(session.get(http_doggo_url))
    session.close_channel()
    open_channels = client.get_open_channels()
    assert len(open_channels) == 0
Exemple #20
0
def test_status_codes(
        doggo_proxy,
        session: Session,
        http_doggo_url: str
):
    patch_on_http_response(session, abort_on=[404])
    session.get(http_doggo_url)
    assert session.last_response.status_code == 200
    session.get(http_doggo_url[:-1])
    assert session.last_response.status_code == 404
Exemple #21
0
def close_channels(private_key: str,
                   password_path: str,
                   resource: str,
                   channel_manager_address: str = None,
                   web3: Web3 = None,
                   retry_interval: float = 5,
                   endpoint_url: str = 'http://0.0.0.0:5010',
                   close_channel: bool = False):

    # Create the client session.
    session = Session(endpoint_url=endpoint_url,
                      private_key=private_key,
                      key_password_path=password_path,
                      channel_manager_address=channel_manager_address,
                      web3=web3,
                      retry_interval=retry_interval,
                      close_channel_on_exit=close_channel)

    #conn = Client(monitor_address)
    #conn.send('share')
    ##print(conn.recv())
    #conn.close()

    print("Private Key:", private_key)
    addr = privkey_to_addr(private_key)
    print("Address:", addr)
    #    response = requests.get('http://0.0.0.0:5000/api/1/channels/{}'.format(addr))
    #response = session.get('{}/api/1/channels/{}'.format('http://0.0.0.0:5000', addr))
    #print(response)

    response = session.get('{}/{}'.format('http://0.0.0.0:5010', resource))
    print(response)
    print(response.text)
    print(response.content)
    print(response.headers)
    #
    time.sleep(4)

    response = session.get('{}/{}'.format('http://0.0.0.0:5010', resource))
    print(response)
    print(response.text)
    print(response.content)
    print(response.headers)

    time.sleep(4)

    response = session.get('{}/{}'.format('http://0.0.0.0:5010', resource))
    print(response)
    print(response.text)
    print(response.content)
    print(response.headers)

    #    time.sleep(10)

    session.channel.close(balance=session.channel.balance - 1)
def test_session_existing_channel_topup(
        doggo_proxy,
        session: Session,
        receiver_address: str,
        http_doggo_url: str
):
    client = session.client
    session.topup_deposit = lambda x: 13
    channel = client.open_channel(receiver_address, 1)
    check_response(session.get(http_doggo_url))
    assert channel.balance == 2
    assert channel.deposit == 13
Exemple #23
0
def test_full_cycle_error_500(session: Session, api_endpoint_address: str,
                              token_address: str, channel_manager_address: str,
                              receiver_address: str):
    session.initial_deposit = lambda x: x

    with requests_mock.mock(real_http=True) as server_mock:
        headers1 = Munch()
        headers1.token_address = token_address
        headers1.contract_address = channel_manager_address
        headers1.receiver_address = receiver_address
        headers1.price = '3'

        headers2 = Munch()
        headers2.cost = '3'

        headers1 = HTTPHeaders.serialize(headers1)
        headers2 = HTTPHeaders.serialize(headers2)

        url = 'http://{}/something'.format(api_endpoint_address)
        server_mock.get(url, [{
            'status_code': 402,
            'headers': headers1
        }, {
            'status_code': 500,
            'headers': {}
        }])
        response = session.get(url)

    # Filter out any requests made to the ethereum node.
    request_history = [
        request for request in server_mock.request_history
        if request.port == 5000
    ]

    # First cycle, request price.
    request = request_history[0]
    assert request.path == '/something'
    assert request.method == 'GET'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address

    # Second cycle, pay price but receive error.
    request = request_history[1]
    assert request.path == '/something'
    assert request.method == 'GET'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address
    assert request.headers['RDN-Balance'] == '3'

    assert session.channel.balance == 3
    balance_sig_hex = encode_hex(session.channel.balance_sig)
    assert request.headers['RDN-Balance-Signature'] == balance_sig_hex
    assert response.status_code == 500
def test_cheating_client(
        doggo_proxy,
        session: Session,
        http_doggo_url: str
):
    """this test scenario where client sends less funds than what is requested
        by the server. In such case, a "RDN-Invalid-Amount=1" header should
        be sent in a server's reply
    """
    def patched_payment(
            self: Session,
            method: str,
            url: str,
            response: Response,
            **kwargs
    ):
        response.headers[HTTPHeaders.PRICE] = str(
            int(response.headers[HTTPHeaders.PRICE]) + self.price_adjust
        )
        return Session.on_payment_requested(self, method, url, response, **kwargs)

    def patched_on_invalid_amount(self, method: str, url: str, response: Response, **kwargs):
        self.invalid_amount_received += 1
        price = int(response.headers[HTTPHeaders.PRICE])
        Session.on_invalid_amount(self, method, url, response, **kwargs)
        # on_invalid_amount will already prepare the next payment which we don't execute anymore,
        # so revert that.
        self.channel.update_balance(self.channel.balance - price)
        return False

    session.on_invalid_amount = types.MethodType(
        patched_on_invalid_amount,
        session
    )
    session.on_payment_requested = types.MethodType(
        patched_payment,
        session
    )

    session.invalid_amount_received = 0

    # correct amount
    session.price_adjust = 0
    response = session.get(http_doggo_url)
    check_response(response)
    assert session.invalid_amount_received == 0
    # underpay
    session.price_adjust = -1
    response = session.get(http_doggo_url)
    assert response.status_code == 402
    assert session.invalid_amount_received == 1
    # overpay
    session.price_adjust = 1
    response = session.get(http_doggo_url)
    assert response.status_code == 402
    assert session.invalid_amount_received == 2
Exemple #25
0
def test_cheating_client(
        doggo_proxy,
        session: Session,
        http_doggo_url: str
):
    """this test scenario where client sends less funds than what is requested
        by the server. In such case, a "RDN-Invalid-Amount=1" header should
        be sent in a server's reply
    """
    def patched_payment(
            self: Session,
            method: str,
            url: str,
            response: Response,
            **kwargs
    ):
        response.headers[HTTPHeaders.PRICE] = str(
            int(response.headers[HTTPHeaders.PRICE]) + self.price_adjust
        )
        return Session.on_payment_requested(self, method, url, response, **kwargs)

    def patched_on_invalid_amount(self, method: str, url: str, response: Response, **kwargs):
        self.invalid_amount_received += 1
        price = int(response.headers[HTTPHeaders.PRICE])
        Session.on_invalid_amount(self, method, url, response, **kwargs)
        # on_invalid_amount will already prepare the next payment which we don't execute anymore,
        # so revert that.
        self.channel.update_balance(self.channel.balance - price)
        return False

    session.on_invalid_amount = types.MethodType(
        patched_on_invalid_amount,
        session
    )
    session.on_payment_requested = types.MethodType(
        patched_payment,
        session
    )

    session.invalid_amount_received = 0

    # correct amount
    session.price_adjust = 0
    response = session.get(http_doggo_url)
    check_response(response)
    assert session.invalid_amount_received == 0
    # underpay
    session.price_adjust = -1
    response = session.get(http_doggo_url)
    assert response is None
    assert session.invalid_amount_received == 1
    # overpay
    session.price_adjust = 1
    response = session.get(http_doggo_url)
    assert response is None
    assert session.invalid_amount_received == 2
Exemple #26
0
def test_full_cycle_success_post(session: Session, api_endpoint_address: str,
                                 token_address: str,
                                 channel_manager_address: str,
                                 receiver_address: str):
    session.initial_deposit = lambda x: x

    with requests_mock.mock() as server_mock:
        headers1 = Munch()
        headers1.token_address = token_address
        headers1.contract_address = channel_manager_address
        headers1.receiver_address = receiver_address
        headers1.price = '7'

        headers2 = Munch()
        headers2.cost = '7'

        headers1 = HTTPHeaders.serialize(headers1)
        headers2 = HTTPHeaders.serialize(headers2)

        url = 'http://{}/something'.format(api_endpoint_address)
        server_mock.post(url, [{
            'status_code': 402,
            'headers': headers1
        }, {
            'status_code': 200,
            'headers': headers2,
            'text': 'success'
        }])
        response = session.post(url, json={'somefield': 'somevalue'})

    # First cycle, request price.
    request = server_mock.request_history[0]
    assert request.path == '/something'
    assert request.method == 'POST'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address
    assert request.json()['somefield'] == 'somevalue'

    # Second cycle, pay price.
    request = server_mock.request_history[1]
    assert request.path == '/something'
    assert request.method == 'POST'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address
    assert request.headers['RDN-Balance'] == '7'
    assert request.json()['somefield'] == 'somevalue'

    assert session.channel.balance == 7
    balance_sig_hex = encode_hex(session.channel.balance_sig)
    assert request.headers['RDN-Balance-Signature'] == balance_sig_hex
    assert session.channel.balance_sig
    assert response.text == 'success'
def test_full_cycle_error_500(
        session: Session,
        api_endpoint_address: str,
        token_address: str,
        channel_manager_address: str,
        receiver_address: str
):
    session.initial_deposit = lambda x: x

    with requests_mock.mock(real_http=True) as server_mock:
        headers1 = Munch()
        headers1.token_address = token_address
        headers1.contract_address = channel_manager_address
        headers1.receiver_address = receiver_address
        headers1.price = '3'

        headers2 = Munch()
        headers2.cost = '3'

        headers1 = HTTPHeaders.serialize(headers1)
        headers2 = HTTPHeaders.serialize(headers2)

        url = 'http://{}/something'.format(api_endpoint_address)
        server_mock.get(url, [
            {'status_code': 402, 'headers': headers1},
            {'status_code': 500, 'headers': {}}
        ])
        response = session.get(url)

    # Filter out any requests made to the ethereum node.
    request_history = [request for request in server_mock.request_history if request.port == 5000]

    # First cycle, request price.
    request = request_history[0]
    assert request.path == '/something'
    assert request.method == 'GET'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address

    # Second cycle, pay price but receive error.
    request = request_history[1]
    assert request.path == '/something'
    assert request.method == 'GET'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address
    assert request.headers['RDN-Balance'] == '3'

    assert session.channel.balance == 3
    balance_sig_hex = encode_hex(session.channel.balance_sig)
    assert request.headers['RDN-Balance-Signature'] == balance_sig_hex
    assert response.status_code == 500
Exemple #28
0
def run(api_endpoint, api_port, **kwargs):
    exclude_kwargs = {'close_channels'}
    kwargs_client = {
        key: value
        for key, value in kwargs.items() if value and key not in exclude_kwargs
    }
    with Client(**kwargs_client) as client:
        m2mclient = Session(client, api_endpoint, api_port)
        resource = m2mclient.run('doggo.jpg')
        log.info('Response: {}'.format(resource))

        if kwargs['close_channels'] is True:
            for channel in client.channels:
                if channel.state == Channel.State.open:
                    channel.close_channel()
Exemple #29
0
def run(private_key: str,
        password_path: str,
        channel_manager_address: str = None,
        reqs: int = 20,
        web3: Web3 = None,
        retry_interval: float = 5,
        endpoint_url: str = 'http://localhost:5000'):
    # Create the client session.
    global session
    session = Session(endpoint_url=endpoint_url,
                      private_key=private_key,
                      key_password_path=password_path,
                      channel_manager_address=channel_manager_address,
                      web3=web3,
                      initial_deposit=lambda price: 10 * 1000 * 1000 * price,
                      topup_deposit=lambda price: 10 * 1000 * 1000 * price,
                      close_channel_on_exit=True,
                      retry_interval=retry_interval)
    resource = "/test/1"
    # Get the resource. If payment is required, client will attempt to create
    # a channel or will use existing one.
    last_time = time.time()
    global rqs
    last_reqs = 0
    for i in range(0, reqs):
        response = session.get('{}/{}'.format(endpoint_url, resource))

        if response.status_code == requests.codes.OK:
            rqs = rqs + 1
            t = time.time()
            if (t - last_time) >= 1:
                print("transaction rate: ", rqs - last_reqs, " req/sec")
                last_time = t
                last_reqs = rqs
            continue
            if re.match('^text/', response.headers['Content-Type']):
                logging.info("Got the resource {} type={}:\n{}".format(
                    resource, response.headers.get('Content-Type', '???'),
                    response.text))
            else:
                logging.info("Got the resource {} type={} (not echoed)".format(
                    resource, response.headers.get('Content-Type', '???')))
        else:
            logging.error("Error getting the resource. Code={} body={}".format(
                response.status_code, response.text))
    print(rqs, " requests processed, closing session.")
    session.close()
    return response
def test_eth_ticker(
        empty_proxy: PaywalledProxy,
        session: Session,
        sender_privkey: str,
        receiver_privkey: str,
        monkeypatch: MonkeyPatch
):
    def get_patched(*args, **kwargs):
        body = {
            'mid': '682.435', 'bid': '682.18', 'ask': '682.69', 'last_price': '683.16',
            'low': '532.97', 'high': '684.0', 'volume': '724266.25906224',
            'timestamp': '1513167820.721733'
        }
        return jsonify(body)

    monkeypatch.setattr(PaywalledProxyUrl, 'get', get_patched)

    ETHTickerProxy(receiver_privkey, proxy=empty_proxy)
    ticker = ETHTickerClient(sender_privkey, session=session, poll_interval=0.5)

    def post():
        ticker.close()

        assert ticker.pricevar.get() == '683.16 USD'
        assert len(session.client.get_open_channels()) == 0
        ticker.success = True

    session.close_channel_on_exit = True
    ticker.success = False
    ticker.root.after(1500, post)
    ticker.run()
    assert ticker.success
    def __init__(
            self,
            sender_privkey: str,
            session: Session = None,
            poll_interval: float = 5
    ) -> None:
        self.poll_interval = poll_interval

        self.root = tkinter.Tk()
        ttk.Frame.__init__(self, self.root)
        self.root.title('µRaiden ETH Ticker')
        self.root.protocol('WM_DELETE_WINDOW', self.close)
        self.pack()
        self.pricevar = tkinter.StringVar(value='0.00 USD')
        ttk.Label(self, textvariable=self.pricevar, font=('Helvetica', '72')).pack()

        if session is None:
            self.session = Session(
                private_key=sender_privkey,
                close_channel_on_exit=True,
                endpoint_url='http://localhost:5000'
            )
        else:
            self.session = session

        self.active_query = False
        self.running = False
Exemple #32
0
def patch_on_http_response(default_http_client: Session, abort_on=[]):
    def patched(self, method: str, url: str, response: Response, **kwargs):
        self.last_response = response
        return (response.status_code not in abort_on)

    default_http_client.on_http_response = types.MethodType(
        patched, default_http_client)
Exemple #33
0
def test_eth_ticker(
        empty_proxy: PaywalledProxy,
        session: Session,
        sender_privkey: str,
        receiver_privkey: str,
        monkeypatch: MonkeyPatch
):
    def get_patched(*args, **kwargs):
        body = {
            'mid': '682.435', 'bid': '682.18', 'ask': '682.69', 'last_price': '683.16',
            'low': '532.97', 'high': '684.0', 'volume': '724266.25906224',
            'timestamp': '1513167820.721733'
        }
        return jsonify(body)

    monkeypatch.setattr(PaywalledProxyUrl, 'get', get_patched)

    ETHTickerProxy(receiver_privkey, proxy=empty_proxy)
    ticker = ETHTickerClient(sender_privkey, session=session, poll_interval=0.5)

    def post():
        ticker.close()

        assert ticker.pricevar.get() == '683.16 USD'
        assert len(session.client.get_open_channels()) == 0
        ticker.success = True

    session.close_channel_on_exit = True
    ticker.success = False
    ticker.root.after(1500, post)
    ticker.run()
    assert ticker.success
Exemple #34
0
def run(
        private_key: str,
        password_path: str,
        resource: str,
        channel_manager_address: str = None,
        web3: Web3 = None,
        retry_interval: float = 5,
        endpoint_url: str = 'http://localhost:5000'
):
    # Create the client session.
    session = Session(
        endpoint_url=endpoint_url,
        private_key=private_key,
        key_password_path=password_path,
        channel_manager_address=channel_manager_address,
        web3=web3,
        retry_interval=retry_interval
    )
    # Get the resource. If payment is required, client will attempt to create
    # a channel or will use existing one.
    response = session.get('{}/{}'.format(endpoint_url, resource))

    if response.status_code == requests.codes.OK:
        if re.match('^text/', response.headers['Content-Type']):
            logging.info(
                "Got the resource {} type={}:\n{}".format(
                    resource,
                    response.headers.get('Content-Type', '???'),
                    response.text
                )
            )
        else:
            logging.info(
                "Got the resource {} type={} (not echoed)".format(
                    resource,
                    response.headers.get('Content-Type', '???')
                )
            )
    else:
        logging.error(
            "Error getting the resource. Code={} body={}".format(
                response.status_code,
                response.text
            )
        )
    return response
Exemple #35
0
def request(method: str, url: str, **kwargs) -> Response:
    session_kwargs = pop_function_kwargs(kwargs, Session.__init__)
    client_kwargs = pop_function_kwargs(kwargs, Client.__init__)

    session_kwargs.update(client_kwargs)

    with Session(**session_kwargs) as session:
        return session.request(method, url, **kwargs)
Exemple #36
0
def test_resource_request(doggo_proxy, http_doggo_url: str, session: Session):
    n_requests = 10
    session.initial_deposit = lambda x: (n_requests + 1) * x

    # First transfer creates channel on-chain => exclude from profiling.
    response = session.get(http_doggo_url)
    assert response.text == 'HI I AM A DOGGO'

    t_start = time.time()
    for i in range(n_requests):
        log.debug('Transfer {}'.format(i))
        response = session.get(http_doggo_url)
        assert response.text == 'HI I AM A DOGGO'
    t_diff = time.time() - t_start

    log.info("{} requests in {} ({} rps)".format(
        n_requests, datetime.timedelta(seconds=t_diff), n_requests / t_diff))
Exemple #37
0
def test_cache_cleanup(empty_proxy: PaywalledProxy, usession: uSession,
                       api_endpoint_address: str):
    now = time.time()
    es_mock = ElasticsearchBackend(None)

    api_path = 'http://' + api_endpoint_address
    resource_url = '/some_index/some_type/_search'
    url = api_path + resource_url

    bodies = [
        {
            'query': 'query something'
        },
        {
            'query': 'query some other thing'
        },
        {
            'query': 'query some third thing'
        },
    ]
    requests = [
        Request.from_values(method='GET',
                            base_url=api_path,
                            path=resource_url,
                            content_type='application/json',
                            data=json.dumps(bodies[0])),
        Request.from_values(method='GET',
                            base_url=api_path,
                            path=resource_url,
                            content_type='application/json',
                            data=json.dumps(bodies[1])),
        Request.from_values(method='GET',
                            base_url=api_path,
                            path=resource_url,
                            content_type='application/json',
                            data=json.dumps(bodies[2]))
    ]
    resources = [
        Resource(content='success1', price=4, expires_at=now - 100),
        Resource(content='success2', price=4, expires_at=now + 100),
        Resource(content='success3', price=4, expires_at=now - 100),
    ]
    es_mock.search = mock.Mock(return_value=resources[1])

    server = APIServer(empty_proxy, es=es_mock)
    server.resource_cache.update({
        ExpensiveElasticsearch.get_request_key(requests[0]):
        resources[0],
        ExpensiveElasticsearch.get_request_key(requests[1]):
        resources[1],
        ExpensiveElasticsearch.get_request_key(requests[2]):
        resources[2]
    })
    assert len(server.resource_cache) == 3

    response = usession.get(url, json=bodies[1])
    assert response.json() == 'success2'
    assert len(server.resource_cache) == 1
Exemple #38
0
def test_error_handling(session: Session, api_endpoint_address: str,
                        token_address: str, channel_manager_address: str,
                        receiver_address: str):
    nonexisting_channel_mock = mock.patch.object(
        session,
        'on_nonexisting_channel',
        wraps=session.on_nonexisting_channel).start()
    insufficient_confirmations_mock = mock.patch.object(
        session,
        'on_insufficient_confirmations',
        wraps=session.on_insufficient_confirmations).start()
    insufficient_funds_mock = mock.patch.object(
        session,
        'on_invalid_balance_proof',
        wraps=session.on_invalid_balance_proof).start()
    invalid_contract_address_mock = mock.patch.object(
        session,
        'on_invalid_contract_address',
        wraps=session.on_invalid_contract_address).start()

    with requests_mock.mock(real_http=True) as server_mock:
        headers = {
            HTTPHeaders.TOKEN_ADDRESS: token_address,
            HTTPHeaders.CONTRACT_ADDRESS: channel_manager_address,
            HTTPHeaders.RECEIVER_ADDRESS: receiver_address,
            HTTPHeaders.PRICE: '3'
        }
        headers = [headers.copy() for _ in range(5)]
        headers[1][HTTPHeaders.NONEXISTING_CHANNEL] = '1'
        headers[2][HTTPHeaders.INSUF_CONFS] = '1'
        headers[3][HTTPHeaders.INVALID_PROOF] = '1'
        headers[4][HTTPHeaders.CONTRACT_ADDRESS] = '0x' + '12' * 20

        url = 'http://{}/something'.format(api_endpoint_address)
        server_mock.get(url, [{
            'status_code': 402,
            'headers': headers[0]
        }, {
            'status_code': 402,
            'headers': headers[1]
        }, {
            'status_code': 402,
            'headers': headers[2]
        }, {
            'status_code': 402,
            'headers': headers[3]
        }, {
            'status_code': 402,
            'headers': headers[4]
        }])
        response = session.get(url)

    assert response.status_code == 402
    assert nonexisting_channel_mock.call_count == 1
    assert insufficient_confirmations_mock.call_count == 1
    assert insufficient_funds_mock.call_count == 1
    assert invalid_contract_address_mock.call_count == 1
Exemple #39
0
def session(client: Client, use_tester: bool, api_endpoint_address: str):
    # patch request_resource of this instance in order to advance blocks when doing requests
    def request_patched(self: Session, method: str, url: str, **kwargs):
        if use_tester:
            log.info('Mining new block.')
            self.client.context.web3.testing.mine(1)
        return Session._request_resource(self, method, url, **kwargs)

    kwargs = {}
    if use_tester:
        kwargs['retry_interval'] = 0.1

    session = Session(client,
                      endpoint_url='http://' + api_endpoint_address,
                      **kwargs)
    session._request_resource = types.MethodType(request_patched, session)
    yield session
    session.close()
def test_resource_request(doggo_proxy, http_doggo_url: str, session: Session):
    n_requests = 10
    session.initial_deposit = lambda x: (n_requests + 1) * x

    # First transfer creates channel on-chain => exclude from profiling.
    response = session.get(http_doggo_url)
    assert response.text == 'HI I AM A DOGGO'

    t_start = time.time()
    for i in range(n_requests):
        log.debug('Transfer {}'.format(i))
        response = session.get(http_doggo_url)
        assert response.text == 'HI I AM A DOGGO'
    t_diff = time.time() - t_start

    log.info("{} requests in {} ({} rps)".format(
        n_requests, datetime.timedelta(seconds=t_diff), n_requests / t_diff)
    )
def test_error_handling(
        session: Session,
        api_endpoint_address: str,
        token_address: str,
        channel_manager_address: str,
        receiver_address: str
):
    nonexisting_channel_mock = mock.patch.object(
        session,
        'on_nonexisting_channel',
        wraps=session.on_nonexisting_channel
    ).start()
    insufficient_confirmations_mock = mock.patch.object(
        session,
        'on_insufficient_confirmations',
        wraps=session.on_insufficient_confirmations
    ).start()
    insufficient_funds_mock = mock.patch.object(
        session,
        'on_invalid_balance_proof',
        wraps=session.on_invalid_balance_proof
    ).start()
    invalid_contract_address_mock = mock.patch.object(
        session,
        'on_invalid_contract_address',
        wraps=session.on_invalid_contract_address
    ).start()

    with requests_mock.mock(real_http=True) as server_mock:
        headers = {
            HTTPHeaders.TOKEN_ADDRESS: token_address,
            HTTPHeaders.CONTRACT_ADDRESS: channel_manager_address,
            HTTPHeaders.RECEIVER_ADDRESS: receiver_address,
            HTTPHeaders.PRICE: '3'
        }
        headers = [headers.copy() for _ in range(5)]
        headers[1][HTTPHeaders.NONEXISTING_CHANNEL] = '1'
        headers[2][HTTPHeaders.INSUF_CONFS] = '1'
        headers[3][HTTPHeaders.INVALID_PROOF] = '1'
        headers[4][HTTPHeaders.CONTRACT_ADDRESS] = '0x' + '12' * 20

        url = 'http://{}/something'.format(api_endpoint_address)
        server_mock.get(url, [
            {'status_code': 402, 'headers': headers[0]},
            {'status_code': 402, 'headers': headers[1]},
            {'status_code': 402, 'headers': headers[2]},
            {'status_code': 402, 'headers': headers[3]},
            {'status_code': 402, 'headers': headers[4]}
        ])
        response = session.get(url)

    assert response.status_code == 402
    assert nonexisting_channel_mock.call_count == 1
    assert insufficient_confirmations_mock.call_count == 1
    assert insufficient_funds_mock.call_count == 1
    assert invalid_contract_address_mock.call_count == 1
Exemple #42
0
def run(key_path, key_password_path, resource):
    # create the client
    with Client(key_path=key_path,
                key_password_path=key_password_path) as client:
        m2mclient = Session(client, 'localhost', 5000)

        # Get the resource. If payment is required, client will attempt to create
        # a channel or will use existing one.
        status, headers, body = m2mclient.run(resource)
        if status == requests.codes.OK:
            if re.match('^text\/', headers['Content-Type']):
                logging.info(
                    "got the resource %s type=%s\n%s" %
                    (resource, headers.get('Content-Type', '???'), body))
            else:
                logging.info("got the resource %s type=%s" %
                             (resource, headers.get('Content-Type', '???')))
        else:
            logging.error("error getting the resource. code=%d body=%s" %
                          (status, body.decode().strip()))
 def patched_payment(
         self: Session,
         method: str,
         url: str,
         response: Response,
         **kwargs
 ):
     response.headers[HTTPHeaders.PRICE] = str(
         int(response.headers[HTTPHeaders.PRICE]) + self.price_adjust
     )
     return Session.on_payment_requested(self, method, url, response, **kwargs)
Exemple #44
0
def session(client: Client, use_tester: bool, api_endpoint_address: str):
    # patch request_resource of this instance in order to advance blocks when doing requests
    def request_patched(self: Session, method: str, url: str, **kwargs):
        if use_tester:
            log.info('Mining new block.')
            self.client.context.web3.testing.mine(1)
        return Session._request_resource(self, method, url, **kwargs)

    kwargs = {}
    if use_tester:
        kwargs['retry_interval'] = 0.1

    session = Session(
        client,
        endpoint_url='http://' + api_endpoint_address,
        **kwargs
    )
    session._request_resource = types.MethodType(request_patched, session)
    yield session
    session.close()
Exemple #45
0
 def patched_payment(
         self: Session,
         method: str,
         url: str,
         response: Response,
         **kwargs
 ):
     response.headers[HTTPHeaders.PRICE] = str(
         int(response.headers[HTTPHeaders.PRICE]) + self.price_adjust
     )
     return Session.on_payment_requested(self, method, url, response, **kwargs)
Exemple #46
0
    def on_payment_requested(self, method: str, url: str, response: Response,
                             **kwargs) -> bool:
        price = int(response.headers[HTTPHeaders.PRICE])
        if price > self.max_rei_per_request:
            log.error(
                'Requested price exceeds configured maximum price per request ({} > {}). '
                'Aborting'.format(price, self.max_rei_per_request))
            return False

        return uSession.on_payment_requested(self, method, url, response,
                                             **kwargs)
Exemple #47
0
def test_cooperative_close_denied(session: Session, api_endpoint_address: str,
                                  token_address: str,
                                  channel_manager_address: str,
                                  receiver_address: str):
    cooperative_close_denied_mock = mock.patch.object(
        session,
        'on_cooperative_close_denied',
        wraps=session.on_cooperative_close_denied).start()

    with requests_mock.mock() as server_mock:
        headers = {
            HTTPHeaders.TOKEN_ADDRESS: token_address,
            HTTPHeaders.CONTRACT_ADDRESS: channel_manager_address,
            HTTPHeaders.RECEIVER_ADDRESS: receiver_address,
            HTTPHeaders.PRICE: '3'
        }
        headers = [headers.copy() for _ in range(2)]
        headers[1][HTTPHeaders.COST] = '3'

        url = 'http://{}/something'.format(api_endpoint_address)
        server_mock.get(url, [
            {
                'status_code': 402,
                'headers': headers[0]
            },
            {
                'status_code': 200,
                'headers': headers[1],
                'text': 'success'
            },
        ])
        channel_url = re.compile(
            'http://{}/api/1/channels/0x.{{40}}/\d+'.format(
                api_endpoint_address))
        server_mock.delete(channel_url, [{'status_code': 403}])
        response = session.get(url)
        session.close_channel()

    assert response.text == 'success'
    assert cooperative_close_denied_mock.call_count == 1
    assert session.client.channels[0].state == Channel.State.settling
def test_cooperative_close_denied(
        session: Session,
        api_endpoint_address: str,
        token_address: str,
        channel_manager_address: str,
        receiver_address: str
):
    cooperative_close_denied_mock = mock.patch.object(
        session,
        'on_cooperative_close_denied',
        wraps=session.on_cooperative_close_denied
    ).start()

    with requests_mock.mock(real_http=True) as server_mock:
        headers = {
            HTTPHeaders.TOKEN_ADDRESS: token_address,
            HTTPHeaders.CONTRACT_ADDRESS: channel_manager_address,
            HTTPHeaders.RECEIVER_ADDRESS: receiver_address,
            HTTPHeaders.PRICE: '3'
        }
        headers = [headers.copy() for _ in range(2)]
        headers[1][HTTPHeaders.COST] = '3'

        url = 'http://{}/something'.format(api_endpoint_address)
        server_mock.get(url, [
            {'status_code': 402, 'headers': headers[0]},
            {'status_code': 200, 'headers': headers[1], 'text': 'success'},
        ])
        channel_url = re.compile(
            'http://{}/api/1/channels/0x.{{40}}/\d+'.format(api_endpoint_address)
        )
        server_mock.delete(channel_url, [
            {'status_code': 403}
        ])
        response = session.get(url)
        session.close_channel()

    assert response.text == 'success'
    assert cooperative_close_denied_mock.call_count == 1
    assert session.channel.state == Channel.State.settling
def test_cheating_client(
        doggo_proxy,
        web3,
        session: Session,
        wait_for_transaction,
        http_doggo_url: str,
        faucet_private_key: str,
        faucet_address: str,
        receiver_privkey: str,
        receiver_address: str
):
    balance = web3.eth.getBalance(doggo_proxy.channel_manager.receiver)
    assert balance > 0
    # remove all receiver's eth
    tx = create_signed_transaction(
        receiver_privkey,
        web3,
        faucet_address,
        balance - NETWORK_CFG.GAS_PRICE * NETWORK_CFG.POT_GAS_LIMIT
    )
    tx_hash = web3.eth.sendRawTransaction(tx)
    wait_for_transaction(tx_hash)
    response = session.get(http_doggo_url)
    # proxy is expected to return 502 - it has no funds
    assert response.status_code == 502
    tx = create_signed_transaction(
        faucet_private_key,
        web3,
        receiver_address,
        balance - NETWORK_CFG.GAS_PRICE * NETWORK_CFG.POT_GAS_LIMIT
    )
    tx_hash = web3.eth.sendRawTransaction(tx)
    wait_for_transaction(tx_hash)
    response = session.get(http_doggo_url)
    # now it should proceed normally
    assert response.status_code == 200
def test_session(
        doggo_proxy,
        session: Session,
        sender_address: str,
        receiver_address: str,
        http_doggo_url: str
):
    check_response(session.get(http_doggo_url))

    client = session.client
    open_channels = client.get_open_channels()
    assert len(open_channels) == 1

    channel = open_channels[0]
    assert channel == session.channel
    assert channel.balance_sig
    assert channel.balance < channel.deposit
    assert is_same_address(channel.sender, sender_address)
    assert is_same_address(channel.receiver, receiver_address)
def test_coop_close(
        doggo_proxy,
        session: Session,
        http_doggo_url: str
):
    check_response(session.get(http_doggo_url))

    client = session.client
    open_channels = client.get_open_channels()
    assert len(open_channels) == 1

    channel = open_channels[0]
    import requests
    reply = requests.get('http://localhost:5000/api/1/channels/%s/%s' %
                         (channel.sender, channel.block))
    assert reply.status_code == 200
    json_reply = json.loads(reply.text)

    request_data = {'balance': json_reply['balance']}
    reply = requests.delete('http://localhost:5000/api/1/channels/%s/%s' %
                            (channel.sender, channel.block), data=request_data)

    assert reply.status_code == 200
Exemple #52
0
 def request_patched(self: Session, method: str, url: str, **kwargs):
     if use_tester:
         log.info('Mining new block.')
         self.client.context.web3.testing.mine(1)
     return Session._request_resource(self, method, url, **kwargs)
def test_full_cycle_adapt_balance(
        session: Session,
        api_endpoint_address: str,
        token_address: str,
        channel_manager_address: str,
        receiver_address: str
):
    # Simulate a lost balance signature.
    client = session.client
    channel = client.get_suitable_channel(receiver_address, 10, initial_deposit=lambda x: 2 * x)
    channel.create_transfer(3)
    lost_balance_sig = channel.balance_sig
    channel.update_balance(0)

    with requests_mock.mock(real_http=True) as server_mock:
        headers1 = Munch()
        headers1.token_address = token_address
        headers1.contract_address = channel_manager_address
        headers1.receiver_address = receiver_address
        headers1.price = '7'

        headers2 = headers1.copy()
        headers2.invalid_amount = '1'
        headers2.sender_balance = '3'
        headers2.balance_signature = encode_hex(lost_balance_sig)

        headers3 = Munch()
        headers3.cost = '7'

        headers1 = HTTPHeaders.serialize(headers1)
        headers2 = HTTPHeaders.serialize(headers2)
        headers3 = HTTPHeaders.serialize(headers3)

        url = 'http://{}/something'.format(api_endpoint_address)
        server_mock.get(url, [
            {'status_code': 402, 'headers': headers1},
            {'status_code': 402, 'headers': headers2},
            {'status_code': 200, 'headers': headers3, 'text': 'success'}
        ])

        response = session.get(url)

    # Filter out any requests made to the ethereum node.
    request_history = [request for request in server_mock.request_history if request.port == 5000]

    # First cycle, request price.
    request = request_history[0]
    assert request.path == '/something'
    assert request.method == 'GET'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address

    # Second cycle, pay price based on outdated balance.
    request = request_history[1]
    assert request.path == '/something'
    assert request.method == 'GET'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address
    assert request.headers['RDN-Balance'] == '7'
    assert request.headers['RDN-Balance-Signature']

    # Third cycle, adapt new balance and pay price again.
    request = request_history[2]
    assert request.path == '/something'
    assert request.method == 'GET'
    assert request.headers['RDN-Contract-Address'] == channel_manager_address
    assert request.headers['RDN-Balance'] == '10'

    assert session.channel.balance == 10
    balance_sig_hex = encode_hex(session.channel.balance_sig)
    assert request.headers['RDN-Balance-Signature'] == balance_sig_hex
    assert session.channel.balance_sig
    assert response.text == 'success'