def __init__( self, sender_privkey: str, httpclient: DefaultHTTPClient = None ): 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 httpclient: self.httpclient = httpclient self.client = httpclient.client else: self.client = Client(sender_privkey) self.httpclient = DefaultHTTPClient( self.client, 'localhost', 5000, initial_deposit=lambda x: 10 * x, topup_deposit=lambda x: 5 * x ) self.active_query = False self.running = False
def client(sender_privkey: str, client_contract_proxy: ChannelContractProxy, client_token_proxy: ContractProxy, datadir: str): client = Client(privkey=sender_privkey, channel_manager_proxy=client_contract_proxy, token_proxy=client_token_proxy, datadir=datadir) yield client client.close()
def close_all_channels_cooperatively( client: Client, privkey_receiver: str, contract_address: str, balance: int=None ): receiver_addr = privkey_to_addr(privkey_receiver) client.sync_channels() channels = [c for c in client.channels if c.state != Channel.State.closed and c.receiver == receiver_addr] for channel in channels: close_channel_cooperatively(channel, privkey_receiver, contract_address, balance)
def client(sender_privkey, client_contract_proxy, client_token_proxy, datadir, channel_manager_contract_address, token_contract_address): client = Client(privkey=sender_privkey, channel_manager_proxy=client_contract_proxy, token_proxy=client_token_proxy, datadir=datadir, channel_manager_address=channel_manager_contract_address, token_address=token_contract_address) yield client client.close()
def test_sync(client: Client, receiver_address, receiver_privkey): c = client.get_suitable_channel(receiver_address, 5, initial_deposit=lambda x: x) assert c in client.channels assert c.deposit == 5 assert len(client.channels) == 1 # Check if channel is still valid after sync. client.sync_channels() assert c in client.channels assert len(client.channels) == 1 # Check if client handles topup events on sync. c_topup = client.get_suitable_channel(receiver_address, 7, topup_deposit=lambda x: 2) assert c_topup == c assert len(client.channels) == 1 assert c.deposit == 7 # Check if channel can be resynced after data loss. client.channels = [] client.sync_channels() assert len(client.channels) == 1 c = client.channels[0] assert c.deposit == 7 # Check if channel is forgotten on resync after closure. close_channel_cooperatively(c, receiver_privkey, client.context.channel_manager.address) client.sync_channels() assert c not in client.channels
def test_sync(client: Client, receiver_address, receiver_privkey): c = client.get_suitable_channel(receiver_address, 5, initial_deposit=lambda x: x) assert c is not None assert c in client.channels assert c.deposit == 5 assert len(client.channels) == 1 # Check if channel is still valid after sync. client.sync_channels() assert c in client.channels assert len(client.channels) == 1 # Check if client handles topup events on sync. c_topup = client.get_suitable_channel(receiver_address, 7, topup_deposit=lambda x: 2) assert c is not None assert c_topup == c assert len(client.channels) == 1 assert c.deposit == 7 # Check if channel can be resynced after data loss. client.channels = [] client.sync_channels() assert len(client.channels) == 1 c = client.channels[0] assert c.deposit == 7 # Check if channel is forgotten on resync after closure. close_channel_cooperatively(c, receiver_privkey, client.context.channel_manager.address) client.sync_channels() assert c not in client.channels
def test_reorg( web3: Web3, channel_manager: ChannelManager, client: Client, receiver_address: str, wait_for_blocks, use_tester: bool ): if not use_tester: pytest.skip('Chain reorg tests only work in tester chain') wait_for_blocks(10) # create unconfirmed channel channel_manager.wait_sync() snapshot_id = web3.testing.snapshot() channel = client.open_channel(receiver_address, 10) wait_for_blocks(0) assert (channel.sender, channel.block) in channel_manager.unconfirmed_channels # remove unconfirmed channel opening with reorg web3.testing.revert(snapshot_id) snapshot_id = web3.testing.snapshot() wait_for_blocks(0) assert (channel.sender, channel.block) not in channel_manager.unconfirmed_channels web3.testing.mine(channel_manager.n_confirmations) assert (channel.sender, channel.block) not in channel_manager.channels # leave confirmed channel opening web3.testing.revert(snapshot_id) channel = client.open_channel(receiver_address, 10) wait_for_blocks(channel_manager.n_confirmations) assert (channel.sender, channel.block) in channel_manager.channels confirmed_snapshot_id = web3.testing.snapshot() wait_for_blocks(3) web3.testing.revert(confirmed_snapshot_id) assert (channel.sender, channel.block) in channel_manager.channels # remove unconfirmed topup channel = client.open_channel(receiver_address, 10) wait_for_blocks(channel_manager.n_confirmations) assert (channel.sender, channel.block) in channel_manager.channels topup_snapshot_id = web3.testing.snapshot() channel.topup(5) wait_for_blocks(0) channel_rec = channel_manager.channels[channel.sender, channel.block] assert len(channel_rec.unconfirmed_topups) == 1 web3.testing.revert(topup_snapshot_id) wait_for_blocks(0) assert (channel.sender, channel.block) in channel_manager.channels channel_rec = channel_manager.channels[channel.sender, channel.block] assert len(channel_rec.unconfirmed_topups) == 0
def close_all_channels_cooperatively( client: Client, private_keys: List[str], contract_address: str, balance: int=None ): addresses_to_keys = { to_checksum_address(privkey_to_addr(private_key)): private_key for private_key in private_keys } client.sync_channels() closable_channels = [c for c in client.channels if c.state != Channel.State.closed] log.info('Closing {} channels.'.format(len(closable_channels))) for channel in closable_channels: private_key = addresses_to_keys.get(to_checksum_address(channel.receiver)) if private_key is not None: close_channel_cooperatively(channel, private_key, contract_address, balance)
def test_challenge( channel_manager: ChannelManager, confirmed_open_channel: Channel, receiver_address: str, sender_address: str, wait_for_blocks, web3: Web3, client: Client ): blockchain = channel_manager.blockchain channel_id = (confirmed_open_channel.sender, confirmed_open_channel.block) sig = encode_hex(confirmed_open_channel.create_transfer(5)) channel_manager.register_payment(sender_address, confirmed_open_channel.block, 5, sig) # hack channel to decrease balance confirmed_open_channel.update_balance(0) sig = confirmed_open_channel.create_transfer(3) block_before = web3.eth.blockNumber confirmed_open_channel.close() # should challenge and immediately settle for waited_blocks in count(): logs = get_logs(client.context.token, 'Transfer', from_block=block_before - 1) if logs: break wait_for_blocks(1) assert waited_blocks < 10 assert len([l for l in logs if is_same_address(l['args']['_to'], receiver_address) and l['args']['_value'] == 5]) == 1 assert len([l for l in logs if is_same_address(l['args']['_to'], sender_address) and l['args']['_value'] == 5]) == 1 wait_for_blocks(blockchain.n_confirmations) gevent.sleep(blockchain.poll_interval) assert channel_id not in channel_manager.channels # update channel state so that it will not be closed twice client.sync_channels() new_state = None for channel in client.channels: if all(channel.sender == confirmed_open_channel.sender, channel.receiver == confirmed_open_channel.receiver, channel.block == confirmed_open_channel.block): new_state = channel.state if new_state is None: confirmed_open_channel.state = confirmed_open_channel.State.closed else: confirmed_open_channel.state = new_state
def init(privkey: str = None, key_path: str = None, key_password_path: str = None, channel_manager_address: str = CHANNEL_MANAGER_ADDRESS, web3: Web3 = None, channel_manager_proxy: ChannelContractProxy = None, token_proxy: ContractProxy = None, contract_metadata: dict = CONTRACT_METADATA, retry_interval: float = 5, initial_deposit: Callable[[int], int] = lambda price: 10 * price, topup_deposit: Callable[[int], int] = lambda price: 5 * price): global client global http_client client = Client(privkey=privkey, key_path=key_path, key_password_path=key_password_path, channel_manager_address=channel_manager_address, web3=web3, channel_manager_proxy=channel_manager_proxy, token_proxy=token_proxy, contract_metadata=contract_metadata) http_client = DefaultHTTPClient(client=client, retry_interval=retry_interval, initial_deposit=initial_deposit, topup_deposit=topup_deposit)
def test_static_price(empty_proxy: PaywalledProxy, api_endpoint_address: str, client: Client, wait_for_blocks, receiver_privkey: str): proxy = empty_proxy endpoint_url = "http://" + api_endpoint_address content = PaywalledContent( 'resource', price=3, get_fn=lambda url: ('SUCCESS', 200), ) proxy.add_content(content) response = requests.get(endpoint_url + '/resource') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 3 channel = client.get_suitable_channel(headers.receiver_address, 3) wait_for_blocks(6) channel.balance = 3 headers = Munch() headers.balance = str(channel.balance) headers.balance_signature = encode_hex(channel.balance_sig) headers.sender_address = channel.sender headers.open_block = str(channel.block) headers = HTTPHeaders.serialize(headers) response = requests.get(endpoint_url + '/resource', headers=headers) assert response.status_code == 200 assert response.text.strip() == '"SUCCESS"' close_channel_cooperatively(channel, receiver_privkey, client.channel_manager_address, 0)
def test_dynamic_price(empty_proxy: PaywalledProxy, api_endpoint_address: str, client: Client, wait_for_blocks, receiver_privkey: str): proxy = empty_proxy endpoint_url = "http://" + api_endpoint_address price_cycle = cycle([1, 2, 3, 4, 5]) url_to_price = {} def price_fn(url: str): if url in url_to_price: price = url_to_price[url] else: price = next(price_cycle) url_to_price[url] = price return price content = PaywalledContent( 'resource_\d', price=price_fn, get_fn=lambda url: (url.split("_")[1], 200), ) proxy.add_content(content) response = requests.get(endpoint_url + '/resource_3') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 1 response = requests.get(endpoint_url + '/resource_5') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 2 response = requests.get(endpoint_url + '/resource_2') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 3 response = requests.get(endpoint_url + '/resource_3') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 1 channel = client.get_suitable_channel(headers.receiver_address, 2) wait_for_blocks(6) channel.balance = 2 headers = Munch() headers.balance = str(channel.balance) headers.balance_signature = encode_hex(channel.balance_sig) headers.sender_address = channel.sender headers.open_block = str(channel.block) headers = HTTPHeaders.serialize(headers) response = requests.get(endpoint_url + '/resource_5', headers=headers) assert response.status_code == 200 assert response.text.strip() == '"5"' close_channel_cooperatively(channel, receiver_privkey, client.channel_manager_address, 0)
def test_integrity(client: Client, receiver_address): c = client.get_suitable_channel(receiver_address, 5) assert c is not None assert c.balance == 0 assert c.balance_sig == sign_balance_proof( client.context.private_key, receiver_address, c.block, 0, client.context.channel_manager.address ) assert c.is_valid() # Balance update without sig update. c._balance = 2 assert not c.is_valid() # Proper balance update with sig update. c.update_balance(2) assert c.is_valid() # Random sig. c._balance_sig = b'wrong' assert not c.is_valid() # Balance exceeds deposit. c.update_balance(100) assert not c.is_valid() c.update_balance(0)
def test_static_price(empty_proxy: PaywalledProxy, api_endpoint_address: str, client: Client, wait_for_blocks): proxy = empty_proxy endpoint_url = "http://" + api_endpoint_address proxy.add_paywalled_resource(StaticPriceResource, '/resource', 3) # test GET response = requests.get(endpoint_url + '/resource') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 3 channel = client.get_suitable_channel(headers.receiver_address, int(headers.price) * 4) wait_for_blocks(6) channel.update_balance(int(headers.price)) headers = Munch() headers.balance = str(channel.balance) headers.balance_signature = encode_hex(channel.balance_sig) headers.sender_address = channel.sender headers.open_block = str(channel.block) headers = HTTPHeaders.serialize(headers) response = requests.get(endpoint_url + '/resource', headers=headers) assert response.status_code == 200 assert response.text.strip() == 'GET' assert_method(requests.post, endpoint_url + '/resource', headers, channel, 'POST') assert_method(requests.put, endpoint_url + '/resource', headers, channel, 'PUT') assert_method(requests.delete, endpoint_url + '/resource', headers, channel, 'DEL')
def test_withdraw_above_minimum( client: Client, channel_manager: ChannelManager, web3: Web3, wait_for_blocks ): sender = client.context.address receiver = channel_manager.receiver channel = client.open_channel(receiver, 10) wait_for_blocks(channel_manager.n_confirmations + 1) channel_manager.register_payment(sender, channel.block, 4, encode_hex(channel.create_transfer(4))) channel_manager.stop() # don't update state from this point on channel_manager.join() state = channel_manager.state tx_count_before = web3.eth.getTransactionCount(receiver) withdraw_from_channels( channel_manager.private_key, state, channel_manager.channel_manager_contract, 3, wait=lambda: wait_for_blocks(1) ) tx_count_after = web3.eth.getTransactionCount(receiver) assert tx_count_after == tx_count_before + 1 channel_id = (channel.sender, channel.receiver, channel.block) channel_info = channel_manager.channel_manager_contract.call().getChannelInfo(*channel_id) _, deposit, settle_block_number, closing_balance, transferred_tokens = channel_info assert transferred_tokens == 4 wait_for_blocks(1)
def test_explicit_json(empty_proxy: PaywalledProxy, api_endpoint_address: str, client: Client, wait_for_blocks): proxy = empty_proxy endpoint_url = "http://" + api_endpoint_address proxy.add_paywalled_resource(JSONResource, '/resource', 3) # test GET response = requests.get(endpoint_url + '/resource') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 3 channel = client.get_suitable_channel(headers.receiver_address, int(headers.price) * 4) wait_for_blocks(6) channel.update_balance(int(headers.price)) headers = Munch() headers.balance = str(channel.balance) headers.balance_signature = encode_hex(channel.balance_sig) headers.sender_address = channel.sender headers.open_block = str(channel.block) headers = HTTPHeaders.serialize(headers) response = requests.get(endpoint_url + '/resource', headers=headers) assert response.status_code == 200 # If headers don't merge properly, this results in 'application/json,application/json'. assert response.headers['Content-Type'] == 'application/json' assert response.json() == {'GET': 1}
def test_close_settled(client: Client, channel_manager: ChannelManager, web3: Web3, wait_for_blocks): sender = client.context.address receiver = channel_manager.receiver channel = client.open_channel(receiver, 10) wait_for_blocks(channel_manager.n_confirmations + 1) channel_manager.register_payment(sender, channel.block, 2, encode_hex(channel.create_transfer(2))) receiver_sig = channel_manager.sign_close(sender, channel.block, 2) channel.close_cooperatively(receiver_sig) wait_for_blocks(channel_manager.n_confirmations + 1) channel_manager.stop() # don't update state from this point on channel_manager.join() state = channel_manager.state tx_count_before = web3.eth.getTransactionCount(receiver) close_open_channels(channel_manager.private_key, state, channel_manager.channel_manager_contract, wait=lambda: wait_for_blocks(1)) tx_count_after = web3.eth.getTransactionCount(receiver) assert tx_count_after == tx_count_before wait_for_blocks(1)
def test_channel_opening(client: Client, web3: Web3, make_account, private_keys: List[str], channel_manager_contract, token_contract, mine_sync_event, wait_for_blocks, use_tester, state_db_path): receiver1_privkey = make_account(RECEIVER_ETH_ALLOWANCE, RECEIVER_TOKEN_ALLOWANCE, private_keys[2]) receiver2_privkey = make_account(RECEIVER_ETH_ALLOWANCE, RECEIVER_TOKEN_ALLOWANCE, private_keys[3]) receiver_address = privkey_to_addr(receiver1_privkey) # make sure channel_manager1 is terminated properly, otherwise Blockchain will be running # in the background, ruining other tests' results channel_manager1 = ChannelManager(web3, channel_manager_contract, token_contract, receiver1_privkey, n_confirmations=5, state_filename=state_db_path) start_channel_manager(channel_manager1, use_tester, mine_sync_event) channel_manager2 = ChannelManager(web3, channel_manager_contract, token_contract, receiver2_privkey, n_confirmations=5, state_filename=state_db_path) start_channel_manager(channel_manager2, use_tester, mine_sync_event) channel_manager1.wait_sync() channel_manager2.wait_sync() blockchain = channel_manager1.blockchain channel = client.open_channel(receiver_address, 10) # should be in unconfirmed channels wait_for_blocks(1) gevent.sleep(blockchain.poll_interval) assert (channel.sender, channel.block) not in channel_manager1.channels assert (channel.sender, channel.block) in channel_manager1.unconfirmed_channels channel_rec = channel_manager1.unconfirmed_channels[channel.sender, channel.block] assert is_same_address(channel_rec.receiver, receiver_address) assert is_same_address(channel_rec.sender, channel.sender) assert channel_rec.mtime == channel_rec.ctime # should be confirmed after n blocks wait_for_blocks(blockchain.n_confirmations) gevent.sleep(blockchain.poll_interval) assert (channel.sender, channel.block) in channel_manager1.channels channel_rec = channel_manager1.channels[channel.sender, channel.block] assert is_same_address(channel_rec.receiver, receiver_address) assert is_same_address(channel_rec.sender, channel.sender) assert channel_rec.balance == 0 assert channel_rec.last_signature is None assert channel_rec.is_closed is False assert channel_rec.settle_timeout == -1 # should not appear in other channel manager assert (channel.sender, channel.block) not in channel_manager2.channels assert (channel.sender, channel.block) not in channel_manager2.unconfirmed_channels channel_manager1.stop() channel_manager2.stop()
def test_close_valid_close(client: Client, channel_manager: ChannelManager, web3: Web3, wait_for_blocks): sender = client.context.address receiver = channel_manager.receiver channel = client.open_channel(receiver, 10) wait_for_blocks(channel_manager.n_confirmations + 1) channel_manager.register_payment(sender, channel.block, 2, encode_hex(channel.create_transfer(2))) channel.close() channel_manager.stop() # don't update state from this point on channel_manager.join() state = channel_manager.state tx_count_before = web3.eth.getTransactionCount(receiver) close_open_channels(channel_manager.private_key, state, channel_manager.channel_manager_contract, wait=lambda: wait_for_blocks(1)) tx_count_after = web3.eth.getTransactionCount(receiver) assert tx_count_after == tx_count_before + 1 with pytest.raises((BadFunctionCallOutput, TransactionFailed)): channel_id = (channel.sender, channel.receiver, channel.block) channel_manager.channel_manager_contract.call().getChannelInfo( *channel_id) wait_for_blocks(1)
def test_open_channel_insufficient_tokens(client: Client, web3: Web3, receiver_address: str): balance_of = client.context.token.call().balanceOf(client.context.address) tx_count_pre = web3.eth.getTransactionCount(client.context.address) channel = client.open_channel(receiver_address, balance_of + 1) tx_count_post = web3.eth.getTransactionCount(client.context.address) assert channel is None assert tx_count_post == tx_count_pre
def test_static_price( empty_proxy: PaywalledProxy, api_endpoint_address: str, client: Client, wait_for_blocks ): proxy = empty_proxy endpoint_url = "http://" + api_endpoint_address proxy.add_paywalled_resource(StaticPriceResource, '/resource', 3) # test GET response = requests.get(endpoint_url + '/resource') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 3 channel = client.get_suitable_channel(headers.receiver_address, int(headers.price) * 4) wait_for_blocks(6) channel.update_balance(int(headers.price)) headers = Munch() headers.balance = str(channel.balance) headers.balance_signature = encode_hex(channel.balance_sig) headers.sender_address = channel.sender headers.open_block = str(channel.block) headers = HTTPHeaders.serialize(headers) response = requests.get(endpoint_url + '/resource', headers=headers) assert response.status_code == 200 assert response.text.strip() == 'GET' assert_method(requests.post, endpoint_url + '/resource', headers, channel, 'POST') assert_method(requests.put, endpoint_url + '/resource', headers, channel, 'PUT') assert_method(requests.delete, endpoint_url + '/resource', headers, channel, 'DEL')
def test_explicit_json( empty_proxy: PaywalledProxy, api_endpoint_address: str, client: Client, wait_for_blocks ): proxy = empty_proxy endpoint_url = "http://" + api_endpoint_address proxy.add_paywalled_resource(JSONResource, '/resource', 3) # test GET response = requests.get(endpoint_url + '/resource') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 3 channel = client.get_suitable_channel(headers.receiver_address, int(headers.price) * 4) wait_for_blocks(6) channel.update_balance(int(headers.price)) headers = Munch() headers.balance = str(channel.balance) headers.balance_signature = encode_hex(channel.balance_sig) headers.sender_address = channel.sender headers.open_block = str(channel.block) headers = HTTPHeaders.serialize(headers) response = requests.get(endpoint_url + '/resource', headers=headers) assert response.status_code == 200 # If headers don't merge properly, this results in 'application/json,application/json'. assert response.headers['Content-Type'] == 'application/json' assert response.json() == {'GET': 1}
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 = DefaultHTTPClient( 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 init(http_client: HTTPClient = None, client: Client = None, privkey: str = None, key_path: str = None, key_password_path: str = None, channel_manager_address: str = CHANNEL_MANAGER_ADDRESS, web3: Web3 = None, retry_interval: float = 5, initial_deposit: Callable[[int], int] = lambda price: 10 * price, topup_deposit: Callable[[int], int] = lambda price: 5 * price): """ TODO: document which of these arguments are actually needed in different use cases. """ global _http_client if http_client is None: if client is None: client = Client(private_key=privkey, key_path=key_path, key_password_path=key_password_path, channel_manager_address=channel_manager_address, web3=web3) _http_client = DefaultHTTPClient(client=client, retry_interval=retry_interval, initial_deposit=initial_deposit, topup_deposit=topup_deposit) else: _http_client = http_client
def openChannel(): global channel_object with Client(config.privkey) as client: channel_object = client.get_suitable_channel( config.receiver, int(utils.convert_to_utf(request.get_data()))) if not channel_object: return json.dumps(utils.error) return json.dumps(utils.ok)
def confirmed_open_channel(channel_manager: ChannelManager, client: Client, receiver_address: str, wait_for_blocks): channel = client.open_channel(receiver_address, 10) wait_for_blocks(channel_manager.n_confirmations + 1) gevent.sleep(channel_manager.blockchain.poll_interval) assert (channel.sender, channel.block) in channel_manager.channels return channel
def test_get_expired(empty_proxy: PaywalledProxy, api_endpoint_address: str, sender_address: str, receiver_address: str, client: Client, wait_for_blocks): """ Test with manual client interaction for isolated server testing. Note: Debug in microraiden.proxy.resources.expensive for most of the server response logic. """ es_mock = ElasticsearchBackend(None) es_mock.search = mock.Mock(return_value=Resource( price=5, content='success', expires_at=time.time() + 0.05)) APIServer(empty_proxy, es=es_mock) api_path = 'http://' + api_endpoint_address # Request price (and cache resource). body = {'query': 'query something'} url = api_path + '/some_index/some_type/_search' response = requests.get(url, json=body) assert response.status_code == 402 # Perform payment and request same resource again. Include payment information in headers. channel = client.get_suitable_channel(receiver_address, 5) # Server requires some confirmations, so mine some blocks. wait_for_blocks(10) channel.create_transfer(5) headers = Munch() headers.balance = str(channel.balance) headers.balance_signature = encode_hex(channel.balance_sig) headers.sender_address = sender_address headers.receiver_address = receiver_address headers.open_block = str(channel.block) headers = HTTPHeaders.serialize(headers) # Make sure the price changes for the next request. es_mock.search = mock.Mock(return_value=Resource( price=3, content='success', expires_at=time.time() + 0.05)) # Let former query expire. gevent.sleep(0.1) # Query same resource again with the old expired price. response = requests.get(url, headers=headers, json=body) assert response.status_code == 402 assert response.headers[HTTPHeaders.PRICE] == '3' channel.update_balance(3) headers[HTTPHeaders.BALANCE] = str(channel.balance) headers[HTTPHeaders.BALANCE_SIGNATURE] = encode_hex(channel.balance_sig) response = requests.get(url, headers=headers, json=body) assert response.json() == 'success' es_mock.search.call_args_list == [{ 'query': 'query something' }, { 'query': 'query something' }]
def test_topup_channel_insufficient_tokens(client: Client, web3: Web3, receiver_address: str): balance_of = client.token_proxy.contract.call().balanceOf(client.account) channel = client.open_channel(receiver_address, 1) tx_count_pre = web3.eth.getTransactionCount(client.account) assert channel.topup(balance_of) is None tx_count_post = web3.eth.getTransactionCount(client.account) assert tx_count_post == tx_count_pre
def test_get(empty_proxy: PaywalledProxy, api_endpoint_address: str, channel_manager_address: str, token_address: str, sender_address: str, receiver_address: str, client: Client, wait_for_blocks): """ Test with manual client interaction for isolated server testing. Note: Debug in microraiden.proxy.resources.expensive for most of the server response logic. """ es_mock = ElasticsearchBackend(None) es_mock.search = mock.Mock(return_value=Resource( price=5, content='success', expires_at=time.time() + 30)) server = APIServer(empty_proxy, es=es_mock) api_path = 'http://' + api_endpoint_address # Request price (and cache resource). body = {'query': 'query something'} url = api_path + '/some_index/some_type/_search' response = requests.get(url, json=body) print(response.content) assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert headers.token_address == token_address assert headers.contract_address == channel_manager_address assert headers.receiver_address == receiver_address price = int(headers.price) assert price == 5 assert len(server.resource_cache) == 1 # Perform payment and request same resource again. Include payment information in headers. channel = client.get_suitable_channel(receiver_address, price) # Server requires some confirmations, so mine some blocks. wait_for_blocks(10) channel.create_transfer(price) headers = Munch() headers.balance = str(channel.balance) headers.balance_signature = encode_hex(channel.balance_sig) headers.sender_address = sender_address headers.receiver_address = receiver_address headers.open_block = str(channel.block) headers = HTTPHeaders.serialize(headers) response = requests.get(url, headers=headers, json=body) headers = HTTPHeaders.deserialize(response.headers) assert 'nonexisting_channel' not in headers, 'Server should acknowledge the created channel.' assert 'insuf_confs' not in headers, 'Server should acknowledge the created channel.' assert 'invalid_amount' not in headers, 'The correct amount should have been sent.' assert response.status_code == 200 assert response.json() == 'success' assert len(server.resource_cache) == 1 es_mock.search.assert_called_once_with(index='some_index', doc_type='some_type', body={'query': 'query something'})
def test_cooperative_close(client: Client, receiver_privkey, receiver_address): c = client.get_suitable_channel(receiver_address, 3) c.create_transfer(3) assert c.deposit >= 3 assert c.balance == 3 sig = sign_balance_proof(receiver_privkey, c.receiver, c.block, c.balance) assert c.close_cooperatively(sig) assert c.state == Channel.State.closed
def close_all_channels_cooperatively(client: Client, private_keys: List[str], contract_address: str, balance: int = None): addresses_to_keys = { to_checksum_address(privkey_to_addr(private_key)): private_key for private_key in private_keys } client.sync_channels() closable_channels = [ c for c in client.channels if c.state != Channel.State.closed ] log.info('Closing {} channels.'.format(len(closable_channels))) for channel in closable_channels: private_key = addresses_to_keys.get( to_checksum_address(channel.receiver)) if private_key is not None: close_channel_cooperatively(channel, private_key, contract_address, balance)
def confirmed_open_channel( channel_manager: ChannelManager, client: Client, receiver_address: str, wait_for_blocks ): channel = client.open_channel(receiver_address, 10) wait_for_blocks(channel_manager.n_confirmations + 1) gevent.sleep(channel_manager.blockchain.poll_interval) assert (channel.sender, channel.block) in channel_manager.channels return channel
def test_dynamic_price(empty_proxy: PaywalledProxy, api_endpoint_address: str, client: Client, wait_for_blocks): proxy = empty_proxy endpoint_url = "http://" + api_endpoint_address price_cycle = cycle([1, 2, 3, 4, 5]) url_to_price = {} # type: Dict def price_fn(url: str): if url in url_to_price: price = url_to_price[url] else: price = next(price_cycle) url_to_price[url] = price return price proxy.add_paywalled_resource(DynamicPriceResource, '/resource_<int:res_id>', price_fn) response = requests.get(endpoint_url + '/resource_3') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 1 response = requests.get(endpoint_url + '/resource_5') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 2 response = requests.get(endpoint_url + '/resource_2') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 3 response = requests.get(endpoint_url + '/resource_3') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 1 channel = client.get_suitable_channel(headers.receiver_address, 2) wait_for_blocks(6) channel.update_balance(2) headers = Munch() headers.balance = str(channel.balance) headers.balance_signature = encode_hex(channel.balance_sig) headers.sender_address = channel.sender headers.open_block = str(channel.block) headers = HTTPHeaders.serialize(headers) response = requests.get(endpoint_url + '/resource_5', headers=headers) assert response.status_code == 200 assert response.text.strip() == '5'
def test_cooperative_close(client: Client, receiver_privkey, receiver_address): c = client.get_suitable_channel(receiver_address, 3) assert c is not None c.create_transfer(3) assert c.deposit >= 3 assert c.balance == 3 sig = sign_close(receiver_privkey, c.sender, c.block, c.balance, client.context.channel_manager.address) assert c.close_cooperatively(sig) assert c.state == Channel.State.closed
def test_sync(client: Client, receiver_address, receiver_privkey): c = client.get_suitable_channel(receiver_address, 5) assert c in client.channels assert len(client.channels) == 1 # Check if channel is still valid after sync. client.sync_channels() assert c in client.channels assert len(client.channels) == 1 # Check if channel can be resynced after data loss. client.channels = [] client.store_channels() client.sync_channels() # Check if channel is forgotten on resync after closure. assert len(client.channels) == 1 c = client.channels[0] close_channel_cooperatively(c, receiver_privkey) client.sync_channels() assert c not in client.channels
def test_client(client: Client, receiver_address): """test if contract calls go through""" c = client.open_channel(receiver_address, 10) assert c is not None sig = c.create_transfer(5) assert sig is not None ev = c.topup(10) assert ev is not None assert c.deposit == 20 ev = c.close() assert ev is not None
def test_method_price( empty_proxy: PaywalledProxy, api_endpoint_address: str, client: Client, wait_for_blocks ): proxy = empty_proxy endpoint_url = "http://" + api_endpoint_address proxy.add_paywalled_resource( DynamicMethodResource, '/resource', resource_class_args=(42,), resource_class_kwargs={'bar': 9814072356}) # test GET response = requests.get(endpoint_url + '/resource') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 1 channel = client.get_suitable_channel(headers.receiver_address, 1 + 2 + 3 + 4) wait_for_blocks(6) channel.update_balance(int(headers.price)) headers = Munch() headers.balance = str(channel.balance) headers.balance_signature = encode_hex(channel.balance_sig) headers.sender_address = channel.sender headers.open_block = str(channel.block) headers = HTTPHeaders.serialize(headers) response = requests.get(endpoint_url + '/resource', headers=headers) assert response.status_code == 200 assert response.text.strip() == 'GET' assert_method(requests.post, endpoint_url + '/resource', headers, channel, 'POST', expected_price=2) assert_method(requests.put, endpoint_url + '/resource', headers, channel, 'PUT', expected_price=3) assert_method(requests.delete, endpoint_url + '/resource', headers, channel, 'DEL', expected_price=4)
def test_cooperative_close(client: Client, receiver_privkey, receiver_address): c = client.get_suitable_channel(receiver_address, 3) assert c is not None c.create_transfer(3) assert c.deposit >= 3 assert c.balance == 3 sig = sign_close( receiver_privkey, c.sender, c.block, c.balance, client.context.channel_manager.address ) assert c.close_cooperatively(sig) assert c.state == Channel.State.closed
def test_unconfirmed_topup( channel_manager: ChannelManager, client: Client, receiver_address: str, wait_for_blocks ): blockchain = channel_manager.blockchain channel_manager.wait_sync() channel = client.open_channel(receiver_address, 10) wait_for_blocks(1) gevent.sleep(blockchain.poll_interval) assert (channel.sender, channel.block) in channel_manager.unconfirmed_channels channel.topup(5) wait_for_blocks(channel_manager.blockchain.n_confirmations) gevent.sleep(blockchain.poll_interval) assert (channel.sender, channel.block) in channel_manager.channels channel_rec = channel_manager.channels[channel.sender, channel.block] assert channel_rec.deposit == 15
def test_close_unconfirmed_event( channel_manager: ChannelManager, client: Client, receiver_address: str, wait_for_blocks ): channel_manager.wait_sync() blockchain = channel_manager.blockchain # if unconfirmed channel is closed it should simply be forgotten channel = client.open_channel(receiver_address, 10) wait_for_blocks(1) gevent.sleep(blockchain.poll_interval) assert (channel.sender, channel.block) in channel_manager.unconfirmed_channels assert (channel.sender, channel.block) not in channel_manager.channels channel.close() wait_for_blocks(channel_manager.blockchain.n_confirmations) # opening confirmed gevent.sleep(blockchain.poll_interval) assert (channel.sender, channel.block) not in channel_manager.unconfirmed_channels assert (channel.sender, channel.block) in channel_manager.channels wait_for_blocks(1) # closing confirmed gevent.sleep(blockchain.poll_interval) assert (channel.sender, channel.block) not in channel_manager.unconfirmed_channels assert (channel.sender, channel.block) in channel_manager.channels
def test_dynamic_price( empty_proxy: PaywalledProxy, api_endpoint_address: str, client: Client, wait_for_blocks ): proxy = empty_proxy endpoint_url = "http://" + api_endpoint_address price_cycle = cycle([1, 2, 3, 4, 5]) url_to_price = {} # type: Dict def price_fn(): url = request.path if int(url.split('_')[-1]) == 0: return 0 if url in url_to_price: price = url_to_price[url] else: price = next(price_cycle) url_to_price[url] = price return price proxy.add_paywalled_resource( DynamicPriceResource, '/resource_<int:res_id>', price=price_fn ) response = requests.get(endpoint_url + '/resource_3') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 1 response = requests.get(endpoint_url + '/resource_5') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 2 response = requests.get(endpoint_url + '/resource_2') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 3 response = requests.get(endpoint_url + '/resource_3') assert response.status_code == 402 headers = HTTPHeaders.deserialize(response.headers) assert int(headers.price) == 1 response = requests.get(endpoint_url + '/resource_0') assert response.status_code == 200 assert response.text.strip() == '0' channel = client.get_suitable_channel(headers.receiver_address, 2) wait_for_blocks(6) channel.update_balance(2) headers = Munch() headers.balance = str(channel.balance) headers.balance_signature = encode_hex(channel.balance_sig) headers.sender_address = channel.sender headers.open_block = str(channel.block) headers = HTTPHeaders.serialize(headers) response = requests.get(endpoint_url + '/resource_5', headers=headers) assert response.status_code == 200 assert response.text.strip() == '5'
def test_different_receivers( web3: Web3, make_account, private_keys: List[str], channel_manager_contract: Contract, token_contract: Contract, mine_sync_event, client: Client, sender_address: str, wait_for_blocks, use_tester: bool, state_db_path: str ): if not use_tester: pytest.skip('This test takes several hours on real blockchains.') receiver1_privkey = make_account( RECEIVER_ETH_ALLOWANCE, RECEIVER_TOKEN_ALLOWANCE, private_keys[2] ) receiver2_privkey = make_account( RECEIVER_ETH_ALLOWANCE, RECEIVER_TOKEN_ALLOWANCE, private_keys[3] ) receiver1_address = privkey_to_addr(receiver1_privkey) channel_manager1 = ChannelManager( web3, channel_manager_contract, token_contract, receiver1_privkey, n_confirmations=5, state_filename=state_db_path ) start_channel_manager(channel_manager1, use_tester, mine_sync_event) channel_manager2 = ChannelManager( web3, channel_manager_contract, token_contract, receiver2_privkey, n_confirmations=5, state_filename=state_db_path ) start_channel_manager(channel_manager2, use_tester, mine_sync_event) channel_manager1.wait_sync() channel_manager2.wait_sync() blockchain = channel_manager1.blockchain assert channel_manager2.blockchain.n_confirmations == blockchain.n_confirmations assert channel_manager2.blockchain.poll_interval == blockchain.poll_interval # unconfirmed open channel = client.open_channel(receiver1_address, 10) wait_for_blocks(1) gevent.sleep(blockchain.poll_interval) assert (sender_address, channel.block) in channel_manager1.unconfirmed_channels assert (sender_address, channel.block) not in channel_manager2.unconfirmed_channels # confirmed open wait_for_blocks(blockchain.n_confirmations) gevent.sleep(blockchain.poll_interval) assert (sender_address, channel.block) in channel_manager1.channels assert (sender_address, channel.block) not in channel_manager2.channels # unconfirmed topup channel.topup(5) wait_for_blocks(1) gevent.sleep(blockchain.poll_interval) channel_rec = channel_manager1.channels[sender_address, channel.block] assert len(channel_rec.unconfirmed_topups) == 1 assert channel_rec.deposit == 10 # confirmed topup wait_for_blocks(blockchain.n_confirmations) gevent.sleep(blockchain.poll_interval) channel_rec = channel_manager1.channels[sender_address, channel.block] assert len(channel_rec.unconfirmed_topups) == 0 assert channel_rec.deposit == 15 # closing channel.close() wait_for_blocks(blockchain.n_confirmations) gevent.sleep(blockchain.poll_interval) channel_rec = channel_manager1.channels[sender_address, channel.block] assert channel_rec.is_closed is True # settlement block_before = web3.eth.blockNumber wait_for_blocks(channel_rec.settle_timeout - block_before) channel.settle() wait_for_blocks(blockchain.n_confirmations) gevent.sleep(blockchain.poll_interval) assert (sender_address, channel.block) not in channel_manager1.channels channel_manager1.stop() channel_manager2.stop()
def test_channel_opening( client: Client, web3: Web3, make_account, private_keys: List[str], channel_manager_contract, token_contract, mine_sync_event, wait_for_blocks, use_tester, state_db_path ): receiver1_privkey = make_account( RECEIVER_ETH_ALLOWANCE, RECEIVER_TOKEN_ALLOWANCE, private_keys[2] ) receiver2_privkey = make_account( RECEIVER_ETH_ALLOWANCE, RECEIVER_TOKEN_ALLOWANCE, private_keys[3] ) receiver_address = privkey_to_addr(receiver1_privkey) # make sure channel_manager1 is terminated properly, otherwise Blockchain will be running # in the background, ruining other tests' results channel_manager1 = ChannelManager( web3, channel_manager_contract, token_contract, receiver1_privkey, n_confirmations=5, state_filename=state_db_path ) start_channel_manager(channel_manager1, use_tester, mine_sync_event) channel_manager2 = ChannelManager( web3, channel_manager_contract, token_contract, receiver2_privkey, n_confirmations=5, state_filename=state_db_path ) start_channel_manager(channel_manager2, use_tester, mine_sync_event) channel_manager1.wait_sync() channel_manager2.wait_sync() blockchain = channel_manager1.blockchain channel = client.open_channel(receiver_address, 10) # should be in unconfirmed channels wait_for_blocks(1) gevent.sleep(blockchain.poll_interval) assert (channel.sender, channel.block) not in channel_manager1.channels assert (channel.sender, channel.block) in channel_manager1.unconfirmed_channels channel_rec = channel_manager1.unconfirmed_channels[channel.sender, channel.block] assert is_same_address(channel_rec.receiver, receiver_address) assert is_same_address(channel_rec.sender, channel.sender) assert channel_rec.mtime == channel_rec.ctime # should be confirmed after n blocks wait_for_blocks(blockchain.n_confirmations) gevent.sleep(blockchain.poll_interval) assert (channel.sender, channel.block) in channel_manager1.channels channel_rec = channel_manager1.channels[channel.sender, channel.block] assert is_same_address(channel_rec.receiver, receiver_address) assert is_same_address(channel_rec.sender, channel.sender) assert channel_rec.balance == 0 assert channel_rec.last_signature is None assert channel_rec.is_closed is False assert channel_rec.settle_timeout == -1 # should not appear in other channel manager assert (channel.sender, channel.block) not in channel_manager2.channels assert (channel.sender, channel.block) not in channel_manager2.unconfirmed_channels channel_manager1.stop() channel_manager2.stop()