def test_cancel_transfer(): deposit = 100 asset = sha3('test_cancel_transfer')[:20] # pylint: disable=unbalanced-tuple-unpacking app0, app1, app2 = create_sequential_network(num_nodes=3, deposit=deposit, asset=asset) messages = setup_messages_cb() mlogger = MessageLogger() assert_synched_channels( channel(app0, app1, asset), deposit, [], channel(app1, app0, asset), deposit, [] ) assert_synched_channels( channel(app1, app2, asset), deposit, [], channel(app2, app1, asset), deposit, [] ) # drain the channel app1 -> app2 amount = 80 direct_transfer(app1, app2, asset, amount) assert_synched_channels( channel(app0, app1, asset), deposit, [], channel(app1, app0, asset), deposit, [] ) assert_synched_channels( channel(app1, app2, asset), deposit - amount, [], channel(app2, app1, asset), deposit + amount, [] ) # app1 -> app2 is the only available path and doens't have resource, app1 # needs to send CancelTransfer to app0 transfer(app0, app2, asset, 50) assert_synched_channels( channel(app0, app1, asset), deposit, [], channel(app1, app0, asset), deposit, [] ) assert_synched_channels( channel(app1, app2, asset), deposit - amount, [], channel(app2, app1, asset), deposit + amount, [] ) assert len(messages) == 6 # DirectTransfer + MediatedTransfer + CancelTransfer + a Ack for each app1_messages = mlogger.get_node_messages(pex(app1.raiden.address), only='sent') assert isinstance(app1_messages[-1], CancelTransfer)
def raiden_chain(request, private_keys, asset, channels_per_node, deposit, settle_timeout, poll_timeout, registry_address, blockchain_service, transport_class): blockchain_service_class = BlockChainServiceMock blockchain_service.new_channel_manager_contract(asset) raiden_apps = create_sequential_network( private_keys, asset, registry_address, channels_per_node, deposit, settle_timeout, poll_timeout, transport_class, blockchain_service_class, ) _raiden_cleanup(request, raiden_apps) return raiden_apps
def test_settled_lock(): """ After a lock has it's secret revealed and a transfer happened, the lock cannot be used to net any value with the contract. """ deposit = 100 asset = sha3('test_settled_lock')[:20] amount = 30 # pylint: disable=unbalanced-tuple-unpacking apps = create_sequential_network(num_nodes=4, deposit=deposit, asset=asset) # mediated transfer with the secret revealed transfer(apps[0], apps[3], asset, amount) # create the latest transfer direct_transfer(apps[0], apps[1], asset, amount) secret = '' # need to get the secret attack_channel = channel(apps[2], apps[1], asset) secret_transfer = get_received_transfer(attack_channel, 0) last_transfer = get_received_transfer(attack_channel, 1) nettingcontract_address = attack_channel.nettingcontract_address # create a fake proof merkle_proof = attack_channel.our_state.locked.get_proof(secret_transfer) # call close giving the secret for a transfer that has being revealed apps[1].raiden.chain.close( asset, nettingcontract_address, apps[1].raiden.address, [last_transfer], [(merkle_proof, secret_transfer.lock, secret)], ) # forward the block number to allow settle for _ in range(NettingChannelContract.locked_time): apps[2].raiden.chain.next_block() apps[1].raiden.chain.settle(asset, nettingcontract_address)
def test_settled_lock(): """ After a lock has it's secret revealed and a transfer happened, the lock cannot be used to net any value with the contract. """ deposit = 100 asset = sha3('test_settled_lock')[:20] amount = 30 # pylint: disable=unbalanced-tuple-unpacking apps = create_sequential_network(num_nodes=4, deposit=deposit, asset=asset) # mediated transfer with the secret revealed transfer(apps[0], apps[3], asset, amount) # create the latest transfer direct_transfer(apps[0], apps[1], asset, amount) secret = '' # need to get the secret attack_channel = channel(apps[2], apps[1], asset) secret_transfer = get_received_transfer(attack_channel, 0) last_transfer = get_received_transfer(attack_channel, 1) nettingcontract_address = attack_channel.nettingcontract_address # create a fake proof merkle_proof = attack_channel.our_state.locked.get_proof(secret_transfer) # call close giving the secret for a transfer that has being revealed apps[1].raiden.chain.close( asset, nettingcontract_address, apps[1].raiden.address, [last_transfer], [(merkle_proof, secret_transfer.lock, secret)], ) # forward the block number to allow settle for _ in range(NettingChannelContract.settle_timeout): apps[2].raiden.chain.next_block() apps[1].raiden.chain.settle(asset, nettingcontract_address)
def raiden_chain(request, assets_addresses, channels_per_node, deposit, settle_timeout, blockchain_services, transport_class): if len(assets_addresses) > 1: raise ValueError('raiden_chain only works with a single asset') assert channels_per_node in (0, 1, 2, CHAIN), ( 'deployed_network uses create_sequential_network that can only work ' 'with 0, 1 or 2 channels') verbosity = request.config.option.verbose raiden_apps = create_sequential_network( blockchain_services, assets_addresses[0], channels_per_node, deposit, settle_timeout, transport_class, verbosity, ) _raiden_cleanup(request, raiden_apps) return raiden_apps
def test_start_end_attack(): """ An attacker can try to steal assets from a hub or the last node in a path. The attacker needs to use two addresses (A1 and A2) and connect both to the hub H, once connected a mediated transfer is initialized from A1 to A2 through H, once the node A2 receives the mediated transfer the attacker uses the it's know secret and reveal to close and settles the channel H-A2, without revealing the secret to H's raiden node. The intention is to make the hub transfer the asset but for him to be unable to require the asset A1. """ asset = sha3('test_two_attack')[:20] deposit = 100 amount = 30 apps = create_sequential_network(num_nodes=3, deposit=deposit, asset=asset) # The attacker creates a mediated transfer from it's account A1, to it's # account A2, throught the hub H secret = hidden_mediated_transfer(apps, asset, amount) attack_channel = channel(apps[2], apps[1], asset) attack_transfer = get_received_transfer(attack_channel, 0) attack_contract = attack_channel.nettingcontract_address hub_contract = channel(apps[1], apps[0], asset).nettingcontract_address # the attacker can create a merkle proof of the locked transfer merkle_proof = attack_channel.our_state.locked.get_proof(attack_transfer) # start the settle counter apps[2].raiden.chain.close( asset, attack_contract, apps[2].raiden.address, [attack_transfer], [], ) # wait until the last block to reveal the secret for _ in range(attack_transfer.lock.expiration - 1): apps[2].raiden.chain.next_block() # since the attacker knows the secret he can net the lock apps[2].raiden.chain.close( asset, attack_contract, apps[2].raiden.address, [attack_transfer], [(merkle_proof, attack_transfer.lock, secret)], ) # XXX: verify that the secret was publicized # at this point the hub might not know yet the secret, and won't be able to # claim the asset from the channel A1 - H # the attacker settle the contract apps[2].raiden.chain.next_block() apps[2].raiden.chain.settle(asset, attack_contract) # at this point the attack has the "stolen" funds attack_contract = apps[2].raiden.chain.asset_hashchannel[asset][attack_contract] assert attack_contract.participants[apps[2].raiden.address]['netted'] == deposit + amount assert attack_contract.participants[apps[1].raiden.address]['netted'] == deposit - amount # and the hub's channel A1-H doesn't hub_contract = apps[1].raiden.chain.asset_hashchannel[asset][hub_contract] assert hub_contract.participants[apps[0].raiden.address]['netted'] == deposit assert hub_contract.participants[apps[1].raiden.address]['netted'] == deposit # to mitigate the attack the Hub _needs_ to use a lower expiration for the # locked transfer between H-A2 than A1-H, since for A2 to acquire the asset # it needs to make the secret public in the block chain we publish the # secret through an event and the Hub will be able to require it's funds apps[1].raiden.chain.next_block() # XXX: verify that the Hub has found the secret, close and settle the channel # the hub has acquired it's asset hub_contract = apps[1].raiden.chain.asset_hashchannel[asset][hub_contract] assert hub_contract.participants[apps[0].raiden.address]['netted'] == deposit + amount assert hub_contract.participants[apps[1].raiden.address]['netted'] == deposit - amount
def deployed_network(request, private_keys, channels_per_node, deposit, number_of_assets, settle_timeout, poll_timeout, transport_class, geth_cluster): gevent.sleep(2) assert channels_per_node in (0, 1, 2, CHAIN), ( 'deployed_network uses create_sequential_network that can only work ' 'with 0, 1 or 2 channels' ) privatekey = private_keys[0] address = privtoaddr(privatekey) blockchain_service_class = BlockChainService jsonrpc_client = JSONRPCClient( host='0.0.0.0', privkey=privatekey, print_communication=False, ) patch_send_transaction(jsonrpc_client) humantoken_path = get_contract_path('HumanStandardToken.sol') registry_path = get_contract_path('Registry.sol') humantoken_contracts = compile_file(humantoken_path, libraries=dict()) registry_contracts = compile_file(registry_path, libraries=dict()) registry_proxy = jsonrpc_client.deploy_solidity_contract( address, 'Registry', registry_contracts, dict(), tuple(), timeout=poll_timeout, ) registry_address = registry_proxy.address # Using 3 * deposit because we assume that is the maximum number of # channels that will be created. # `total_per_node = channels_per_node * deposit` total_per_node = 3 * deposit total_asset = total_per_node * len(private_keys) asset_addresses = [] for _ in range(number_of_assets): token_proxy = jsonrpc_client.deploy_solidity_contract( address, 'HumanStandardToken', humantoken_contracts, dict(), (total_asset, 'raiden', 2, 'Rd'), timeout=poll_timeout, ) asset_address = token_proxy.address assert len(asset_address) asset_addresses.append(asset_address) transaction_hash = registry_proxy.addAsset(asset_address) # pylint: disable=no-member jsonrpc_client.poll(transaction_hash.decode('hex'), timeout=poll_timeout) # only the creator of the token starts with a balance, transfer from # the creator to the other nodes for transfer_to in private_keys: if transfer_to != jsonrpc_client.privkey: transaction_hash = token_proxy.transfer( # pylint: disable=no-member privtoaddr(transfer_to), total_per_node, startgas=GAS_LIMIT, ) jsonrpc_client.poll(transaction_hash.decode('hex')) for key in private_keys: assert token_proxy.balanceOf(privtoaddr(key)) == total_per_node # pylint: disable=no-member raiden_apps = create_sequential_network( private_keys, asset_addresses[0], registry_address, channels_per_node, deposit, settle_timeout, poll_timeout, transport_class, blockchain_service_class, ) _raiden_cleanup(request, raiden_apps) return raiden_apps
def test_start_end_attack(): """ An attacker can try to steal assets from a hub or the last node in a path. The attacker needs to use two addresses (A1 and A2) and connect both to the hub H, once connected a mediated transfer is initialized from A1 to A2 through H, once the node A2 receives the mediated transfer the attacker uses the it's know secret and reveal to close and settles the channel H-A2, without revealing the secret to H's raiden node. The intention is to make the hub transfer the asset but for him to be unable to require the asset A1. """ asset = sha3('test_two_attack')[:20] deposit = 100 amount = 30 apps = create_sequential_network(num_nodes=3, deposit=deposit, asset=asset) # The attacker creates a mediated transfer from it's account A1, to it's # account A2, throught the hub H secret = hidden_mediated_transfer(apps, asset, amount) attack_channel = channel(apps[2], apps[1], asset) attack_transfer = get_received_transfer(attack_channel, 0) attack_contract = attack_channel.nettingcontract_address hub_contract = channel(apps[1], apps[0], asset).nettingcontract_address # the attacker can create a merkle proof of the locked transfer merkle_proof = attack_channel.our_state.locked.get_proof(attack_transfer) # start the settle counter apps[2].raiden.chain.close( asset, attack_contract, apps[2].raiden.address, [attack_transfer], [], ) # wait until the last block to reveal the secret for _ in range(attack_transfer.lock.expiration - 1): apps[2].raiden.chain.next_block() # since the attacker knows the secret he can net the lock apps[2].raiden.chain.close( asset, attack_contract, apps[2].raiden.address, [attack_transfer], [(merkle_proof, attack_transfer.lock, secret)], ) # XXX: verify that the secret was publicized # at this point the hub might not know yet the secret, and won't be able to # claim the asset from the channel A1 - H # the attacker settle the contract apps[2].raiden.chain.next_block() apps[2].raiden.chain.settle(asset, attack_contract) # at this point the attack has the "stolen" funds attack_contract = apps[2].raiden.chain.asset_hashchannel[asset][ attack_contract] assert attack_contract.participants[ apps[2].raiden.address]['netted'] == deposit + amount assert attack_contract.participants[ apps[1].raiden.address]['netted'] == deposit - amount # and the hub's channel A1-H doesn't hub_contract = apps[1].raiden.chain.asset_hashchannel[asset][hub_contract] assert hub_contract.participants[ apps[0].raiden.address]['netted'] == deposit assert hub_contract.participants[ apps[1].raiden.address]['netted'] == deposit # to mitigate the attack the Hub _needs_ to use a lower expiration for the # locked transfer between H-A2 than A1-H, since for A2 to acquire the asset # it needs to make the secret public in the block chain we publish the # secret through an event and the Hub will be able to require it's funds apps[1].raiden.chain.next_block() # XXX: verify that the Hub has found the secret, close and settle the channel # the hub has acquired it's asset hub_contract = apps[1].raiden.chain.asset_hashchannel[asset][hub_contract] assert hub_contract.participants[ apps[0].raiden.address]['netted'] == deposit + amount assert hub_contract.participants[ apps[1].raiden.address]['netted'] == deposit - amount
def deployed_network(request, private_keys, channels_per_node, deposit, number_of_assets, settle_timeout, poll_timeout, transport_class, geth_cluster): gevent.sleep(2) assert channels_per_node in (0, 1, 2, CHAIN), ( 'deployed_network uses create_sequential_network that can only work ' 'with 0, 1 or 2 channels') privatekey = private_keys[0] address = privtoaddr(privatekey) blockchain_service_class = BlockChainService jsonrpc_client = JSONRPCClient( host='0.0.0.0', privkey=privatekey, print_communication=False, ) patch_send_transaction(jsonrpc_client) humantoken_path = get_contract_path('HumanStandardToken.sol') registry_path = get_contract_path('Registry.sol') humantoken_contracts = compile_file(humantoken_path, libraries=dict()) registry_contracts = compile_file(registry_path, libraries=dict()) registry_proxy = jsonrpc_client.deploy_solidity_contract( address, 'Registry', registry_contracts, dict(), tuple(), timeout=poll_timeout, ) registry_address = registry_proxy.address # Using 3 * deposit because we assume that is the maximum number of # channels that will be created. # `total_per_node = channels_per_node * deposit` total_per_node = 3 * deposit total_asset = total_per_node * len(private_keys) asset_addresses = [] for _ in range(number_of_assets): token_proxy = jsonrpc_client.deploy_solidity_contract( address, 'HumanStandardToken', humantoken_contracts, dict(), (total_asset, 'raiden', 2, 'Rd'), timeout=poll_timeout, ) asset_address = token_proxy.address assert len(asset_address) asset_addresses.append(asset_address) transaction_hash = registry_proxy.addAsset(asset_address) # pylint: disable=no-member jsonrpc_client.poll(transaction_hash.decode('hex'), timeout=poll_timeout) # only the creator of the token starts with a balance, transfer from # the creator to the other nodes for transfer_to in private_keys: if transfer_to != jsonrpc_client.privkey: transaction_hash = token_proxy.transfer( # pylint: disable=no-member privtoaddr(transfer_to), total_per_node, startgas=GAS_LIMIT, ) jsonrpc_client.poll(transaction_hash.decode('hex')) for key in private_keys: assert token_proxy.balanceOf(privtoaddr(key)) == total_per_node # pylint: disable=no-member raiden_apps = create_sequential_network( private_keys, asset_addresses[0], registry_address, channels_per_node, deposit, settle_timeout, poll_timeout, transport_class, blockchain_service_class, ) _raiden_cleanup(request, raiden_apps) return raiden_apps