├── .bumpversion_client.cfg ├── .circleci ├── conditional_skip.sh ├── config.yml ├── fetch_geth_parity_solc.sh ├── fetch_pr_base_commit.sh ├── fetch_ssh_hostkey.sh ├── get_archive_tag.sh ├── kill_if_no_output.py ├── run_tests.sh ├── select_tests.sh └── upload_to_s3.sh ├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yaml │ └── feature_request.md ├── auto_assign.yml ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── auto-merge-dependabot.yml │ ├── check-licenses.yml │ ├── dependent-issues.yml │ └── docker-images.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .shellcheckrc ├── .unittest-coveragerc ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── SECURITY.md ├── codecov.yml ├── config_sample.toml ├── conftest.py ├── docker ├── Dockerfile ├── build.Dockerfile ├── hooks │ └── build ├── raiden.desktop └── raiden.svg ├── docs ├── Makefile ├── Thoughts-on-Performance.md ├── _static │ ├── css │ │ └── custom.css │ ├── sphinxcontrib-httpexample.css │ └── sphinxcontrib-httpexample.js ├── adr │ ├── 0002-testing-strategy.rst │ ├── 0003-git-workflow.rst │ ├── 0004-python-dependency-management.rst │ ├── 0005-withdraw-expiry.rst │ ├── 0006-transport-mulitlayer-with-webRTC.rst │ ├── 0007-drain-imbalance-fee.rst │ ├── 0008-connection-manager-prevents-fast-sync.rst │ ├── 0010-refunds.rst │ ├── 0011-routing-with-token-swaps.rst │ ├── 0012-token-swaps.rst │ ├── overview.rst │ └── template.rst ├── changelog.rst ├── conf.py ├── config_file.rst ├── custom-setups │ ├── custom_token.rst │ ├── private_net_tutorial.rst │ ├── remix_compile2.png │ ├── remix_deploy.png │ ├── remix_deploy3.png │ └── remix_gist_import.png ├── custom_setups.rst ├── getting_started.rst ├── glossary.rst ├── images │ ├── architecture_overview.png │ ├── channelsgraph.png │ ├── merkletree.png │ ├── pending_transfers_for_mediator.gif │ ├── raiden_webui_channels_page_screenshot.png │ ├── raiden_webui_landing_page_screenshot.png │ ├── raiden_webui_tokens_page_screenshot.png │ ├── restore_wal.png │ ├── routing_a_transfer.gif │ ├── settle_pending_locks.gif │ ├── state_tasks.png │ ├── transfer_happy_case.gif │ ├── transfer_unhappy_case.gif │ └── write_state_change_to_wal.png ├── index.rst ├── macos_install_guide.rst ├── manual-udc-deposit.rst ├── nix.rst ├── onboarding.rst ├── other │ ├── known-issues.rst │ ├── safe-usage.rst │ └── slow-network-test.rst ├── overview_and_guide.rst ├── proxies.rst ├── raiden-api-1 │ └── api-tutorial │ │ ├── 1-join-a-token-network.inc.rst │ │ ├── 2-open-a-channel.inc.rst │ │ ├── 3-deposit.inc.rst │ │ ├── 3-make-a-payment.inc.rst │ │ ├── 4-settle-payments-and-close-channels.inc.rst │ │ ├── README.rst │ │ └── withdraw-tokens.inc.rst ├── raiden.png ├── raiden_services.rst ├── rest_api.rst ├── testing.rst ├── the-raiden-web-interface │ ├── add-more-tokens.inc.rst │ ├── close-channels-and-settle-payments.inc.rst │ ├── join-a-token-network.inc.rst │ ├── payment.inc.rst │ ├── screens.inc.rst │ ├── the-raiden-web-interface.rst │ ├── user-deposit.inc.rst │ └── webui.gif ├── token_swaps.rst ├── trademarks.rst └── using-raiden-on-mainnet │ ├── channel-status.inc.rst │ ├── deposit-tokens-to-the-udc.inc.rst │ ├── deposit-tokens.inc.rst │ ├── join-a-token-network-1.inc.rst │ ├── make-a-payment.inc.rst │ ├── mediation-fees.inc.rst │ ├── overview.rst │ └── whitelisted-tokens-1.inc.rst ├── pylintrc ├── pyproject.toml ├── raiden.spec ├── raiden ├── __init__.py ├── __main__.py ├── _monkey.py ├── accounts.py ├── api │ ├── __init__.py │ ├── exceptions.py │ ├── objects.py │ ├── python.py │ ├── rest.py │ ├── rest_utils.py │ └── v1 │ │ ├── __init__.py │ │ ├── encoding.py │ │ └── resources.py ├── blockchain │ ├── __init__.py │ ├── decode.py │ ├── events.py │ ├── exceptions.py │ ├── filters.py │ ├── state.py │ └── utils.py ├── constants.py ├── exceptions.py ├── log_config.py ├── message_handler.py ├── messages │ ├── __init__.py │ ├── abstract.py │ ├── cmdid.py │ ├── decode.py │ ├── encode.py │ ├── healthcheck.py │ ├── metadata.py │ ├── monitoring_service.py │ ├── path_finding_service.py │ ├── synchronization.py │ ├── transfers.py │ └── withdraw.py ├── network │ ├── __init__.py │ ├── pathfinding.py │ ├── proxies │ │ ├── README.md │ │ ├── __init__.py │ │ ├── custom_token.py │ │ ├── exceptions.py │ │ ├── metadata.py │ │ ├── monitoring_service.py │ │ ├── one_to_n.py │ │ ├── payment_channel.py │ │ ├── proxy_manager.py │ │ ├── secret_registry.py │ │ ├── service_registry.py │ │ ├── token.py │ │ ├── token_network.py │ │ ├── token_network_registry.py │ │ ├── user_deposit.py │ │ └── utils.py │ ├── resolver │ │ ├── __init__.py │ │ └── client.py │ ├── rpc │ │ ├── __init__.py │ │ ├── client.py │ │ └── middleware.py │ ├── transport │ │ ├── __init__.py │ │ ├── matrix │ │ │ ├── __init__.py │ │ │ ├── client.py │ │ │ ├── rtc │ │ │ │ ├── __init__.py │ │ │ │ ├── aiogevent.py │ │ │ │ ├── utils.py │ │ │ │ └── web_rtc.py │ │ │ ├── sync_progress.py │ │ │ ├── transport.py │ │ │ └── utils.py │ │ └── utils.py │ └── utils.py ├── py.typed ├── raiden_event_handler.py ├── raiden_service.py ├── routing.py ├── services.py ├── settings.py ├── storage │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── restore.py │ ├── serialization │ │ ├── __init__.py │ │ ├── schemas.py │ │ └── serializer.py │ ├── sqlite.py │ ├── utils.py │ ├── versions.py │ └── wal.py ├── tasks.py ├── tests │ ├── README.rst │ ├── __init__.py │ ├── benchmark │ │ ├── __init__.py │ │ ├── _codespeed.py │ │ ├── conftest.py │ │ ├── test_mediated_transfer.py │ │ └── utils.py │ ├── fixtures │ │ ├── __init__.py │ │ ├── blockchain.py │ │ ├── constants.py │ │ └── variables.py │ ├── fuzz │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_state_changes.py │ │ └── utils.py │ ├── integration │ │ ├── __init__.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ ├── conftest.py │ │ │ ├── fixtures.py │ │ │ ├── rest │ │ │ │ ├── __init__.py │ │ │ │ ├── test_api_wrapper.py │ │ │ │ ├── test_channel.py │ │ │ │ ├── test_notifications_endpoint.py │ │ │ │ ├── test_payments.py │ │ │ │ ├── test_rest.py │ │ │ │ ├── test_token_networks.py │ │ │ │ └── utils.py │ │ │ ├── test_pythonapi.py │ │ │ ├── test_pythonapi_regression.py │ │ │ └── utils.py │ │ ├── cli │ │ │ ├── __init__.py │ │ │ ├── conftest.py │ │ │ ├── test_cli_development.py │ │ │ ├── test_cli_production.py │ │ │ └── util.py │ │ ├── conftest.py │ │ ├── exception.py │ │ ├── fixtures │ │ │ ├── __init__.py │ │ │ ├── blockchain.py │ │ │ ├── raiden_network.py │ │ │ ├── smartcontracts.py │ │ │ └── transport.py │ │ ├── long_running │ │ │ ├── __init__.py │ │ │ ├── test_integration_events.py │ │ │ ├── test_settlement.py │ │ │ ├── test_stress.py │ │ │ └── test_token_networks.py │ │ ├── network │ │ │ ├── __init__.py │ │ │ ├── proxies │ │ │ │ ├── __init__.py │ │ │ │ ├── test_custom_token.py │ │ │ │ ├── test_payment_channel.py │ │ │ │ ├── test_secret_registry.py │ │ │ │ ├── test_service_registry.py │ │ │ │ ├── test_token.py │ │ │ │ ├── test_token_network.py │ │ │ │ ├── test_token_network_registry.py │ │ │ │ └── test_user_deposit.py │ │ │ ├── test_pathfinding.py │ │ │ └── transport │ │ │ │ ├── __init__.py │ │ │ │ ├── test_matrix_transport.py │ │ │ │ ├── test_matrix_transport_assumptions.py │ │ │ │ └── test_web_rtc.py │ │ ├── rpc │ │ │ ├── __init__.py │ │ │ └── assumptions │ │ │ │ ├── test_geth_rpc_assumptions.py │ │ │ │ ├── test_parity_rpc_assumptions.py │ │ │ │ ├── test_rpc_call_assumptions.py │ │ │ │ ├── test_rpc_events_assumptions.py │ │ │ │ ├── test_rpc_filters_assumptions.py │ │ │ │ ├── test_rpc_gas_estimation_assumptions.py │ │ │ │ ├── test_rpc_gas_price_assumptions.py │ │ │ │ └── test_rpc_transaction_assumptions.py │ │ ├── test_balance_proof_check.py │ │ ├── test_blockchainservice.py │ │ ├── test_integration_pfs.py │ │ ├── test_raidenservice.py │ │ ├── test_recovery.py │ │ ├── test_regression.py │ │ ├── test_regression_parity.py │ │ ├── test_send_queued_messages.py │ │ └── transfer │ │ │ ├── __init__.py │ │ │ ├── test_mediatedtransfer.py │ │ │ ├── test_mediatedtransfer_events.py │ │ │ ├── test_mediatedtransfer_invalid.py │ │ │ └── test_refund_invalid.py │ ├── mocked │ │ ├── __init__.py │ │ └── test_matrix_transport.py │ ├── scenarios │ │ ├── README.md │ │ ├── bf1_basic_functionality.yaml │ │ ├── bf2_long_running.yaml │ │ ├── bf3_multi_directional_payment.yaml │ │ ├── bf4_multi_payments_same_node.yaml │ │ ├── bf6_stress_hub_node.yaml │ │ ├── bf7_long_path.yaml │ │ ├── ci │ │ │ ├── sp1 │ │ │ │ ├── bf1_basic_functionality.yaml │ │ │ │ ├── mfee2_proportional_fees.yaml │ │ │ │ ├── mfee3_only_imbalance_fees.yaml │ │ │ │ ├── ms1_simple_monitoring.yaml │ │ │ │ ├── ms4_udc_too_low.yaml │ │ │ │ ├── pfs2_simple_no_path.yaml │ │ │ │ ├── pfs3_multiple_paths.yaml │ │ │ │ ├── pfs4_use_best_path.yaml │ │ │ │ ├── pfs5_too_low_capacity.yaml │ │ │ │ ├── pfs6_simple_path_rewards.yaml │ │ │ │ ├── pfs7_multiple_payments.yaml │ │ │ │ ├── pfs8_mediator_goes_offline.yaml │ │ │ │ └── pfs9_partial_withdraw.yaml │ │ │ └── sp2 │ │ │ │ ├── bf2_long_running.yaml │ │ │ │ ├── bf3_multi_directional_payment.yaml │ │ │ │ ├── bf4_multi_payments_same_node.yaml │ │ │ │ ├── bf6_stress_hub_node.yaml │ │ │ │ ├── bf7_long_path.yaml │ │ │ │ ├── mfee1_flat_fee.yaml │ │ │ │ ├── mfee4_combined_fees.yaml │ │ │ │ ├── ms2_simple_monitoring.yaml │ │ │ │ ├── ms3_simple_monitoring.yaml │ │ │ │ └── pfs1_get_a_simple_path.yaml │ │ ├── mfee1_flat_fee.yaml │ │ ├── mfee2_proportional_fees.yaml │ │ ├── mfee3_only_imbalance_fees.yaml │ │ ├── mfee4_combined_fees.yaml │ │ ├── ms1_simple_monitoring.yaml │ │ ├── ms1_simple_monitoring_snapshot.yaml │ │ ├── ms2_simple_monitoring.yaml │ │ ├── ms3_simple_monitoring.yaml │ │ ├── ms4_udc_too_low.yaml │ │ ├── pfs1_get_a_simple_path.yaml │ │ ├── pfs2_simple_no_path.yaml │ │ ├── pfs3_multiple_paths.yaml │ │ ├── pfs4_use_best_path.yaml │ │ ├── pfs5_too_low_capacity.yaml │ │ ├── pfs6_simple_path_rewards.yaml │ │ ├── pfs7_multiple_payments.yaml │ │ ├── pfs8_mediator_goes_offline.yaml │ │ └── pfs9_partial_withdraw.yaml │ ├── smart_contracts │ │ ├── RpcTest.sol │ │ └── RpcWithStorageTest.sol │ ├── test_files │ │ ├── .gitignore │ │ ├── UTC--2016-10-26T16-55-53.551024336Z--0d5a0e4fece4b84365b9b8dba6e6d41348c73645 │ │ ├── UTC--2017-06-20T16-06-00.000000000Z--invalid │ │ ├── passwordfile.txt │ │ └── valid_keystorefile_with_unexpected_name │ ├── unit │ │ ├── __init__.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ ├── test_api.py │ │ │ ├── test_api_events.py │ │ │ └── test_encoding.py │ │ ├── channel_state │ │ │ ├── __init__.py │ │ │ ├── test_channel_deposit.py │ │ │ ├── test_closed_channel.py │ │ │ ├── test_coop_settle.py │ │ │ ├── test_locked_transfer.py │ │ │ ├── test_state.py │ │ │ ├── test_time_lock.py │ │ │ ├── test_withdraw.py │ │ │ └── utils.py │ │ ├── conftest.py │ │ ├── fixtures.py │ │ ├── network │ │ │ ├── __init__.py │ │ │ ├── rpc │ │ │ │ ├── __init__.py │ │ │ │ └── test_client.py │ │ │ └── rtc │ │ │ │ └── test_web_rtc.py │ │ ├── serialized_messages │ │ │ ├── Delivered.json │ │ │ ├── LockExpired.json │ │ │ ├── LockedTransfer.json │ │ │ ├── PFSCapacityUpdate.json │ │ │ ├── PFSFeeUpdate.json │ │ │ ├── Processed.json │ │ │ ├── RefundTransfer.json │ │ │ ├── RequestMonitoring.json │ │ │ ├── RevealSecret.json │ │ │ ├── SecretRequest.json │ │ │ ├── Unlock.json │ │ │ ├── WithdrawConfirmation.json │ │ │ ├── WithdrawExpired.json │ │ │ └── WithdrawRequest.json │ │ ├── storage │ │ │ ├── __init__.py │ │ │ ├── test_serialization.py │ │ │ ├── test_storage.py │ │ │ └── test_versions.py │ │ ├── test_accounts.py │ │ ├── test_app.py │ │ ├── test_binary_encoding.py │ │ ├── test_cli.py │ │ ├── test_data │ │ │ ├── db_events.json │ │ │ └── db_statechanges.json │ │ ├── test_dict_encoding.py │ │ ├── test_logging.py │ │ ├── test_matrix_transport.py │ │ ├── test_messages.py │ │ ├── test_node_queue.py │ │ ├── test_notifyingqueue.py │ │ ├── test_operators.py │ │ ├── test_pending_locks.py │ │ ├── test_pfs_integration.py │ │ ├── test_pfs_integration_refactored.py │ │ ├── test_raiden_event_handler.py │ │ ├── test_rpc.py │ │ ├── test_runnable.py │ │ ├── test_serialization.py │ │ ├── test_sqlite.py │ │ ├── test_startup.py │ │ ├── test_state.py │ │ ├── test_tokennetwork.py │ │ ├── test_upgrade.py │ │ ├── test_utils.py │ │ ├── test_version_check.py │ │ ├── test_wal.py │ │ ├── test_web3_assumptions.py │ │ ├── transfer │ │ │ ├── __init__.py │ │ │ ├── mediated_transfer │ │ │ │ ├── __init__.py │ │ │ │ ├── test_initiatorstate.py │ │ │ │ ├── test_mediation_fee.py │ │ │ │ ├── test_mediatorstate.py │ │ │ │ ├── test_mediatorstate_regression.py │ │ │ │ ├── test_state_and_state_change.py │ │ │ │ └── test_targetstate.py │ │ │ ├── test_architecture.py │ │ │ ├── test_channel.py │ │ │ ├── test_events.py │ │ │ ├── test_node.py │ │ │ ├── test_source_routing.py │ │ │ ├── test_utils.py │ │ │ └── test_views.py │ │ └── utils │ │ │ ├── __init__.py │ │ │ ├── test_dynamic_block_batch_size.py │ │ │ ├── test_filters.py │ │ │ ├── test_formatting.py │ │ │ ├── test_functions.py │ │ │ ├── test_mediation_fees.py │ │ │ ├── test_nursery.py │ │ │ ├── test_secrethash.py │ │ │ └── test_upgrade_manager.py │ └── utils │ │ ├── __init__.py │ │ ├── app.py │ │ ├── ci.py │ │ ├── cli.py │ │ ├── client.py │ │ ├── detect_failure.py │ │ ├── eth_node.py │ │ ├── events.py │ │ ├── factories.py │ │ ├── genesis.py │ │ ├── mediation_fees.py │ │ ├── migrations.py │ │ ├── mocks.py │ │ ├── network.py │ │ ├── protocol.py │ │ ├── smartcontracts.py │ │ ├── smoketest.py │ │ ├── synapse_config.yaml.template │ │ ├── tests.py │ │ ├── transfer.py │ │ └── transport.py ├── transfer │ ├── __init__.py │ ├── architecture.py │ ├── channel.py │ ├── events.py │ ├── identifiers.py │ ├── mediated_transfer │ │ ├── __init__.py │ │ ├── events.py │ │ ├── initiator.py │ │ ├── initiator_manager.py │ │ ├── mediation_fee.py │ │ ├── mediator.py │ │ ├── state.py │ │ ├── state_change.py │ │ ├── target.py │ │ └── tasks.py │ ├── node.py │ ├── routes.py │ ├── secret_registry.py │ ├── state.py │ ├── state_change.py │ ├── token_network.py │ ├── utils │ │ ├── __init__.py │ │ └── secret.py │ └── views.py ├── ui │ ├── __init__.py │ ├── app.py │ ├── checks.py │ ├── cli.py │ ├── console.py │ ├── prompt.py │ ├── runners.py │ ├── startup.py │ └── sync.py ├── utils │ ├── __init__.py │ ├── capabilities.py │ ├── cli.py │ ├── copy.py │ ├── datastructures.py │ ├── debugging.py │ ├── ethereum_clients.py │ ├── formatting.py │ ├── gas_reserve.py │ ├── gevent.py │ ├── http.py │ ├── keys.py │ ├── logging.py │ ├── mediation_fees.py │ ├── notifying_queue.py │ ├── nursery.py │ ├── packing.py │ ├── predicates.py │ ├── profiling │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── cpu.py │ │ ├── graph.py │ │ ├── greenlets.py │ │ ├── memory.py │ │ ├── profiler.py │ │ ├── sampler.py │ │ ├── stack.py │ │ ├── timer.py │ │ └── trace.py │ ├── runnable.py │ ├── secrethash.py │ ├── signer.py │ ├── smart_contracts.py │ ├── system.py │ ├── timeout.py │ ├── tracing.py │ ├── transfers.py │ ├── typing.py │ ├── upgrades.py │ └── validation.py └── waiting.py ├── raiden_errors_key.gpg ├── readthedocs.yml ├── requirements.txt ├── requirements ├── README.rst ├── _raiden-dev.txt ├── _raiden.txt ├── deps ├── requirements-ci.in ├── requirements-ci.txt ├── requirements-dev.in ├── requirements-dev.txt ├── requirements-docs.in ├── requirements-docs.txt ├── requirements.in └── requirements.txt ├── setup.cfg ├── setup.py ├── stubs ├── gevent │ ├── __init__.pyi │ ├── event.pyi │ ├── greenlet.pyi │ ├── hub.pyi │ └── monkey.pyi ├── greenlet.pyi ├── pytest.pyi └── structlog │ └── __init__.pyi └── tools ├── README.md ├── __init__.py ├── _compat.sh ├── check_circleci_config.py ├── debugging ├── analyze_sp_logs.py ├── channels_with_minimum_balance.py ├── channels_with_minimum_balance_config.json ├── extract_sp_exceptions.sh ├── gather-test-addresses.py ├── json-log-to-html.py ├── matrix │ ├── __init__.py │ ├── api_shell.py │ ├── check_federation_status.py │ ├── get_login.py │ ├── load_with_generate_messages.py │ ├── presence_report.py │ └── presence_update.py ├── mint.sh ├── plot │ ├── boxplot.py │ ├── line.py │ └── scatter.py ├── replay_wal.py ├── sp-download-logs.sh ├── sp_download_scenario_logs.sh ├── split_debug_logs.sh ├── state_machine_report.py ├── stress_test_transfers.py └── stress_test_transfers_config.ini ├── dummy_resolver_server.py ├── gas_cost_measures.py ├── parallel_tests.sh ├── pip-compile-wrapper.py ├── pre-commit ├── pre-commit-check-versions.py └── pre-commit-wrapper.py ├── process_results.py ├── pyinstaller_hooks ├── hook-av.py ├── hook-coincurve.py ├── hook-cytoolz.py ├── hook-raiden.py ├── hook-raiden_contracts.py ├── hook-raiden_webui.py ├── runtime_aiortc_pyav_stub.py ├── runtime_encoding.py ├── runtime_gevent_monkey.py └── runtime_raiden_contracts.py ├── pylint ├── __init__.py ├── assert_checker.py ├── del_method_checker.py └── gevent_checker.py ├── startcluster.py ├── test_and_report.sh └── transfer_eth.py /.bumpversion_client.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 3.0.1 3 | commit = False 4 | tag = False 5 | 6 | [bumpversion:file:docs/conf.py] 7 | serialize = {major}.{minor}.{patch} 8 | -------------------------------------------------------------------------------- /.circleci/conditional_skip.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Seems that circle calls this script with -e and we don't want 4 | # grep failure to stop the build 5 | set +e 6 | 7 | SKIP_TAG="\[skip tests\]" 8 | 9 | # shellcheck disable=SC2154 10 | if [[ -n ${CIRCLE_TAG} ]]; then 11 | # TAG build - never skip those 12 | echo "Tagged commit, not skipping build" 13 | exit 0 14 | fi 15 | 16 | # shellcheck disable=SC2154 17 | if [[ -z ${CIRCLE_PULL_REQUEST} ]]; then 18 | # Not a PR, also never skip 19 | echo "Not a PR, not skipping build" 20 | exit 0 21 | fi 22 | 23 | if [[ -a ~/.local/BASE_COMMIT ]]; then 24 | # The is a PR and we know the base commit (see fetch_pr_base_commit.sh) 25 | LOG_RANGE="$(cat ~/.local/BASE_COMMIT)..${CIRCLE_SHA1:?}" 26 | else 27 | # Otherwise just look at the HEAD commit 28 | LOG_RANGE="-1" 29 | fi 30 | 31 | git log --pretty="- %h %B" ${LOG_RANGE} | grep "${SKIP_TAG}" 32 | 33 | if [[ ${PIPESTATUS[1]} == 0 ]]; then 34 | echo "Skip tag found - skipping build" 35 | circleci step halt 36 | fi 37 | set -e 38 | -------------------------------------------------------------------------------- /.circleci/fetch_pr_base_commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # shellcheck disable=SC2154 4 | if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then 5 | # If this is a PR get the base commit from the GitHub API 6 | PR_DETAILS_URL="https://api.github.com/repos/${CIRCLE_PROJECT_USERNAME:?}/${CIRCLE_PROJECT_REPONAME:?}/pulls/${CIRCLE_PR_NUMBER}" 7 | BASE_COMMIT=$(curl "${PR_DETAILS_URL}" | jq -r .base.sha) 8 | if [[ ${BASE_COMMIT} =~ ^[0-9a-zA-Z]{40}$ ]]; then 9 | echo "${BASE_COMMIT}" > ~/.local/BASE_COMMIT 10 | fi 11 | fi 12 | -------------------------------------------------------------------------------- /.circleci/fetch_ssh_hostkey.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | HOST="$1" 6 | FINGERPRINT="$2" 7 | 8 | PUBKEY=$(mktemp) 9 | 10 | mkdir -p ~/.ssh 11 | chmod 700 ~/.ssh 12 | 13 | ssh-keyscan -H "${HOST}" > "${PUBKEY}" 2>/dev/null 14 | 15 | if [[ $(ssh-keygen -l -f "${PUBKEY}" | cut -d ' ' -f 2) != "${FINGERPRINT}" ]]; then 16 | echo "Warning fingerprint mismatch while fetching public key for ${HOST}" 17 | exit 1 18 | fi 19 | 20 | cat "${PUBKEY}" >> ~/.ssh/known_hosts 21 | -------------------------------------------------------------------------------- /.circleci/get_archive_tag.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | # shellcheck disable=SC2154 5 | if [[ -n ${CIRCLE_TAG} ]]; then 6 | export ARCHIVE_TAG=${CIRCLE_TAG} 7 | if [[ ${CIRCLE_TAG} =~ .*(a|b|rc).* ]]; then 8 | export RELEASE_TYPE="RC" 9 | else 10 | export RELEASE_TYPE="RELEASE" 11 | fi 12 | 13 | else 14 | DATE=$(date +%Y-%m-%dT%H-%M-%S) 15 | RAIDEN_VERSION=$(python setup.py --version) 16 | export ARCHIVE_TAG="nightly-${DATE}-v${RAIDEN_VERSION}" 17 | export RELEASE_TYPE="NIGHTLY" 18 | fi 19 | 20 | echo "export ARCHIVE_TAG=${ARCHIVE_TAG}" >> "${BASH_ENV}" 21 | echo "export RELEASE_TYPE=${RELEASE_TYPE}" >> "${BASH_ENV}" 22 | 23 | set +ex 24 | -------------------------------------------------------------------------------- /.circleci/select_tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | 6 | tests=$1 7 | blockchain_type=$2 8 | additional_args=$3 9 | output_file=$(mktemp) 10 | 11 | { 12 | pytest \ 13 | "${tests}" \ 14 | --collect-only \ 15 | --quiet \ 16 | "--blockchain-type=${blockchain_type}" \ 17 | ${additional_args:+"$additional_args"} > "${output_file}" 18 | } || { 19 | # Print pytest's output if test selection failed. This may happen if a 20 | # depencency is broken or if the codebase has syntax errors. 21 | cat "${output_file}" 22 | exit 1; 23 | } 24 | 25 | # Save the tests in a file, it will be used by follow up steps 26 | # shellcheck disable=SC2002 27 | cat "${output_file}" \ 28 | | grep '::' \ 29 | | circleci tests split --split-by=timings --timings-type=testname \ 30 | | grep '::' > selected-tests.txt 31 | 32 | # Print all the selected tests for debugging 33 | cat selected-tests.txt 34 | -------------------------------------------------------------------------------- /.circleci/upload_to_s3.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o xtrace 5 | set -o nounset 6 | 7 | access_key=$1 8 | secret_key=$2 9 | dir=$3 10 | bucket=$4 11 | endpoint=$5 12 | 13 | [[ -n ${RELEASE_TYPE:?} ]] 14 | 15 | s3cmd \ 16 | --access_key "${access_key}" \ 17 | --secret_key "${secret_key}" \ 18 | --host "${endpoint}" \ 19 | --host-bucket "%(bucket)s.${endpoint}" \ 20 | --no-progress \ 21 | --stats \ 22 | --no-delete-removed \ 23 | --guess-mime-type \ 24 | --acl-public \ 25 | put \ 26 | "${dir}"/_LATEST*.txt \ 27 | s3://"${bucket}"/ 28 | 29 | s3cmd \ 30 | --access_key "${access_key}" \ 31 | --secret_key "${secret_key}" \ 32 | --host "${endpoint}" \ 33 | --host-bucket "%(bucket)s.${endpoint}" \ 34 | --no-progress \ 35 | --stats \ 36 | --no-delete-removed \ 37 | --guess-mime-type \ 38 | --acl-public \ 39 | put \ 40 | "${dir}"/raiden* \ 41 | s3://"${bucket}"/"${RELEASE_TYPE}"/ 42 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | tools/testnet/.terraform/plugins 3 | docs/_build 4 | .mypy_cache 5 | .eggs 6 | .pytest_cache 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root=true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = false 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 4 9 | 10 | [*.{js,py}] 11 | trim_trailing_whitespace = true 12 | 13 | [*.{yml,yaml,json}] 14 | indent_size = 2 15 | 16 | [Makefile] 17 | insert_final_newline = true 18 | indent_style = tab 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | *.py diff=python -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yaml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Critical Vulnerability Report 4 | url: https://bounty.raiden.network/ 5 | about: | 6 | * DO NOT REPORT SECURITY ISSUES VIA THE ISSUE TRACKER * 7 | If the issue you want to report is a critical vulnerability, 8 | for example something that may lead to lock up or loss of funds for Raiden users, 9 | then please do not use the public issue tracker. 10 | Instead send an email with a detailed report to bounty@raiden.network. 11 | Click the link to the right for details on our bug bounty program. 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Requesting new features to be added to either Raiden backend or UI 4 | --- 5 | 6 | ## Abstract 7 | 8 | Please describe by example what problem you see in the current Raiden Network implementation that you would like addressed. 9 | 10 | ## Motivation 11 | 12 | In this section please describe how you propose to address the problem you described earlier. Try to include example use cases that showcase the value of your proposal. 13 | 14 | ## Specification 15 | 16 | If the feature is technical in nature please write as detailed as possible a specification of what needs to be built. 17 | 18 | ## Backwards Compatibility 19 | 20 | If the feature is technical in nature and introduces changes to the Raiden Protocol, then please use this section to describe how backwards compatibility with the previous version of the protocol can be achieved. 21 | -------------------------------------------------------------------------------- /.github/auto_assign.yml: -------------------------------------------------------------------------------- 1 | # Set to true to add reviewers to pull requests 2 | addReviewers: true 3 | 4 | # Set to true to add assignees to pull requests 5 | addAssignees: false 6 | 7 | # A list of reviewers to be added to pull requests (GitHub user name) 8 | reviewers: 9 | - netcriptus 10 | - karlb 11 | 12 | # A number of reviewers added to the pull request 13 | # Set 0 to add all the reviewers (default: 0) 14 | numberOfReviewers: 1 15 | 16 | # A list of assignees, overrides reviewers if set 17 | # assignees: 18 | # - assigneeA 19 | 20 | # A number of assignees to add to the pull request 21 | # Set to 0 to add all of the assignees. 22 | # Uses numberOfReviewers if unset. 23 | # numberOfAssignees: 2 24 | 25 | # A list of keywords to be skipped the process that add reviewers if pull requests include it 26 | skipKeywords: 27 | - WIP 28 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" 4 | directory: "/requirements/" 5 | schedule: 6 | interval: "daily" 7 | open-pull-requests-limit: 5 8 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Fixes: # 4 | 5 | Please, describe what this PR does in detail: 6 | - If it's a bug fix, detail the root cause of the bug and how this PR fixes it. 7 | - If it's a new feature, describe the feature this PR is introducing and design decisions. 8 | - If it's a refactoring, describe why it is necessary. What are its pros and cons in respect to the previous code, and other possible design choices. 9 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge-dependabot.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-merge 2 | on: pull_request_target 3 | 4 | permissions: 5 | pull-requests: write 6 | contents: write 7 | 8 | jobs: 9 | dependabot: 10 | runs-on: ubuntu-latest 11 | if: ${{ github.actor == 'dependabot[bot]' }} 12 | steps: 13 | - name: Dependabot metadata 14 | id: metadata 15 | uses: dependabot/fetch-metadata@v1.1.1 16 | with: 17 | github-token: "${{ secrets.GITHUB_TOKEN }}" 18 | - name: Enable auto-merge for Dependabot PRs 19 | if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} 20 | run: gh pr merge --auto --rebase "$PR_URL" 21 | env: 22 | PR_URL: ${{github.event.pull_request.html_url}} 23 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 24 | -------------------------------------------------------------------------------- /.github/workflows/check-licenses.yml: -------------------------------------------------------------------------------- 1 | name: Check Licenses 2 | on: 3 | pull_request: 4 | paths: 5 | - 'requirements/requirements.txt' 6 | - 'requirements/requirements.in' 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | license-check: 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | python-version: [3.9] 17 | os: [ubuntu-latest] 18 | runs-on: ${{ matrix.os }} 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: actions/setup-python@v2 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | - name: install dependencies 25 | run: | 26 | python3 -m venv .venv-raiden 27 | source .venv-raiden/bin/activate 28 | make install 29 | pip install -U pip setuptools 30 | pip install liccheck 31 | - name: check versions 32 | run: | 33 | source .venv-raiden/bin/activate 34 | liccheck -r requirements/requirements.txt 35 | -------------------------------------------------------------------------------- /.github/workflows/dependent-issues.yml: -------------------------------------------------------------------------------- 1 | name: Dependent Issues 2 | 3 | on: 4 | issues: 5 | pull_request_target: 6 | schedule: 7 | - cron: '0 0 * * *' # schedule daily check 8 | 9 | jobs: 10 | check: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: z0al/dependent-issues@v1 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | with: 17 | label: "State / Blocked" 18 | check_issues: on 19 | keywords: depends on, blocked by, requires 20 | -------------------------------------------------------------------------------- /.github/workflows/docker-images.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'develop' 7 | tags: 8 | - 'v*' 9 | pull_request: 10 | branches: 11 | - 'develop' 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | docker: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - 21 | name: Checkout 22 | uses: actions/checkout@v2 23 | - 24 | name: Docker meta 25 | id: meta 26 | uses: docker/metadata-action@v3 27 | with: 28 | images: raidennetwork/raiden 29 | - 30 | name: Login to DockerHub 31 | if: github.event_name != 'pull_request' 32 | uses: docker/login-action@v1 33 | with: 34 | username: ${{ secrets.DOCKERHUB_USERNAME }} 35 | password: ${{ secrets.DOCKERHUB_TOKEN }} 36 | - 37 | name: Build and push 38 | uses: docker/build-push-action@v2 39 | with: 40 | context: docker/ 41 | push: ${{ github.event_name != 'pull_request' }} 42 | tags: ${{ steps.meta.outputs.tags }} 43 | labels: ${{ steps.meta.outputs.labels }} 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore private keys 2 | *.pem 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | devenv/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | pip-wheel-metadata/ 30 | 31 | # Local dev-requirements 32 | requirements/requirements-local.txt 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *,cover 52 | .pytest_cache/ 53 | mypy-out.txt 54 | .hypothesis/ 55 | 56 | # Pyenv 57 | .python-version 58 | 59 | # mypy 60 | .mypy_cache 61 | 62 | # Translations 63 | *.mo 64 | *.pot 65 | 66 | # Django stuff: 67 | *.log 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # never check in .static-abi.json files 76 | *.static-abi.json 77 | 78 | # editors 79 | .idea 80 | .vscode 81 | 82 | # Our logs 83 | raiden-debug_*.log 84 | 85 | # direnv virtual envs 86 | .direnv 87 | .envrc 88 | 89 | # venvs 90 | venv/ 91 | -------------------------------------------------------------------------------- /.shellcheckrc: -------------------------------------------------------------------------------- 1 | enable=check-unassigned-uppercase 2 | 3 | # Workaround to fix issue with pre-commit. 4 | # See: https://github.com/gruntwork-io/pre-commit/issues/28 5 | disable=SC1091 6 | 7 | # This has been disabled by default in v0.7.1 but the CircleCI Orb is still on v0.7.0 8 | disable=SC2230 9 | -------------------------------------------------------------------------------- /.unittest-coveragerc: -------------------------------------------------------------------------------- 1 | [report] 2 | exclude_lines = 3 | pragma: no cover 4 | pragma: no unittest 5 | if TYPE_CHECKING: 6 | def __repr__ 7 | raise NotImplementedError 8 | assert not isinstance 9 | typecheck 10 | 11 | [run] 12 | branch = True 13 | include = 14 | raiden/*.py 15 | raiden/**/*.py 16 | omit = 17 | raiden/tests/** 18 | 19 | # Modules that are not expected to be unit tested 20 | raiden/app.py 21 | raiden/raiden_service.py 22 | raiden/ui/** 23 | raiden/network/proxies/** 24 | raiden/network/transport/** 25 | concurrency = gevent 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2020 Brainbot Labs Est. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | 24 | "chardet" is licensed under the LPGL v2.1 license (https://github.com/chardet/chardet/blob/master/LICENSE). 25 | It's source code can be found at https://github.com/chardet/chardet - you are free to modify it's source code 26 | and build a bundled version of the Raiden software (https://github.com/raiden-network/raiden) including the changes. 27 | 28 | "mirakuru" is licensed under the LPGL v3.0 license: https://github.com/ClearcodeHQ/mirakuru/blob/main/LICENSE 29 | It's source code can be found at https://github.com/ClearcodeHQ/mirakuru/ - you are free to modify it's source code 30 | and build a bundled version of the Raiden software (https://github.com/raiden-network/raiden) including the changes. 31 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include requirements.txt 3 | include requirements/requirements.txt 4 | include raiden/tests/utils/synapse_config.yaml.template 5 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If you find any security issues, please report to bounty@raiden.network 6 | 7 | Visit https://bounty.raiden.network/ for more information... 8 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # When changing this file, please use 2 | # curl --data-binary @codecov.yml https://codecov.io/validate 3 | # to check it for validity. 4 | 5 | codecov: 6 | notify: 7 | require_ci_to_pass: yes # delay sending notifications until all CI is finished 8 | 9 | coverage: 10 | status: 11 | project: 12 | default: 13 | target: 75% 14 | flags: 15 | - unit 16 | - integration 17 | threshold: 1 # check is successful is coverage decreased by less than 1% 18 | unit: 19 | target: 55% 20 | flags: 21 | - unit 22 | threshold: 1 23 | patch: off # no github status notice for coverage of the PR diff. 24 | -------------------------------------------------------------------------------- /config_sample.toml: -------------------------------------------------------------------------------- 1 | #keystore-path = 2 | #datadir = "~/.raiden" 3 | 4 | ## Ethereum address to use, must exist in `keystore-path` 5 | #address = 6 | #password-file = 7 | 8 | #secret-registry-contract-address = "0x7862eF1296EeF5Fa55ef056c0cB03FF8D4d49Cf8" 9 | #tokennetwork-registry-contract-address = "0x66eea3159A01d134DD64Bfe36fde4bE9ED9c1695" 10 | #endpoint-registry-contract-address = "0x1E3941d8c05ffFA7466216480209240cc26ea577" 11 | 12 | #console = false 13 | 14 | ## Transport system to use. 'matrix' is the only supported one at the moment 15 | #transport = "matrix" 16 | 17 | ## Available networks: 18 | ## mainnet: 1 19 | ## ropsten: 3 20 | ## rinkeby: 4 21 | ## kovan: 42 22 | #network-id = 3 23 | 24 | #sync-check = true 25 | #gas-price = 26 | #eth-rpc-endpoint = "127.0.0.1:8545" 27 | #listen-address = "0.0.0.0:38647" 28 | #max-unresponsive-time = 30 29 | #send-ping-time = 60 30 | 31 | ## Available nat methods: 32 | ## auto - Try UPnP, then STUN, fallback to none 33 | ## upnp - Try UPnP, fallback to none 34 | ## stun - Try STUN, fallback to none 35 | ## none - Use the local interface address 36 | # nat = "auto" 37 | 38 | ## Valid values: 39 | ## auto - automatically select a suitable homeserver 40 | ## A URL pointing to a Raiden matrix homeserver 41 | #matrix-server = "auto" 42 | 43 | #log-file = 44 | #log-json = false 45 | 46 | #rpc = true 47 | #rpccorsdomain = "http://localhost:*/*" 48 | #api-address = "127.0.0.1:5001" 49 | #web-ui = true 50 | 51 | #[log-config] 52 | #"" = "INFO" 53 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # This will build the latest master 3 | # 4 | # we use an intermediate image to build this image. it will make the resulting 5 | # image a bit smaller. 6 | # 7 | # you can build the image with: 8 | # 9 | # docker build . -t raiden 10 | 11 | FROM python:3.9 as builder 12 | 13 | # use --build-arg RAIDENVERSION=v0.0.3 to build a specific (tagged) version 14 | ARG REPO=raiden-network/raiden 15 | ARG RAIDENVERSION=develop 16 | 17 | # This is a "hack" to automatically invalidate the cache in case there are new commits 18 | ADD https://api.github.com/repos/${REPO}/commits/${RAIDENVERSION} /dev/null 19 | 20 | # clone raiden repo + install dependencies 21 | RUN git clone -b ${RAIDENVERSION} https://github.com/${REPO} /app/raiden 22 | 23 | RUN python3 -m venv /opt/venv 24 | 25 | ENV PATH="/opt/venv/bin:$PATH" 26 | 27 | WORKDIR /app/raiden 28 | 29 | RUN make install 30 | 31 | 32 | FROM python:3.9-slim as runner 33 | 34 | COPY --from=builder /opt/venv /opt/venv 35 | 36 | EXPOSE 5001 37 | 38 | ENTRYPOINT ["/opt/venv/bin/python3", "-m", "raiden"] 39 | -------------------------------------------------------------------------------- /docker/build.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-buster 2 | 3 | # install dependencies 4 | RUN apt-get update 5 | RUN apt-get install -y git-core wget xz-utils build-essential \ 6 | automake pkg-config libtool libffi-dev python3-dev libgmp-dev \ 7 | libavdevice-dev libavfilter-dev libopus-dev libvpx-dev pkg-config \ 8 | libsrtp2-dev 9 | 10 | RUN python3 -m venv /venv 11 | ENV PATH="/venv/bin:$PATH" 12 | 13 | ADD requirements/requirements.txt /tmp/ 14 | WORKDIR /tmp 15 | 16 | 17 | RUN pip install -U pip setuptools pip-tools wheel 18 | RUN pip-sync requirements.txt 19 | 20 | ADD . /raiden 21 | 22 | WORKDIR /raiden 23 | RUN git fetch --tags || true 24 | 25 | # install raiden 26 | RUN make install && pip install pyinstaller 27 | 28 | ARG ARCHIVE_TAG 29 | ARG ARCHITECTURE_TAG 30 | 31 | # build pyinstaller package 32 | RUN pyinstaller --noconfirm --clean raiden.spec 33 | 34 | # pack result to have a unique name to get it out of the container later 35 | RUN export FILE_TAG=${ARCHIVE_TAG:-v$(python setup.py --version)} && \ 36 | cd dist && \ 37 | tar -cvf ./raiden-${FILE_TAG}-linux-${ARCHITECTURE_TAG}.tar raiden-${FILE_TAG}-linux-${ARCHITECTURE_TAG} && \ 38 | mv raiden-${FILE_TAG}-linux-${ARCHITECTURE_TAG}.tar .. 39 | -------------------------------------------------------------------------------- /docker/hooks/build: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | docker build --build-arg RAIDENVERSION=${SOURCE_BRANCH} -t $IMAGE_NAME . 3 | -------------------------------------------------------------------------------- /docker/raiden.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Raiden 3 | Version=XXVERSIONXX 4 | Exec=raiden 5 | Icon=raiden 6 | Type=Application 7 | StartupNotify=true 8 | -------------------------------------------------------------------------------- /docker/raiden.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 18 | 21 | 22 | 23 | 25 | 26 | 28 | image/svg+xml 29 | 31 | 32 | 33 | 34 | 35 | 38 | 41 | 44 | 47 | 51 | 52 | 53 | 54 | 57 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /docs/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | @import url("default.css"); 2 | 3 | /*Make sidebar captions steelblue*/ 4 | span.caption-text { 5 | color: steelblue; 6 | } 7 | -------------------------------------------------------------------------------- /docs/_static/sphinxcontrib-httpexample.css: -------------------------------------------------------------------------------- 1 | .http-example .caption, 2 | .section ul .http-example .caption { 3 | display: inline-block; 4 | cursor: pointer; 5 | padding: 0.5em 1em; 6 | margin: 0; 7 | } 8 | .http-example .caption.selected { 9 | font-weight: bold; 10 | background-color: #fff; 11 | border: 1px solid #e1e4e5; 12 | border-bottom: 1px solid white; 13 | } 14 | .http-example div[class^='highlight'] { 15 | margin-top: -1px; 16 | } 17 | .http-example div[class^='highlight'] pre { 18 | white-space: normal; 19 | } 20 | .http-example div.highlight-bash pre { 21 | white-space: pre-wrap; 22 | } 23 | .http-example-http div[class^='highlight'] pre, 24 | .http-example-response div[class^='highlight'] pre { 25 | white-space: pre; 26 | } 27 | -------------------------------------------------------------------------------- /docs/_static/sphinxcontrib-httpexample.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var jQuery = window.jQuery || function() {}; 3 | 4 | jQuery(function($) { 5 | $('.http-example.container').each(function() { 6 | var $container = $(this), 7 | $blocks = $(this).children(), 8 | $captions = $(this).find('.caption'); 9 | $captions.each(function() { 10 | var $block = $(this).parent(); 11 | $(this).on('click', function() { 12 | $captions.removeClass('selected'); 13 | $(this).addClass('selected'); 14 | $blocks.hide(); 15 | $block.show(); 16 | }); 17 | $container.append($(this)); 18 | }); 19 | $container.append($blocks); 20 | $captions.first().click(); 21 | }); 22 | }); 23 | 24 | })(); 25 | -------------------------------------------------------------------------------- /docs/adr/0011-routing-with-token-swaps.rst: -------------------------------------------------------------------------------- 1 | Routing With Built-In Token Swaps 2 | ================================= 3 | 4 | Use Case 5 | -------- 6 | 7 | Buyers and sellers usually each have a preferred currency/token. When 8 | both parties prefer different tokens, one party has to accept dealing 9 | with a non-preferred token. In addition to being inconvenient, this can 10 | also require open a new channel, this making the payment slow and 11 | expensive. Dealing with a different token might not even be possible for 12 | some parties (e.g. due to bookkeeping restrictions). If the Raiden 13 | Network provided a way to swap tokens while doing the payment, both 14 | parties could deal with their preferred token and transaction would be 15 | quick, cheap and convenient. 16 | 17 | Concept 18 | ------- 19 | 20 | Node A wants to pay node B an amount of 10 T2 tokens, but A only has T1 21 | tokens. Node A requests a route from the PFS: 22 | 23 | :: 24 | 25 | source: A 26 | target: B 27 | target_amount: 10 T2 28 | source_token: T1 29 | 30 | The PFS looks for a route that minimizes the amount of T1 tokens that A 31 | has to spend in order for B to receive 10 T2, taking into account both 32 | mediation fees and the required token swap. The returned route might 33 | look like 34 | 35 | :: 36 | 37 | T1 T1 T2 38 | A --> M1 --> M2 --> B 39 | 40 | meaning that mediator M2 accepts T1 and sends T2, thereby providing both 41 | mediation and token conversion. Despite using different token networks, 42 | the payment will still be atomic by using the same hash time lock across 43 | the whole path. 44 | 45 | Problems 46 | -------- 47 | 48 | - Nodes willing to do token swaps have to tell the PFS their exchange 49 | rate. Broadcasting could be much traffic, polling would slow down 50 | routing. 51 | - The PFS has to be able to route through multiple token networks at 52 | the same time. 53 | - Behavior not clear for non-strict routing 54 | - The route can fail due to changes in conversion rate. This can be 55 | mitigated by adding some safety margin, but this increases the price 56 | for the sender. 57 | -------------------------------------------------------------------------------- /docs/adr/overview.rst: -------------------------------------------------------------------------------- 1 | Raiden ADRs 2 | *********** 3 | 4 | The Raiden project utilizes the `Architectural Decision Records `_ (ADR) system to capture the important architectural decisions made during development along with their context and consequences. 5 | 6 | The following is an overview of the Raiden ADRs 7 | 8 | Finished ADRs 9 | ------------- 10 | 11 | .. toctree:: 12 | :maxdepth: 1 13 | 14 | 0002-testing-strategy 15 | 0003-git-workflow 16 | 0004-python-dependency-management 17 | 0005-withdraw-expiry 18 | 0006-transport-mulitlayer-with-webRTC 19 | 0007-drain-imbalance-fee 20 | 0008-connection-manager-prevents-fast-sync 21 | 0010-refunds 22 | 23 | Discussion Documents 24 | -------------------- 25 | 26 | .. toctree:: 27 | :maxdepth: 1 28 | 29 | 0011-routing-with-token-swaps 30 | 0012-token-swaps 31 | -------------------------------------------------------------------------------- /docs/custom-setups/remix_compile2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/custom-setups/remix_compile2.png -------------------------------------------------------------------------------- /docs/custom-setups/remix_deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/custom-setups/remix_deploy.png -------------------------------------------------------------------------------- /docs/custom-setups/remix_deploy3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/custom-setups/remix_deploy3.png -------------------------------------------------------------------------------- /docs/custom-setups/remix_gist_import.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/custom-setups/remix_gist_import.png -------------------------------------------------------------------------------- /docs/custom_setups.rst: -------------------------------------------------------------------------------- 1 | Custom Setups 2 | ############# 3 | 4 | This section provides tutorials for using Raiden in custom setups. 5 | You can either read about how to :doc:`run Raiden on a private Ethereum network ` or how to :doc:`create a custom token ` for using it with Raiden in a testnet. 6 | 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | 11 | custom-setups/private_net_tutorial 12 | custom-setups/custom_token 13 | -------------------------------------------------------------------------------- /docs/getting_started.rst: -------------------------------------------------------------------------------- 1 | Getting Started 2 | ############### 3 | 4 | Now that your Raiden node is up and running, you can choose from the following tutorials, depending on your Raiden use case: 5 | 6 | See :doc:`Using Raiden on Mainnet ` for getting started to use Raiden on the Ethereum mainnet. 7 | The :doc:`Raiden Web Interface ` is the easiest way to use Raiden interactively. 8 | For programmatic use of Raiden, see :doc:`The Raiden API Tutorial `. 9 | 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | Using Raiden on Mainnet Guide 15 | Raiden Web Interface Tutorial 16 | Raiden API Tutorial 17 | 18 | -------------------------------------------------------------------------------- /docs/images/architecture_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/images/architecture_overview.png -------------------------------------------------------------------------------- /docs/images/channelsgraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/images/channelsgraph.png -------------------------------------------------------------------------------- /docs/images/merkletree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/images/merkletree.png -------------------------------------------------------------------------------- /docs/images/pending_transfers_for_mediator.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/images/pending_transfers_for_mediator.gif -------------------------------------------------------------------------------- /docs/images/raiden_webui_channels_page_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/images/raiden_webui_channels_page_screenshot.png -------------------------------------------------------------------------------- /docs/images/raiden_webui_landing_page_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/images/raiden_webui_landing_page_screenshot.png -------------------------------------------------------------------------------- /docs/images/raiden_webui_tokens_page_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/images/raiden_webui_tokens_page_screenshot.png -------------------------------------------------------------------------------- /docs/images/restore_wal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/images/restore_wal.png -------------------------------------------------------------------------------- /docs/images/routing_a_transfer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/images/routing_a_transfer.gif -------------------------------------------------------------------------------- /docs/images/settle_pending_locks.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/images/settle_pending_locks.gif -------------------------------------------------------------------------------- /docs/images/state_tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/images/state_tasks.png -------------------------------------------------------------------------------- /docs/images/transfer_happy_case.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/images/transfer_happy_case.gif -------------------------------------------------------------------------------- /docs/images/transfer_unhappy_case.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/images/transfer_unhappy_case.gif -------------------------------------------------------------------------------- /docs/images/write_state_change_to_wal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/images/write_state_change_to_wal.png -------------------------------------------------------------------------------- /docs/nix.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | .. _nix_development_setup: 4 | 5 | Dev environment with nix and direnv 6 | =================================== 7 | 8 | Install nix 9 | ----------- 10 | 11 | Install the ``nix`` package manager, by running 12 | 13 | :: 14 | 15 | curl https://nixos.org/nix/install | sh 16 | 17 | or better, verify integrity of the installer script first according to 18 | https://nixos.org/nix/download.html. 19 | 20 | If you would like to set up python manually run 21 | 22 | :: 23 | 24 | nix-shell nix/shell.nix 25 | 26 | from the project root and ``nix`` will install all system dependencies 27 | and make them available in your current shell. Then continue with the 28 | :ref:`Installation from source ` section. 29 | 30 | You can also let `direnv `__ take care of 31 | activating the ``nix`` shell and a python virtual environment for you. 32 | 33 | Install direnv 34 | -------------- 35 | 36 | Install ``direnv`` for instance by running 37 | 38 | :: 39 | 40 | nix-env -iA nixpkgs.direnv 41 | 42 | Hook it into your shell 43 | 44 | :: 45 | 46 | eval "$(direnv hook bash)" # for bash 47 | eval "$(direnv hook zsh)" # for zsh 48 | eval (direnv hook fish) # for fish 49 | 50 | and add that line into ``~/.bashrc`` (or equivalent) for future use if 51 | desired. 52 | 53 | Activate direnv 54 | --------------- 55 | 56 | Run 57 | 58 | :: 59 | 60 | ln -s nix/envrc-nix-python .envrc 61 | direnv allow 62 | 63 | which will 64 | 65 | 1. Fetch all system dependencies with ``nix`` if necessary. 66 | 2. Activate the ``nix`` shell. 67 | 3. Create a python virtualenv for the project if necessary. 68 | 69 | Now each time you ``cd`` into this directory, ``direnv`` will activate 70 | this environment. 71 | 72 | Run 73 | 74 | :: 75 | 76 | make install-dev 77 | 78 | to install the dependencies in ``requirements-dev.txt`` and ``raiden`` 79 | itself. 80 | -------------------------------------------------------------------------------- /docs/raiden-api-1/api-tutorial/3-deposit.inc.rst: -------------------------------------------------------------------------------- 1 | Deposit Tokens 2 | ============== 3 | 4 | If you opened a channel by yourself you most likely already deposited 5 | tokens when sending the request. However, in case someone opened a 6 | channel with you, you will not have any tokens at your end of the 7 | channel. 8 | 9 | You need to make a deposit whenever you want to: 10 | 11 | - Add tokens to an empty channel 12 | - Top up the amount of tokens in a channel 13 | 14 | Deposit into a channel 15 | ---------------------- 16 | 17 | The :ref:`channel ` endpoint 18 | together with a PATCH request is used for depositing tokens into a 19 | channel. When making the call you need to include: 20 | 21 | 1. The token address as a path parameter. 22 | 2. The address of the partner node as a path parameter. 23 | 3. The amount of tokens you want to deposit in the channel as a body 24 | parameter. 25 | 26 | .. code:: bash 27 | 28 | curl -i -X PATCH \ 29 | http://localhost:5001/api/v1/channels/0x9aBa529db3FF2D8409A1da4C9eB148879b046700/0x61C808D82A3Ac53231750daDc13c777b59310bD9 \ 30 | -H 'Content-Type: application/json' \ 31 | --data-raw '{"total_deposit": "7331"}' 32 | 33 | This will give you the same response object as when :ref:`opening a 34 | channel ` or :ref:`querying the state 35 | of a channel `. 36 | 37 | Now, once there are tokens at your end of the channel, let's start 38 | making payments. 39 | -------------------------------------------------------------------------------- /docs/raiden-api-1/api-tutorial/README.rst: -------------------------------------------------------------------------------- 1 | API Tutorial 2 | ############ 3 | 4 | This tutorial will show you how to use Raiden via the API by walking you through a few API calls. 5 | The steps are similar to the WebUI tutorial, but directly use the API endpoints. 6 | We will outline the steps necessary for participating in a token 7 | network. To see all available endpoints visit the 8 | :ref:`resources ` part of the documentation. 9 | 10 | In the examples throughout this tutorial we will be using a hypothetical 11 | ERC20 token with the address ``0x9aBa529db3FF2D8409A1da4C9eB148879b046700``. 12 | 13 | Check if Raiden was started correctly 14 | ===================================== 15 | 16 | Before we start we'll make sure that Raiden is running correctly. 17 | 18 | This gives us an opportunity to introduce the 19 | :ref:`address ` endpoint. 20 | 21 | .. code:: bash 22 | 23 | curl -i http://localhost:5001/api/v1/address 24 | 25 | Your Raiden node is up and running if the response returns the same 26 | address as the Ethereum address used for starting Raiden. 27 | 28 | You're now ready to start interacting with the different endpoints and 29 | learn how they can be used. 30 | 31 | .. include:: 1-join-a-token-network.inc.rst 32 | 33 | .. include:: 2-open-a-channel.inc.rst 34 | 35 | .. include:: 3-deposit.inc.rst 36 | 37 | .. include:: 3-make-a-payment.inc.rst 38 | 39 | .. include:: withdraw-tokens.inc.rst 40 | 41 | .. include:: 4-settle-payments-and-close-channels.inc.rst 42 | -------------------------------------------------------------------------------- /docs/raiden-api-1/api-tutorial/withdraw-tokens.inc.rst: -------------------------------------------------------------------------------- 1 | .. _withdraw-tokens: 2 | 3 | Withdraw Tokens 4 | =============== 5 | 6 | Similar to depositing, a PATCH call to the 7 | :ref:`channel ` endpoint is used 8 | for withdrawing with the difference that a value for ``total_withdraw`` 9 | is provided in the request body. 10 | 11 | So, whenever you'd like to make a withdraw your request needs to 12 | include: 13 | 14 | 1. The token address as a path parameter. 15 | 2. The address of the node you have the channel with as a path 16 | parameter. 17 | 3. The amount of tokens you want to withdraw as a body parameter. 18 | 19 | .. code:: bash 20 | 21 | curl -i -X PATCH \ 22 | http://localhost:5001/api/v1/channels/0x9aBa529db3FF2D8409A1da4C9eB148879b046700/0x61C808D82A3Ac53231750daDc13c777b59310bD9 \ 23 | -H 'Content-Type: application/json' \ 24 | --data-raw '{"total_withdraw": "200"}' 25 | 26 | In the next section we will take a look at how to withdraw all tokens 27 | for a channel and how to leave a token network completely. 28 | -------------------------------------------------------------------------------- /docs/raiden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/raiden.png -------------------------------------------------------------------------------- /docs/the-raiden-web-interface/add-more-tokens.inc.rst: -------------------------------------------------------------------------------- 1 | Add More Tokens 2 | =============== 3 | 4 | At some point, you might need to top up your channels with tokens to be able to send more payments. 5 | You can allocate more tokens by depositing to a channel. 6 | 7 | Deposit to a channel 8 | -------------------- 9 | 10 | Depositing tokens to a specific channel lets you allocate more funds to 11 | that channel. You can compare this to "topping up" a prepaid card. 12 | 13 | In the **"Channels"** screen: 14 | 15 | 1. Click the three dots on the channel where you want to make the 16 | deposit and choose **"Deposit"**. 17 | 2. Enter an amount. 18 | 3. Click **"Deposit"**. 19 | 20 | The amount you choose will be subtracted from your on-chain token 21 | balance and added to the channel balance. 22 | -------------------------------------------------------------------------------- /docs/the-raiden-web-interface/payment.inc.rst: -------------------------------------------------------------------------------- 1 | .. _webui_payment: 2 | 3 | Make a Payment 4 | ============== 5 | 6 | Payment channels allow for Raiden transfers to be made without the need to involve the actual blockchain. 7 | If you have one or more channels open you can start making payments. If 8 | you used :ref:`Quick Connect ` three 9 | channels were automatically opened with three nodes. 10 | 11 | Let's walk through how to make a transfer. 12 | 13 | Transfer 14 | -------- 15 | 16 | After selecting a token network with open channels. Click on the 17 | **"Transfer"** button, this will bring open the *transfer dialog*. 18 | 19 | 1. If you accessed the transfer dialog from the *all networks view*, you 20 | can select the token network you want to make payments in also here. 21 | 2. Enter the receiving address. 22 | 3. Enter the amount. 23 | 4. Click **"Send"** to complete the transfer. 24 | 25 | .. note:: 26 | 27 | **Minting tokens** 28 | 29 | When running Raiden on a testnet the WebUI lets you mint tokens to fund 30 | your account with additional tokens. 31 | 32 | To mint tokens, click the three dots on the top right of the token 33 | network view and choose **"Mint"**. 34 | 35 | Minting tokens is an on-chain transaction that will consume some of your 36 | ETH. 37 | 38 | .. note:: 39 | 40 | **How do payment channels work?** 41 | 42 | Payment channels enable parties to exchange tokens off-chain without 43 | involving the blockchain for every transaction. 44 | 45 | This helps to avoid the blockchain consensus bottleneck and makes 46 | transfers both faster and cheaper. 47 | 48 | Raiden lets users pay anyone in the network by using a path of connected 49 | payment channels to mediate payments. This means that *a user don't need 50 | to open channels with every node they want to send payments to*. 51 | 52 | A payment channel in Raiden is always backed by an on-chain deposit of 53 | tokens. Depositing tokens or opening payment channels are on-chain 54 | transactions which require transaction fees (gas) that are paid in ETH. 55 | -------------------------------------------------------------------------------- /docs/the-raiden-web-interface/the-raiden-web-interface.rst: -------------------------------------------------------------------------------- 1 | Raiden Web Interface Tutorial 2 | ############################# 3 | 4 | The Raiden web interface (WebUI) helps you to interact with your Raiden node and aims to inspire to create own applications that utilize the Raiden REST API endpoints. 5 | 6 | .. image:: webui.gif 7 | 8 | Run the Raiden web application 9 | ============================== 10 | 11 | To run the WebUI you need to install Raiden and set up a Raiden node. 12 | The easiest way to do so is by using the :doc:`quick start guide <../installation/quick-start/README>`. 13 | 14 | **Once you have a Raiden node up and running the WebUI will be available 15 | on** `http://localhost:5001 `__\ **.** 16 | 17 | .. include:: screens.inc.rst 18 | 19 | .. include:: user-deposit.inc.rst 20 | 21 | .. include:: join-a-token-network.inc.rst 22 | 23 | .. include:: payment.inc.rst 24 | 25 | .. include:: add-more-tokens.inc.rst 26 | 27 | .. include:: close-channels-and-settle-payments.inc.rst 28 | -------------------------------------------------------------------------------- /docs/the-raiden-web-interface/user-deposit.inc.rst: -------------------------------------------------------------------------------- 1 | .. _webui_udc: 2 | 3 | Manage UDC funds 4 | ================ 5 | 6 | On the top bar you can click the button next to the **"UDC Deposit"** to open a dialog for managing your funds in the User Deposit Contract. 7 | This dialog allows you to perform deposits and withdrawals. 8 | It will show you the amount of Raiden Network Tokens (RDN) you have on-chain, which are necessary for deposits. 9 | 10 | For a deposit, click the **"Deposit"** button and enter the amount of RDN tokens you would like to have in the UDC. 11 | This will trigger all necessary on-chain transactions. 12 | 13 | For a withdrawal, you first need to click **"Plan Withdrawal"**. 14 | After entering the amount of tokens you would like to withdraw, you have to wait for at least 100 blocks on-chain before you can perform the actual withdrawal. 15 | When these 100 blocks have passed, a **"Withdraw"** button will show up. 16 | Clicking it will trigger another on-chain transaction. 17 | 18 | .. note:: 19 | 20 | **What is the User Deposit Contract (UDC)?** 21 | 22 | The UDC is a smart contract where you deposit RDN (Raiden Network Tokens) for later paying the :doc:`Raiden Services <../raiden_services>`. 23 | For sending a :ref:`mediated transfer ` it is even necessary to have RDN tokens in the UDC. 24 | 25 | -------------------------------------------------------------------------------- /docs/the-raiden-web-interface/webui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/docs/the-raiden-web-interface/webui.gif -------------------------------------------------------------------------------- /docs/trademarks.rst: -------------------------------------------------------------------------------- 1 | Trademark Attributions 2 | ########################## 3 | 4 | 5 | - MacOS® is a trademark of Apple Inc., registered in the U.S. and other countries. 6 | - Linux® is the registered trademark of Linus Torvalds in the U.S. and other countries. 7 | -------------------------------------------------------------------------------- /docs/using-raiden-on-mainnet/channel-status.inc.rst: -------------------------------------------------------------------------------- 1 | View Channel Status 2 | =================== 3 | 4 | To view the status of a channel you have to make a GET request to the 5 | :ref:`channels endpoint ` and provide: 6 | 7 | 1. The address of the W-ETH token as a path parameter. 8 | 2. The address of the partner node as a path parameter. 9 | 10 | .. code:: text 11 | 12 | curl -i \ 13 | http://localhost:5001/api/v1/channels/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C 14 | 15 | This will return the following response object: 16 | 17 | .. code:: text 18 | 19 | HTTP/1.1 201 CREATED 20 | Content-Type: application/json 21 | 22 | { 23 | "token_network_address": "0xE5637F0103794C7e05469A9964E4563089a5E6f2", 24 | "channel_identifier": "0xa24f51685de3effe829f7c2e94b9db8e9e1b17b137da5", 25 | "network_state": "unknown", 26 | "partner_address": "0x61C808D82A3Ac53231750daDc13c777b59310bD9", 27 | "token_address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", 28 | "balance": "3958", 29 | "state": "open", 30 | "settle_timeout": "500", 31 | "reveal_timeout": "50" 32 | } 33 | 34 | As you can tell by the response, the current balance in the channel is 35 | ``3958`` which matches the two deposits and one payment you've just made 36 | (``2000`` + ``2000`` - ``42`` = ``3958`` ). 37 | -------------------------------------------------------------------------------- /docs/using-raiden-on-mainnet/deposit-tokens-to-the-udc.inc.rst: -------------------------------------------------------------------------------- 1 | .. _mainnet_tutorial_deposit_udc: 2 | 3 | Deposit Tokens to the UDC 4 | ========================= 5 | 6 | To do :ref:`mediated transfers ` we need to have RDN (Raiden Network Tokens) in the UDC (User Deposit Contract) for paying the monitoring and pathfinding services. 7 | This section will describe how to add Raiden Network Tokens to the UDC by making a call to the :ref:`User Deposit endpoint ` of the Raiden API. 8 | The following POST request will deposit 100 RDN tokens to the UDC: 9 | 10 | .. code:: bash 11 | 12 | curl -i -X POST \ 13 | http://localhost:5001/api/v1/user_deposit \ 14 | -H 'Content-Type: application/json' \ 15 | --data-raw '{"total_deposit": "100000000000000000000"}' 16 | 17 | .. note:: 18 | Raiden utilizes a RESTful API where all URL paths starts with ``/api/`` followed by a version number. The current API version is ``1`` and therefore all requests begins with ``/api/v1/``. 19 | 20 | The request will take a couple of minutes, because two on-chain transactions are performed: Approving the UDC to use RDN and Depositing RDN to the UDC. 21 | When successfully completed the API will respond with a transaction hash. 22 | -------------------------------------------------------------------------------- /docs/using-raiden-on-mainnet/deposit-tokens.inc.rst: -------------------------------------------------------------------------------- 1 | Deposit Tokens 2 | ============== 3 | 4 | You deposit in a channel by making a PATCH request to the 5 | :ref:`channels ` 6 | endpoint that includes: 7 | 8 | 1. The address of the W-ETH token as a path parameter. 9 | 2. The address of the partner node as a path parameter. 10 | 3. The amount of tokens you want to deposit in the channel as a body 11 | parameter. 12 | 13 | .. code:: text 14 | 15 | curl -i -X PATCH \ 16 | http://localhost:5001/api/v1/channels/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/ \ 17 | -H 'Content-Type: application/json' \ 18 | --data-raw '{"total_deposit":"4000"}' 19 | 20 | .. note:: 21 | 22 | Notice how we are specifying the ``total_deposit`` in the request and not only the amount we want to "top-up" with. This means that if we initially deposited 2000 WEI and want to increase the amount with 2000 we would need to make a total deposit of 4000 WEI. 23 | 24 | This is done to prevent requests that are sent repeatedly (by accident or as part of an attack) from having any further consequences. 25 | 26 | -------------------------------------------------------------------------------- /docs/using-raiden-on-mainnet/make-a-payment.inc.rst: -------------------------------------------------------------------------------- 1 | Make a Payment 2 | ============== 3 | 4 | Payments are made from the 5 | :ref:`payments ` 6 | endpoint via a POST request that has to include: 7 | 8 | 1. The address of the W-ETH token as a path parameter. 9 | 2. The address of the node receiving your payment as a path parameter. 10 | 3. The amount you would like to pay as a body parameter. 11 | 12 | .. code:: text 13 | 14 | curl -i -X POST \ 15 | http://localhost:5001/api/v1/payments/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/ \ 16 | -H 'Content-Type: application/json' \ 17 | --data-raw '{"amount":"42"}' 18 | 19 | You will receive a success response once the payment goes through. 20 | 21 | Payments in Raiden can be done either as: 22 | 23 | - Direct payments 24 | - Mediated payments 25 | 26 | .. note:: 27 | 28 | It is possible to receive tokens through the Raiden Network without having any initial Ether or tokens. The receiving peer just needs to have a Raiden full node running and the sender must be willing to pay the fees for the on-chain transactions. 29 | 30 | 31 | Direct payments 32 | --------------- 33 | 34 | You can open a channel directly with any node you know the address of. 35 | You might want to open a direct channel if you know you'll be making 36 | frequent payments to a specific peer. Each payment will then go straight 37 | to that peer via the open channel. This is called a direct payment. 38 | 39 | .. _mainnet_tutorial_mediated_payments: 40 | 41 | Mediated payments 42 | ----------------- 43 | 44 | If you don't have a direct channel to a node for whom you want to send a 45 | payment, Raiden will try to find a path from you to the recipient node 46 | through the network of channels. This is called mediated payments. 47 | 48 | Mediated payments are a powerful feature in Raiden since it lets you pay 49 | anyone in the network by leverging the path of connected payment 50 | channels. 51 | 52 | By making no more than two API calls - one call to join the token 53 | network and one call to make the payment - we can send payments to 54 | anyone who is part of the W-ETH token network. 55 | -------------------------------------------------------------------------------- /docs/using-raiden-on-mainnet/overview.rst: -------------------------------------------------------------------------------- 1 | Using Raiden on Mainnet 2 | ####################### 3 | 4 | .. warning:: 5 | Before you start, make sure Raiden is 6 | correctly installed and configured by following the :doc:`installation 7 | instructions <../overview_and_guide>`. 8 | 9 | Also make sure you have read and understood the :doc:`safe usage 10 | guidelines <../other/safe-usage>` before starting to use Raiden. 11 | 12 | 13 | .. include:: whitelisted-tokens-1.inc.rst 14 | 15 | .. include:: deposit-tokens-to-the-udc.inc.rst 16 | 17 | .. include:: join-a-token-network-1.inc.rst 18 | 19 | .. include:: make-a-payment.inc.rst 20 | 21 | .. include:: deposit-tokens.inc.rst 22 | 23 | .. include:: channel-status.inc.rst 24 | 25 | .. include:: mediation-fees.inc.rst 26 | -------------------------------------------------------------------------------- /docs/using-raiden-on-mainnet/whitelisted-tokens-1.inc.rst: -------------------------------------------------------------------------------- 1 | Get Whitelisted Tokens 2 | ====================== 3 | 4 | The Raiden Coruscant release puts a certain 5 | limit on the amount of tokens that can be deposited into a channel. This 6 | is to minimize potential loss of funds in case of bugs. 7 | 8 | - The global limit per token network is approximately **$5000000**. 9 | - The limit per channel side is approximately **$5000**. 10 | 11 | .. note:: 12 | 13 | These dollar values of these limits are calculated at contract deployment 14 | time and vary with the exchange rates of the underlying tokens. 15 | 16 | W-ETH 17 | ----- 18 | 19 | W-ETH stands for *Wrapped Ether* and is essentially Ether that has been 20 | packaged to become ERC-20 compatible. 21 | 22 | There are several ways in which you can get W-ETH from your Ether. 23 | 24 | - Using the 25 | `contract `__ 26 | directly 27 | - Trade the token on an exchange, e.g. `Uniswap `__ 28 | 29 | DAI 30 | --- 31 | 32 | DAI is a popular stablecoin. Visit `Maker 33 | DAO `__ for further information on how to 34 | purchase it. 35 | -------------------------------------------------------------------------------- /pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | jobs=4 3 | persistent=yes 4 | suggestion-mode=yes 5 | unsafe-load-any-extension=no 6 | load-plugins=tools.pylint.gevent_checker,tools.pylint.assert_checker,tools.pylint.del_method_checker 7 | 8 | # Blacklist files or directories (basenames, not paths) 9 | ignore= 10 | 11 | # blacklist files or directories by regex (basenames, not paths) 12 | ignore-patterns= 13 | 14 | [EXCEPTIONS] 15 | 16 | overgeneral-exceptions=Exception,RaidenUnrecoverableError 17 | 18 | [BASIC] 19 | 20 | bad-names=foo,bar,baz,toto,tutu,tata 21 | good-names=i,j,k,_,log 22 | 23 | [LOGGING] 24 | 25 | logging-modules=logging,structlog 26 | 27 | [MESSAGES CONTROL] 28 | 29 | disable=all 30 | enable= 31 | assert-message, 32 | bad-except-order, 33 | del-method-used, 34 | exception-in-del, 35 | expression-not-assigned, 36 | gevent-joinall-raise-error, 37 | gevent-disable-wait, 38 | gevent-group-join, 39 | gevent-input-forbidden, 40 | gevent-sys-stdfds-forbidden, 41 | import-self, 42 | inconsistent-return-statements, 43 | no-member, 44 | no-self-use, 45 | no-value-for-parameter, 46 | pointless-statement, 47 | redefined-builtin, 48 | reimported, 49 | too-many-format-args, 50 | too-many-function-args, 51 | unexpected-keyword-arg, 52 | unused-argument, 53 | unused-import, 54 | unused-variable, 55 | useless-object-inheritance, 56 | 57 | [REPORTS] 58 | 59 | reports=no 60 | score=no 61 | 62 | [FORMAT] 63 | 64 | expected-line-ending-format=LF 65 | ignore-long-lines=^\s*(# )??$ 66 | max-line-length=100 67 | max-module-lines=1000 68 | no-space-check=trailing-comma 69 | 70 | [VARIABLES] 71 | 72 | callbacks=cb_,_cb 73 | dummy-variables-rgx=_ 74 | ignored-argument-names=_.* 75 | 76 | [TYPECHECK] 77 | 78 | contextmanager-decorators=contextlib.contextmanager 79 | 80 | # List of class names for which member attributes should not be checked 81 | ignored-classes= 82 | 83 | # List of module names for which member attributes should not be checked 84 | ignored-modules= 85 | -------------------------------------------------------------------------------- /raiden/__init__.py: -------------------------------------------------------------------------------- 1 | from . import _monkey 2 | 3 | _monkey.patch_all() 4 | del _monkey 5 | -------------------------------------------------------------------------------- /raiden/__main__.py: -------------------------------------------------------------------------------- 1 | # make it possible to run raiden with 'python -m raiden' 2 | 3 | 4 | def main() -> None: 5 | import gevent.monkey 6 | 7 | gevent.monkey.patch_all() 8 | 9 | from raiden.network.transport.matrix.rtc.utils import setup_asyncio_event_loop 10 | 11 | setup_asyncio_event_loop() 12 | 13 | from raiden.ui.cli import run 14 | 15 | # auto_envvar_prefix on a @click.command will cause all options to be 16 | # available also through environment variables prefixed with given prefix 17 | # http://click.pocoo.org/6/options/#values-from-environment-variables 18 | run(auto_envvar_prefix="RAIDEN") # pylint: disable=no-value-for-parameter 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /raiden/_monkey.py: -------------------------------------------------------------------------------- 1 | import asyncio.selector_events 2 | 3 | import aioice.ice 4 | 5 | 6 | def _patch_aioice() -> None: 7 | # local_candidate could be None so make sure we check for it before 8 | # invoking data_received 9 | def StunProtocol__connection_lost(self, exc: Exception) -> None: # type: ignore 10 | self._StunProtocol__log_debug("connection_lost(%s)", exc) 11 | if self.local_candidate is not None: 12 | self.receiver.data_received(None, self.local_candidate.component) 13 | self._StunProtocol__closed.set_result(True) 14 | 15 | aioice.ice.StunProtocol.connection_lost = StunProtocol__connection_lost # type: ignore 16 | 17 | 18 | def _patch_asyncio() -> None: 19 | # don't do anything if the transport is closing/closed 20 | def wrap(orig): # type: ignore 21 | def sendto(self, data, addr=None): # type: ignore 22 | if not self._closing: 23 | orig(self, data, addr) 24 | 25 | return sendto 26 | 27 | orig = asyncio.selector_events._SelectorDatagramTransport.sendto # type: ignore 28 | asyncio.selector_events._SelectorDatagramTransport.sendto = wrap(orig) # type: ignore 29 | 30 | 31 | def patch_all() -> None: 32 | _patch_aioice() 33 | _patch_asyncio() 34 | -------------------------------------------------------------------------------- /raiden/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/api/__init__.py -------------------------------------------------------------------------------- /raiden/api/exceptions.py: -------------------------------------------------------------------------------- 1 | from raiden.exceptions import RaidenValidationError 2 | 3 | 4 | class ChannelNotFound(RaidenValidationError): 5 | """Raised when a provided channel via the REST api is not found in the 6 | internal data structures. 7 | """ 8 | 9 | 10 | class NonexistingChannel(RaidenValidationError): 11 | """The requested channel does not exist. 12 | 13 | This exception can be raised for a few reasons: 14 | 15 | - The user request raced and lost against a transaction to close/settle the 16 | channel. 17 | - The user provided invalid values, and the given channel does not exist. 18 | """ 19 | -------------------------------------------------------------------------------- /raiden/api/objects.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from raiden.utils.typing import List, TokenAddress 3 | 4 | 5 | class FlatList(list): 6 | """ 7 | This class inherits from list and has the same interface as a list-type. 8 | However, there is a 'data'-attribute introduced, that is required for the encoding of the list! 9 | The fields of the encoding-Schema must match the fields of the Object to be encoded! 10 | """ 11 | 12 | @property 13 | def data(self) -> List: 14 | return list(self) 15 | 16 | def __repr__(self) -> str: 17 | return "<{}: {}>".format(self.__class__.__name__, list(self)) 18 | 19 | 20 | class AddressList(FlatList): 21 | pass 22 | 23 | 24 | class PartnersPerTokenList(FlatList): 25 | pass 26 | 27 | 28 | class Address: 29 | def __init__(self, token_address: TokenAddress) -> None: 30 | self.address = token_address 31 | 32 | 33 | class PartnersPerToken: 34 | def __init__(self, partner_address: Address, channel: str) -> None: 35 | self.partner_address = partner_address 36 | self.channel = channel 37 | 38 | 39 | @dataclass 40 | class Notification: 41 | id: str 42 | summary: str 43 | body: str 44 | urgency: str 45 | -------------------------------------------------------------------------------- /raiden/api/rest_utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | from http import HTTPStatus 3 | 4 | import structlog 5 | from flask import Response, make_response 6 | 7 | from raiden.utils.typing import Any, Callable 8 | 9 | log = structlog.get_logger(__name__) 10 | 11 | 12 | ERROR_STATUS_CODES = [ 13 | HTTPStatus.FORBIDDEN, 14 | HTTPStatus.CONFLICT, 15 | HTTPStatus.PAYMENT_REQUIRED, 16 | HTTPStatus.BAD_REQUEST, 17 | HTTPStatus.NOT_FOUND, 18 | HTTPStatus.NOT_IMPLEMENTED, 19 | HTTPStatus.INTERNAL_SERVER_ERROR, 20 | HTTPStatus.SERVICE_UNAVAILABLE, 21 | ] 22 | 23 | 24 | def api_response(result: Any, status_code: HTTPStatus = HTTPStatus.OK) -> Response: 25 | if status_code == HTTPStatus.NO_CONTENT: 26 | assert not result, "Provided 204 response with non-zero length response" 27 | data = "" 28 | else: 29 | data = json.dumps(result) 30 | 31 | log.debug("Request successful", response=result, status_code=status_code) 32 | response = make_response( 33 | (data, status_code, {"mimetype": "application/json", "Content-Type": "application/json"}) 34 | ) 35 | return response 36 | 37 | 38 | def api_error(errors: Any, status_code: HTTPStatus) -> Response: 39 | assert status_code in ERROR_STATUS_CODES, "Programming error, unexpected error status code" 40 | log.error("Error processing request", errors=errors, status_code=status_code) 41 | response = make_response( 42 | ( 43 | json.dumps(dict(errors=errors)), 44 | status_code, 45 | {"mimetype": "application/json", "Content-Type": "application/json"}, 46 | ) 47 | ) 48 | return response 49 | 50 | 51 | def if_api_available(method: Callable) -> Callable: 52 | """Decorator for resource methods which only work if the API is fully available.""" 53 | 54 | def decorated(self, *args, **kwargs): # type: ignore 55 | if not self.rest_api.available: 56 | msg = "Service unavailable. Try again later." 57 | return api_error(msg, HTTPStatus.SERVICE_UNAVAILABLE) 58 | 59 | return method(self, *args, **kwargs) 60 | 61 | return decorated 62 | -------------------------------------------------------------------------------- /raiden/api/v1/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/api/v1/__init__.py -------------------------------------------------------------------------------- /raiden/blockchain/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/blockchain/__init__.py -------------------------------------------------------------------------------- /raiden/blockchain/exceptions.py: -------------------------------------------------------------------------------- 1 | from raiden.exceptions import RaidenRecoverableError, RaidenUnrecoverableError 2 | 3 | 4 | class UnknownRaidenEventType(RaidenUnrecoverableError): 5 | """Raised if decoding an event from a Raiden smart contract failed. 6 | 7 | Deserializing an event from one of the Raiden smart contracts fails may 8 | happen for a few reasons: 9 | 10 | - The address is not a Raiden smart contract. 11 | - The address is for a newer version of the Raiden's smart contracts with 12 | an unknown event. 13 | 14 | Either case, it means the node will not be properly synchronized with the 15 | on-chain state, and this cannot be recovered from. 16 | """ 17 | 18 | 19 | class EthGetLogsTimeout(RaidenRecoverableError): 20 | """Raised when an eth.get_logs RPC call caused a ReadTimeout exception. 21 | 22 | It is used to automatically tune the block batching size. 23 | """ 24 | 25 | 26 | class BlockBatchSizeTooSmall(RaidenUnrecoverableError): 27 | """Raised when the block batch size would have to be reduced below the minimum allowed value. 28 | 29 | This is an unrecoverable error since it indicates that either the connected Eth-node or the 30 | network connection is not capable of supporting minimum performance requirements for the 31 | eth.get_logs call. 32 | """ 33 | -------------------------------------------------------------------------------- /raiden/messages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/messages/__init__.py -------------------------------------------------------------------------------- /raiden/messages/cmdid.py: -------------------------------------------------------------------------------- 1 | import enum 2 | 3 | 4 | @enum.unique 5 | class CmdId(enum.Enum): 6 | """Identifier for off-chain messages. 7 | 8 | These magic numbers are used to identify the type of a message. 9 | """ 10 | 11 | PROCESSED = 0 12 | PING = 1 13 | PONG = 2 14 | SECRETREQUEST = 3 15 | UNLOCK = 4 16 | LOCKEDTRANSFER = 7 17 | REFUNDTRANSFER = 8 18 | REVEALSECRET = 11 19 | DELIVERED = 12 20 | LOCKEXPIRED = 13 21 | WITHDRAW_REQUEST = 15 22 | WITHDRAW_CONFIRMATION = 16 23 | WITHDRAW_EXPIRED = 17 24 | -------------------------------------------------------------------------------- /raiden/messages/encode.py: -------------------------------------------------------------------------------- 1 | from raiden.messages.abstract import Message 2 | from raiden.messages.synchronization import Processed 3 | from raiden.messages.transfers import ( 4 | LockedTransfer, 5 | LockExpired, 6 | RevealSecret, 7 | SecretRequest, 8 | Unlock, 9 | ) 10 | from raiden.messages.withdraw import WithdrawConfirmation, WithdrawExpired, WithdrawRequest 11 | from raiden.transfer.architecture import SendMessageEvent 12 | from raiden.transfer.events import ( 13 | SendProcessed, 14 | SendWithdrawConfirmation, 15 | SendWithdrawExpired, 16 | SendWithdrawRequest, 17 | ) 18 | from raiden.transfer.mediated_transfer.events import ( 19 | SendLockedTransfer, 20 | SendLockExpired, 21 | SendSecretRequest, 22 | SendSecretReveal, 23 | SendUnlock, 24 | ) 25 | 26 | 27 | _EVENT_MAP = { 28 | SendLockExpired: LockExpired, 29 | SendLockedTransfer: LockedTransfer, 30 | SendProcessed: Processed, 31 | SendSecretRequest: SecretRequest, 32 | SendSecretReveal: RevealSecret, 33 | SendUnlock: Unlock, 34 | SendWithdrawConfirmation: WithdrawConfirmation, 35 | SendWithdrawExpired: WithdrawExpired, 36 | SendWithdrawRequest: WithdrawRequest, 37 | } 38 | 39 | 40 | def message_from_sendevent(send_event: SendMessageEvent) -> Message: 41 | t_event = type(send_event) 42 | EventClass = _EVENT_MAP.get(t_event) 43 | if EventClass is None: 44 | raise ValueError(f"Unknown event type {t_event}") 45 | return EventClass.from_event(send_event) # type: ignore 46 | -------------------------------------------------------------------------------- /raiden/messages/healthcheck.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from raiden.messages.abstract import SignedMessage 4 | from raiden.messages.cmdid import CmdId 5 | from raiden.utils.typing import ClassVar, Nonce, RaidenProtocolVersion 6 | 7 | 8 | @dataclass(repr=False, eq=False) 9 | class Ping(SignedMessage): 10 | """Healthcheck message. 11 | 12 | This message is sent to another node with an unique nonce, a Pong response is 13 | expected. If the recipient takes too long to send a Pong back it is assumed 14 | the node is offline. 15 | 16 | If the transport requires, this message can also be used to keep a 17 | connection alive and preserve NAT mappings. 18 | """ 19 | 20 | cmdid: ClassVar[CmdId] = CmdId.PING 21 | 22 | nonce: Nonce 23 | current_protocol_version: RaidenProtocolVersion 24 | 25 | def _data_to_sign(self) -> bytes: 26 | return ( 27 | bytes([self.cmdid.value, 0, 0, 0]) 28 | + self.nonce.to_bytes(8, byteorder="big") 29 | + bytes([self.current_protocol_version]) 30 | ) 31 | 32 | 33 | @dataclass(repr=False, eq=False) 34 | class Pong(SignedMessage): 35 | """Response to a Ping message.""" 36 | 37 | cmdid: ClassVar[CmdId] = CmdId.PONG 38 | 39 | nonce: Nonce 40 | 41 | def _data_to_sign(self) -> bytes: 42 | return bytes([self.cmdid.value, 0, 0, 0]) + self.nonce.to_bytes(8, byteorder="big") 43 | -------------------------------------------------------------------------------- /raiden/network/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/network/__init__.py -------------------------------------------------------------------------------- /raiden/network/proxies/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/network/proxies/__init__.py -------------------------------------------------------------------------------- /raiden/network/proxies/exceptions.py: -------------------------------------------------------------------------------- 1 | from raiden.exceptions import RaidenRecoverableError 2 | 3 | 4 | class MintFailed(RaidenRecoverableError): 5 | """Raised if calling the mint function failed.""" 6 | 7 | pass 8 | -------------------------------------------------------------------------------- /raiden/network/proxies/metadata.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from raiden.utils.typing import ABI, Address, BlockNumber, GasMeasurements, Optional, TypeVar 4 | 5 | T = TypeVar("T") 6 | 7 | 8 | @dataclass 9 | class SmartContractMetadata: 10 | """Basic metadata of a smart contract.""" 11 | 12 | # If the user deployed the smart contract, the mined block is unknown. 13 | deployed_at: Optional[BlockNumber] 14 | 15 | # Value to use as `fromBlock` for filters. If the deployed block number is 16 | # know it must be used, otherwise a hard fork block number can be used as a 17 | # lower bound. 18 | # 19 | # The deployed_at must be used because querying for logs before the smart 20 | # contract is deployed has bad performance (see #3958), and values larger 21 | # than the deployed_at will potentially miss logs. 22 | filters_start_at: BlockNumber 23 | 24 | # Make this a generic once https://github.com/python/mypy/issues/7520 is fixed 25 | address: Address 26 | 27 | abi: ABI 28 | gas_measurements: GasMeasurements 29 | 30 | def __post_init__(self) -> None: 31 | is_filter_start_valid = ( 32 | self.deployed_at is None or self.deployed_at == self.filters_start_at 33 | ) 34 | if not is_filter_start_valid: 35 | raise ValueError( 36 | "The deployed_at is known, the filters should start at that exact block" 37 | ) 38 | -------------------------------------------------------------------------------- /raiden/network/proxies/one_to_n.py: -------------------------------------------------------------------------------- 1 | import structlog 2 | from eth_utils import is_binary_address 3 | 4 | from raiden.network.rpc.client import JSONRPCClient, check_address_has_code_handle_pruned_block 5 | from raiden.utils.typing import Address, BlockIdentifier, OneToNAddress 6 | from raiden_contracts.constants import CONTRACT_ONE_TO_N 7 | from raiden_contracts.contract_manager import ContractManager 8 | 9 | log = structlog.get_logger(__name__) 10 | 11 | 12 | class OneToN: 13 | def __init__( 14 | self, 15 | jsonrpc_client: JSONRPCClient, 16 | one_to_n_address: OneToNAddress, 17 | contract_manager: ContractManager, 18 | block_identifier: BlockIdentifier, 19 | ): 20 | if not is_binary_address(one_to_n_address): 21 | raise ValueError("Expected binary address for monitoring service") 22 | 23 | self.contract_manager = contract_manager 24 | check_address_has_code_handle_pruned_block( 25 | client=jsonrpc_client, 26 | address=Address(one_to_n_address), 27 | contract_name=CONTRACT_ONE_TO_N, 28 | given_block_identifier=block_identifier, 29 | ) 30 | 31 | proxy = jsonrpc_client.new_contract_proxy( 32 | abi=self.contract_manager.get_contract_abi(CONTRACT_ONE_TO_N), 33 | contract_address=Address(one_to_n_address), 34 | ) 35 | 36 | self.address = one_to_n_address 37 | self.proxy = proxy 38 | self.client = jsonrpc_client 39 | self.node_address = self.client.address 40 | -------------------------------------------------------------------------------- /raiden/network/proxies/utils.py: -------------------------------------------------------------------------------- 1 | from raiden.exceptions import RaidenUnrecoverableError 2 | from raiden.utils.formatting import format_block_id 3 | from raiden.utils.typing import BlockIdentifier, NoReturn 4 | 5 | 6 | def raise_on_call_returned_empty(given_block_identifier: BlockIdentifier) -> NoReturn: 7 | """Format a message and raise RaidenUnrecoverableError.""" 8 | # We know that the given address has code because this is checked 9 | # in the constructor 10 | msg = ( 11 | f"Either the given address is for a different smart contract, " 12 | f"or the contract was not yet deployed at the block " 13 | f"{format_block_id(given_block_identifier)}. Either way this call " 14 | f"should never have happened." 15 | ) 16 | raise RaidenUnrecoverableError(msg) 17 | -------------------------------------------------------------------------------- /raiden/network/resolver/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/network/resolver/__init__.py -------------------------------------------------------------------------------- /raiden/network/rpc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/network/rpc/__init__.py -------------------------------------------------------------------------------- /raiden/network/rpc/middleware.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | from cachetools import LRUCache 4 | from web3.gas_strategies.time_based import construct_time_based_gas_price_strategy 5 | from web3.middleware.cache import construct_simple_cache_middleware 6 | from web3.types import RPCEndpoint 7 | 8 | BLOCK_HASH_CACHE_RPC_WHITELIST = {RPCEndpoint("eth_getBlockByHash")} 9 | 10 | 11 | block_hash_cache_middleware = construct_simple_cache_middleware( 12 | # default sample size of gas price strategies is 120 13 | cache_class=functools.partial(LRUCache, 150), # type: ignore 14 | rpc_whitelist=BLOCK_HASH_CACHE_RPC_WHITELIST, 15 | ) 16 | 17 | faster_gas_price_strategy = construct_time_based_gas_price_strategy( 18 | max_wait_seconds=15, sample_size=120, probability=99 19 | ) 20 | -------------------------------------------------------------------------------- /raiden/network/transport/__init__.py: -------------------------------------------------------------------------------- 1 | from raiden.network.transport.matrix import MatrixTransport # NOQA 2 | from raiden.network.transport.matrix.transport import populate_services_addresses # NOQA 3 | -------------------------------------------------------------------------------- /raiden/network/transport/matrix/__init__.py: -------------------------------------------------------------------------------- 1 | from raiden.network.transport.matrix.transport import MatrixTransport, _RetryQueue # noqa 2 | from raiden.network.transport.matrix.utils import ( # noqa 3 | AddressReachability, 4 | UserPresence, 5 | login, 6 | make_client, 7 | sort_servers_closest, 8 | validate_userid_signature, 9 | ) 10 | -------------------------------------------------------------------------------- /raiden/network/transport/matrix/rtc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/network/transport/matrix/rtc/__init__.py -------------------------------------------------------------------------------- /raiden/network/transport/matrix/rtc/utils.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from asyncio import AbstractEventLoop 3 | 4 | import gevent 5 | import structlog 6 | from gevent.timeout import Timeout 7 | 8 | from raiden.exceptions import RaidenUnrecoverableError 9 | from raiden.network.transport.matrix.rtc import aiogevent 10 | from raiden.utils.typing import Type 11 | 12 | ASYNCIO_LOOP_RUNNING_TIMEOUT = 10 13 | 14 | log = structlog.get_logger(__name__) 15 | 16 | 17 | def setup_asyncio_event_loop( 18 | exception: Type[Exception] = RaidenUnrecoverableError, 19 | ) -> AbstractEventLoop: 20 | asyncio.set_event_loop_policy(aiogevent.EventLoopPolicy()) 21 | new_event_loop = asyncio.new_event_loop() 22 | gevent.spawn(new_event_loop.run_forever) 23 | gevent.sleep(0.05) 24 | if not new_event_loop.is_running(): 25 | log.debug("Asyncio loop not running yet. Waiting.") 26 | with Timeout(ASYNCIO_LOOP_RUNNING_TIMEOUT, exception): 27 | while not new_event_loop.is_running(): 28 | gevent.sleep(0.05) 29 | 30 | return new_event_loop 31 | -------------------------------------------------------------------------------- /raiden/network/transport/utils.py: -------------------------------------------------------------------------------- 1 | from typing import Iterator 2 | 3 | 4 | def timeout_exponential_backoff(retries: int, timeout: float, maximum: float) -> Iterator[float]: 5 | """Timeouts generator with an exponential backoff strategy. 6 | Timeouts start spaced by `timeout`, after `retries` exponentially increase 7 | the retry delays until `maximum`, then maximum is returned indefinitely. 8 | """ 9 | yield timeout 10 | 11 | tries = 1 12 | while tries < retries: 13 | tries += 1 14 | yield timeout 15 | 16 | while timeout < maximum: 17 | timeout = min(timeout * 2, maximum) 18 | yield timeout 19 | 20 | while True: 21 | yield maximum 22 | -------------------------------------------------------------------------------- /raiden/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/py.typed -------------------------------------------------------------------------------- /raiden/storage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/storage/__init__.py -------------------------------------------------------------------------------- /raiden/storage/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/storage/migrations/__init__.py -------------------------------------------------------------------------------- /raiden/storage/serialization/__init__.py: -------------------------------------------------------------------------------- 1 | from .serializer import DictSerializer, JSONSerializer, SerializationBase # noqa 2 | -------------------------------------------------------------------------------- /raiden/storage/versions.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import re 3 | from pathlib import Path 4 | 5 | from raiden.utils.typing import List, Optional 6 | 7 | VERSION_RE = re.compile(r"^v(\d+)_log[.]db$") 8 | 9 | 10 | def latest_db_file(paths: List[Path]) -> Optional[Path]: 11 | """Returns the path with the highest `version` number. 12 | 13 | Raises: 14 | AssertionError: If any of the `paths` in the list is an invalid name. 15 | 16 | Args: 17 | paths: A list of file names. 18 | """ 19 | dbs = {} 20 | for db_path in paths: 21 | matches = VERSION_RE.match(os.path.basename(db_path)) 22 | assert matches, f"Invalid path name {db_path}" 23 | 24 | try: 25 | version = int(matches.group(1)) 26 | except ValueError: 27 | continue 28 | 29 | dbs[version] = db_path 30 | 31 | if dbs: 32 | highest_version = sorted(dbs)[-1] 33 | return dbs[highest_version] 34 | 35 | return None 36 | 37 | 38 | def filter_db_names(paths: List[str]) -> List[Path]: 39 | """Returns a filtered list of `paths`, where every name matches our format. 40 | 41 | Args: 42 | paths: A list of file names. 43 | """ 44 | return [Path(db_path) for db_path in paths if VERSION_RE.match(os.path.basename(db_path))] 45 | -------------------------------------------------------------------------------- /raiden/tests/README.rst: -------------------------------------------------------------------------------- 1 | Running integration tests with different transport protocols 2 | ============================================================ 3 | -------------------------------------------------------------------------------- /raiden/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/__init__.py -------------------------------------------------------------------------------- /raiden/tests/benchmark/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/benchmark/__init__.py -------------------------------------------------------------------------------- /raiden/tests/benchmark/_codespeed.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import warnings 4 | 5 | import requests 6 | 7 | try: 8 | _CODESPEED_USER = os.environ["CODESPEED_USER"] 9 | 10 | _CODESPEED_PASSWORD = os.environ["CODESPEED_PASSWORD"] 11 | 12 | _BENCHMARK_HOST = os.environ["BENCHMARK_HOST"] 13 | except KeyError: 14 | warnings.warn( 15 | "Codespeed environment variables not available, posting results would fail.", 16 | RuntimeWarning, 17 | ) 18 | 19 | 20 | def post_result(codespeed_url, commit_id, branch, bench_name, value): 21 | data = [ 22 | { 23 | "commitid": commit_id, 24 | "project": "raiden", 25 | "branch": branch, 26 | "executable": "raiden", 27 | "benchmark": bench_name, 28 | "environment": _BENCHMARK_HOST, 29 | "result_value": value, 30 | } 31 | ] 32 | 33 | data_ = {"json": json.dumps(data)} 34 | url = codespeed_url + "/result/add/json/" 35 | resp = requests.post(url, data=data_, auth=(_CODESPEED_USER, _CODESPEED_PASSWORD)) 36 | resp.raise_for_status() 37 | -------------------------------------------------------------------------------- /raiden/tests/benchmark/utils.py: -------------------------------------------------------------------------------- 1 | def print_serialization(pstats): # pylint: disable=too-many-locals 2 | print("ncalls tottime percall % cumtime percall function") 3 | total_pct = 0.0 4 | 5 | for path_line_func, data in pstats.sort_stats("module", "cumulative").stats.items(): 6 | path, line, func = path_line_func # pylint: disable=unused-variable 7 | 8 | is_rlp = "rlp" in path 9 | is_encoding = "encoding" in path 10 | if is_rlp or is_encoding: 11 | # primitive calls dont count recursion 12 | # total calls count recursion 13 | # total time is the time for the function itself (excluding subcalls) 14 | # accumulated_time is the time of the function plus the subcalls 15 | primitive_calls, total_calls, total_time, acc_time, _ = data 16 | 17 | if primitive_calls != total_calls: 18 | calls = f"{total_calls}/{primitive_calls}" 19 | else: 20 | calls = str(primitive_calls) 21 | 22 | pct = (total_time / pstats.total_tt) * 100 23 | total_pct += pct 24 | print( 25 | "{:<14} {:<8.3f} {:<8.3f} {:<3.2f} {:<8.3f} {:<8.3f} {}".format( 26 | calls, 27 | total_time, 28 | total_time / total_calls, 29 | pct, 30 | acc_time, 31 | acc_time / total_calls, 32 | func, 33 | ) 34 | ) 35 | 36 | print(f" Runtime: {pstats.total_tt}, Total %: {total_pct}") 37 | 38 | 39 | def print_slow_path(pstats): 40 | pstats.strip_dirs().sort_stats("cumulative").print_stats(15) 41 | 42 | 43 | def print_slow_function(pstats): 44 | pstats.strip_dirs().sort_stats("time").print_stats(15) 45 | -------------------------------------------------------------------------------- /raiden/tests/fixtures/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/fixtures/__init__.py -------------------------------------------------------------------------------- /raiden/tests/fixtures/blockchain.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import pytest 4 | 5 | from raiden.settings import RAIDEN_CONTRACT_VERSION 6 | from raiden_contracts.contract_manager import ContractManager, contracts_precompiled_path 7 | 8 | __all__ = ("contracts_path", "contract_manager") 9 | 10 | 11 | @pytest.fixture 12 | def contracts_path() -> Path: 13 | version = RAIDEN_CONTRACT_VERSION 14 | return contracts_precompiled_path(version) 15 | 16 | 17 | @pytest.fixture 18 | def contract_manager(contracts_path): 19 | return ContractManager(contracts_path) 20 | -------------------------------------------------------------------------------- /raiden/tests/fixtures/constants.py: -------------------------------------------------------------------------------- 1 | from eth_utils import denoms 2 | 3 | from raiden.utils.typing import TokenAmount 4 | 5 | DEFAULT_PASSPHRASE = "notsosecret" # Geth's account passphrase 6 | DEFAULT_BALANCE = TokenAmount(denoms.ether * 10) # pylint: disable=no-member 7 | -------------------------------------------------------------------------------- /raiden/tests/fuzz/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/fuzz/__init__.py -------------------------------------------------------------------------------- /raiden/tests/fuzz/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.fixture(autouse=True) 5 | def override_capture_setting_for_hypothesis_tests(request): 6 | """override the general setting to see failed paths generated by Hypothesis""" 7 | request.config.option.showcapture = "all" 8 | -------------------------------------------------------------------------------- /raiden/tests/integration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/integration/__init__.py -------------------------------------------------------------------------------- /raiden/tests/integration/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/integration/api/__init__.py -------------------------------------------------------------------------------- /raiden/tests/integration/api/conftest.py: -------------------------------------------------------------------------------- 1 | from raiden.tests.integration.api.fixtures import * # noqa: F401,F403 2 | -------------------------------------------------------------------------------- /raiden/tests/integration/api/fixtures.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Generator 2 | 3 | import pytest 4 | 5 | from raiden.raiden_service import RaidenService 6 | from raiden.tests.integration.api.utils import prepare_api_server 7 | from raiden.utils.typing import List 8 | 9 | 10 | # TODO: Figure out why this fixture can't work as session scoped 11 | # What happens is that after one test is done, in the next one 12 | # the server is no longer running even though the teardown has not 13 | # been invoked. 14 | @pytest.fixture 15 | def api_server_test_instance(raiden_network: List[RaidenService]) -> Generator: 16 | api_server = prepare_api_server(raiden_network[0]) 17 | 18 | yield api_server 19 | 20 | 21 | @pytest.fixture 22 | def client(api_server_test_instance): 23 | with api_server_test_instance.flask_app.test_client() as client: 24 | yield client 25 | -------------------------------------------------------------------------------- /raiden/tests/integration/api/rest/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/integration/api/rest/__init__.py -------------------------------------------------------------------------------- /raiden/tests/integration/api/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import gevent 4 | import psutil 5 | 6 | from raiden.api.rest import APIServer 7 | from raiden.raiden_service import RaidenService 8 | from raiden.utils.typing import Port 9 | 10 | 11 | def wait_for_listening_port( 12 | port_number: Port, tries: int = 10, sleep: float = 0.1, pid: int = None 13 | ) -> None: 14 | if pid is None: 15 | pid = os.getpid() 16 | for _ in range(tries): 17 | gevent.sleep(sleep) 18 | # macoOS requires root access for the connections api to work 19 | # so get connections of the current process only 20 | connections = psutil.Process(pid).connections() 21 | for conn in connections: 22 | if conn.status == "LISTEN" and conn.laddr[1] == port_number: 23 | return 24 | 25 | raise RuntimeError(f"{port_number} is not bound") 26 | 27 | 28 | def prepare_api_server(raiden_app: RaidenService) -> APIServer: 29 | api_server = raiden_app.api_server 30 | if api_server is None: 31 | raise RuntimeError("REST API not enabled, enable it using the `enable_rest_api` fixture") 32 | 33 | assert api_server is not None 34 | config = raiden_app.config 35 | port = config.rest_api.port 36 | assert port is not None, "REST API port is `None`" 37 | 38 | # required for `url_for` 39 | api_server.flask_app.config["SERVER_NAME"] = f"localhost:{port}" 40 | 41 | # Fixes flaky test, where requests are done prior to the server initializing 42 | # the listening socket. 43 | # https://github.com/raiden-network/raiden/issues/389#issuecomment-305551563 44 | wait_for_listening_port(port) 45 | 46 | return api_server 47 | -------------------------------------------------------------------------------- /raiden/tests/integration/cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/integration/cli/__init__.py -------------------------------------------------------------------------------- /raiden/tests/integration/cli/test_cli_development.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from raiden.constants import Environment 4 | from raiden.settings import RAIDEN_CONTRACT_VERSION 5 | from raiden.tests.integration.cli.util import ( 6 | expect_cli_normal_startup, 7 | expect_cli_successful_connected, 8 | expect_cli_until_account_selection, 9 | ) 10 | 11 | pytestmark = [ 12 | pytest.mark.parametrize( 13 | "cli_tests_contracts_version", [RAIDEN_CONTRACT_VERSION], scope="module" 14 | ), 15 | pytest.mark.parametrize("environment_type", [Environment.DEVELOPMENT], scope="module"), 16 | ] 17 | 18 | 19 | def test_cli_full_init_dev(cli_args, raiden_spawner): 20 | child = raiden_spawner(cli_args) 21 | expect_cli_normal_startup(child, Environment.DEVELOPMENT.value) 22 | 23 | 24 | @pytest.mark.parametrize("removed_args", [["address"]]) 25 | def test_cli_manual_account_selection(cli_args, raiden_spawner): 26 | child = raiden_spawner(cli_args) 27 | expect_cli_until_account_selection(child) 28 | expect_cli_successful_connected(child, Environment.DEVELOPMENT.value) 29 | -------------------------------------------------------------------------------- /raiden/tests/integration/cli/util.py: -------------------------------------------------------------------------------- 1 | def expect_cli_until_acknowledgment(child): 2 | child.expect("Welcome to Raiden") 3 | child.expect("Have you read, understood and hereby accept the above") 4 | child.sendline("y") 5 | 6 | 7 | def expect_cli_until_account_selection(child): 8 | expect_cli_until_acknowledgment(child) 9 | child.expect("The following accounts were found in your machine:") 10 | child.expect("Select one of them by index to continue: ") 11 | child.sendline("0") 12 | 13 | 14 | def expect_cli_successful_connected(child, mode: str): 15 | child.expect(f"Raiden is running in {mode} mode") 16 | child.expect("You are connected") 17 | child.expect("REST API services now available.") 18 | 19 | 20 | def expect_cli_normal_startup(child, mode: str): 21 | expect_cli_until_acknowledgment(child) 22 | expect_cli_successful_connected(child, mode) 23 | -------------------------------------------------------------------------------- /raiden/tests/integration/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from raiden.tests.integration.fixtures.blockchain import * # noqa: F401,F403 4 | from raiden.tests.integration.fixtures.raiden_network import * # noqa: F401,F403 5 | from raiden.tests.integration.fixtures.smartcontracts import * # noqa: F401,F403 6 | from raiden.tests.integration.fixtures.transport import * # noqa: F401,F403 7 | 8 | 9 | def pytest_collection_modifyitems(items): 10 | """Use ``flaky`` to rerun tests failing with ``RetryTestError``""" 11 | # We don't want this in every test's namespace, so import locally 12 | from raiden.tests.integration.exception import RetryTestError 13 | 14 | for item in items: 15 | item.add_marker( 16 | pytest.mark.flaky( 17 | rerun_filter=lambda err, *args: issubclass(err[0], RetryTestError), max_runs=3 18 | ) 19 | ) 20 | item.add_marker(pytest.mark.asyncio) 21 | 22 | 23 | def pytest_configure(config): 24 | config.addinivalue_line("markers", "expect_failure") 25 | 26 | 27 | def pytest_collection_finish(session): 28 | def unsafe(item): 29 | has_nodes = "raiden_network" in item.fixturenames or "raiden_chain" in item.fixturenames 30 | is_secure = getattr(item.function, "_decorated_raise_on_failure", False) 31 | is_exempt = item.get_closest_marker("expect_failure") is not None 32 | return has_nodes and not (is_secure or is_exempt) 33 | 34 | unsafe_tests = [item.originalname or item.name for item in session.items if unsafe(item)] 35 | 36 | if unsafe_tests: 37 | unsafe_tests_list = "\n- ".join(unsafe_tests) 38 | pytest.exit( 39 | f"\nERROR: Found {len(unsafe_tests)} tests with no clear node failure policy." 40 | f"\nPlease decorate each of them with either @raise_on_failure or @expect_failure." 41 | f"\n- {unsafe_tests_list}" 42 | ) 43 | -------------------------------------------------------------------------------- /raiden/tests/integration/exception.py: -------------------------------------------------------------------------------- 1 | class RetryTestError(Exception): 2 | """Use `flaky` to rerun a test failed due to this exception""" 3 | -------------------------------------------------------------------------------- /raiden/tests/integration/fixtures/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/integration/fixtures/__init__.py -------------------------------------------------------------------------------- /raiden/tests/integration/long_running/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/integration/long_running/__init__.py -------------------------------------------------------------------------------- /raiden/tests/integration/long_running/test_token_networks.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | import pytest 4 | 5 | from raiden import waiting 6 | from raiden.api.python import RaidenAPI 7 | from raiden.raiden_service import RaidenService 8 | from raiden.tests.utils.detect_failure import raise_on_failure 9 | from raiden.tests.utils.network import CHAIN 10 | from raiden.tests.utils.transfer import block_offset_timeout 11 | from raiden.transfer import views 12 | from raiden.utils.typing import BlockTimeout 13 | 14 | 15 | @raise_on_failure 16 | @pytest.mark.parametrize("channels_per_node", [CHAIN]) 17 | @pytest.mark.parametrize("number_of_nodes", [3]) 18 | def test_leave_token_network(raiden_network: List[RaidenService], token_addresses): 19 | registry_address = raiden_network[0].default_registry.address 20 | token_address = token_addresses[0] 21 | _, app1, _ = raiden_network 22 | 23 | channels = views.list_channelstate_for_tokennetwork( 24 | chain_state=views.state_from_raiden(app1), 25 | token_network_registry_address=registry_address, 26 | token_address=token_address, 27 | ) 28 | 29 | timeout = block_offset_timeout( 30 | app1, "Channels not settled in time", BlockTimeout(channels[0].settle_timeout * 10) 31 | ) 32 | with timeout: 33 | RaidenAPI(app1).token_network_leave(registry_address, token_address) 34 | waiting.wait_for_settle( 35 | raiden=app1, 36 | token_network_registry_address=registry_address, 37 | token_address=token_address, 38 | channel_ids=[channel.identifier for channel in channels], 39 | retry_timeout=0.1, 40 | ) 41 | -------------------------------------------------------------------------------- /raiden/tests/integration/network/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/integration/network/__init__.py -------------------------------------------------------------------------------- /raiden/tests/integration/network/proxies/test_custom_token.py: -------------------------------------------------------------------------------- 1 | from raiden.constants import BLOCK_ID_LATEST 2 | from raiden.network.proxies.custom_token import CustomToken 3 | from raiden.network.proxies.service_registry import ServiceRegistry 4 | from raiden.network.rpc.client import JSONRPCClient 5 | from raiden.tests.utils.smartcontracts import is_tx_hash_bytes 6 | 7 | 8 | def test_custom_token(service_registry_address, private_keys, web3, contract_manager): 9 | block_identifier = BLOCK_ID_LATEST 10 | c1_client = JSONRPCClient(web3, private_keys[0]) 11 | c1_service_proxy = ServiceRegistry( 12 | jsonrpc_client=c1_client, 13 | service_registry_address=service_registry_address, 14 | contract_manager=contract_manager, 15 | block_identifier=block_identifier, 16 | ) 17 | token_address = c1_service_proxy.token_address(block_identifier=block_identifier) 18 | c1_token_proxy = CustomToken( 19 | jsonrpc_client=c1_client, 20 | token_address=token_address, 21 | contract_manager=contract_manager, 22 | block_identifier=block_identifier, 23 | ) 24 | 25 | c2_client = JSONRPCClient(web3, private_keys[1]) 26 | 27 | mint_amount = 1000 * 10**18 28 | tx_hash = c1_token_proxy.mint_for(mint_amount, c2_client.address) 29 | # check that we return a correctly formatted transaction_hash for a successful tx 30 | assert is_tx_hash_bytes(tx_hash) 31 | assert c1_token_proxy.balance_of(c2_client.address) == mint_amount 32 | assert c1_token_proxy.balance_of(c1_client.address) == 0 33 | 34 | tx_hash = c1_token_proxy.mint(mint_amount) 35 | assert is_tx_hash_bytes(tx_hash) 36 | assert c1_token_proxy.balance_of(c1_client.address) == mint_amount 37 | -------------------------------------------------------------------------------- /raiden/tests/integration/network/proxies/test_token.py: -------------------------------------------------------------------------------- 1 | from eth_utils import to_canonical_address 2 | 3 | from raiden.constants import BLOCK_ID_LATEST 4 | from raiden.network.proxies.token import Token 5 | from raiden.network.rpc.client import JSONRPCClient 6 | from raiden.tests.utils.smartcontracts import is_tx_hash_bytes 7 | from raiden.utils.keys import privatekey_to_address 8 | 9 | 10 | def test_token(deploy_client, token_proxy, private_keys, web3, contract_manager): 11 | privkey = private_keys[1] 12 | address = privatekey_to_address(privkey) 13 | address = to_canonical_address(address) 14 | other_client = JSONRPCClient(web3, privkey) 15 | other_token_proxy = Token( 16 | jsonrpc_client=other_client, 17 | token_address=to_canonical_address(token_proxy.proxy.address), 18 | contract_manager=contract_manager, 19 | block_identifier=BLOCK_ID_LATEST, 20 | ) 21 | 22 | # send some funds from deployer to generated address 23 | transfer_funds = 100 24 | transaction_hash = token_proxy.transfer(address, transfer_funds) 25 | assert is_tx_hash_bytes(transaction_hash) 26 | assert transfer_funds == token_proxy.balance_of(address) 27 | allow_funds = 100 28 | 29 | transaction_hash = token_proxy.approve(address, allow_funds) 30 | assert is_tx_hash_bytes(transaction_hash) 31 | assert allow_funds == token_proxy.proxy.functions.allowance( 32 | deploy_client.address, address 33 | ).call(block_identifier=BLOCK_ID_LATEST) 34 | other_token_proxy.transfer(deploy_client.address, transfer_funds) 35 | assert token_proxy.balance_of(address) == 0 36 | -------------------------------------------------------------------------------- /raiden/tests/integration/network/transport/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/integration/network/transport/__init__.py -------------------------------------------------------------------------------- /raiden/tests/integration/rpc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/integration/rpc/__init__.py -------------------------------------------------------------------------------- /raiden/tests/integration/test_blockchainservice.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from raiden.constants import BLOCK_ID_LATEST 4 | from raiden.exceptions import SamePeerAddress 5 | from raiden.raiden_service import RaidenService 6 | from raiden.tests.utils.detect_failure import raise_on_failure 7 | from raiden.transfer import views 8 | from raiden.utils.typing import List 9 | 10 | 11 | @raise_on_failure 12 | @pytest.mark.parametrize("number_of_nodes", [1]) 13 | @pytest.mark.parametrize("channels_per_node", [0]) 14 | def test_channel_with_self(raiden_network: List[RaidenService], settle_timeout, token_addresses): 15 | (app0,) = raiden_network # pylint: disable=unbalanced-tuple-unpacking 16 | 17 | registry_address = app0.default_registry.address 18 | token_address = token_addresses[0] 19 | 20 | current_chanels = views.list_channelstate_for_tokennetwork( 21 | views.state_from_raiden(app0), registry_address, token_address 22 | ) 23 | assert not current_chanels 24 | 25 | token_network_address = app0.default_registry.get_token_network(token_address, BLOCK_ID_LATEST) 26 | assert token_network_address, "the token must be registered by the fixtures" 27 | 28 | token_network0 = app0.proxy_manager.token_network(token_network_address, BLOCK_ID_LATEST) 29 | 30 | with pytest.raises(SamePeerAddress): 31 | token_network0.new_netting_channel( 32 | partner=app0.address, 33 | settle_timeout=settle_timeout, 34 | given_block_identifier=BLOCK_ID_LATEST, 35 | ) 36 | -------------------------------------------------------------------------------- /raiden/tests/integration/transfer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/integration/transfer/__init__.py -------------------------------------------------------------------------------- /raiden/tests/mocked/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/mocked/__init__.py -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp1/bf1_basic_functionality.yaml: -------------------------------------------------------------------------------- 1 | ../../bf1_basic_functionality.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp1/mfee2_proportional_fees.yaml: -------------------------------------------------------------------------------- 1 | ../../mfee2_proportional_fees.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp1/mfee3_only_imbalance_fees.yaml: -------------------------------------------------------------------------------- 1 | ../../mfee3_only_imbalance_fees.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp1/ms1_simple_monitoring.yaml: -------------------------------------------------------------------------------- 1 | ../../ms1_simple_monitoring.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp1/ms4_udc_too_low.yaml: -------------------------------------------------------------------------------- 1 | ../../ms4_udc_too_low.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp1/pfs2_simple_no_path.yaml: -------------------------------------------------------------------------------- 1 | ../../pfs2_simple_no_path.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp1/pfs3_multiple_paths.yaml: -------------------------------------------------------------------------------- 1 | ../../pfs3_multiple_paths.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp1/pfs4_use_best_path.yaml: -------------------------------------------------------------------------------- 1 | ../../pfs4_use_best_path.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp1/pfs5_too_low_capacity.yaml: -------------------------------------------------------------------------------- 1 | ../../pfs5_too_low_capacity.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp1/pfs6_simple_path_rewards.yaml: -------------------------------------------------------------------------------- 1 | ../../pfs6_simple_path_rewards.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp1/pfs7_multiple_payments.yaml: -------------------------------------------------------------------------------- 1 | ../../pfs7_multiple_payments.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp1/pfs8_mediator_goes_offline.yaml: -------------------------------------------------------------------------------- 1 | ../../pfs8_mediator_goes_offline.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp1/pfs9_partial_withdraw.yaml: -------------------------------------------------------------------------------- 1 | ../../pfs9_partial_withdraw.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp2/bf2_long_running.yaml: -------------------------------------------------------------------------------- 1 | ../../bf2_long_running.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp2/bf3_multi_directional_payment.yaml: -------------------------------------------------------------------------------- 1 | ../../bf3_multi_directional_payment.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp2/bf4_multi_payments_same_node.yaml: -------------------------------------------------------------------------------- 1 | ../../bf4_multi_payments_same_node.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp2/bf6_stress_hub_node.yaml: -------------------------------------------------------------------------------- 1 | ../../bf6_stress_hub_node.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp2/bf7_long_path.yaml: -------------------------------------------------------------------------------- 1 | ../../bf7_long_path.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp2/mfee1_flat_fee.yaml: -------------------------------------------------------------------------------- 1 | ../../mfee1_flat_fee.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp2/mfee4_combined_fees.yaml: -------------------------------------------------------------------------------- 1 | ../../mfee4_combined_fees.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp2/ms2_simple_monitoring.yaml: -------------------------------------------------------------------------------- 1 | ../../ms2_simple_monitoring.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp2/ms3_simple_monitoring.yaml: -------------------------------------------------------------------------------- 1 | ../../ms3_simple_monitoring.yaml -------------------------------------------------------------------------------- /raiden/tests/scenarios/ci/sp2/pfs1_get_a_simple_path.yaml: -------------------------------------------------------------------------------- 1 | ../../pfs1_get_a_simple_path.yaml -------------------------------------------------------------------------------- /raiden/tests/smart_contracts/RpcTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.3; 2 | 3 | contract RpcTest { 4 | function fail_require() pure public { 5 | require(false); 6 | } 7 | 8 | function fail_assert() pure public { 9 | assert(false); 10 | } 11 | 12 | function ret() pure public returns (uint) { 13 | return 1; 14 | } 15 | 16 | function ret_str() pure public returns (string memory) { 17 | return ""; 18 | } 19 | 20 | function loop(uint reps) pure public returns (uint) { 21 | uint result = 0; 22 | for (uint i=0; i None: 11 | # 641 is a port registered with IANA for this service: 12 | # 13 | # repcmd 641/tcp 14 | # repcmd 641/udp 15 | # 16 | # This is a comcast utility agent that is unlikely to be running. The numbe 17 | # was chosen with. `sum(ord(c) for c in 'random')` 18 | 19 | web3 = Web3(HTTPProvider(URI("http://localhost:641"))) 20 | 21 | with pytest.raises(RequestsConnectionError): 22 | JSONRPCClient(web3=web3, privkey=make_privatekey_bin()) 23 | -------------------------------------------------------------------------------- /raiden/tests/unit/serialized_messages/Delivered.json: -------------------------------------------------------------------------------- 1 | { 2 | "delivered_message_identifier": "14089154938208861744", 3 | "signature": "0x60672e5edb3b5684e74ac8c80d9e9715885e211a47fd638fd56fa8ffc7d0f8f7231066bf7a5896fde817b0d87704149f7b3053fa6b39aeea0ef17a50798d77fc1c", 4 | "type": "Delivered" 5 | } 6 | -------------------------------------------------------------------------------- /raiden/tests/unit/serialized_messages/LockExpired.json: -------------------------------------------------------------------------------- 1 | { 2 | "chain_id": "337", 3 | "channel_identifier": "1338", 4 | "locked_amount": "0", 5 | "locksroot": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 6 | "message_identifier": "1", 7 | "nonce": "1", 8 | "recipient": "0x7461726765747461726765747461726765747461", 9 | "secrethash": "0xd4683a22c1ce39824d931eedc68ea8fa5259ceb03528b1a22f7075863ef8baf0", 10 | "signature": "0x8b9b3baad1e495bd721e6da13463c2f00036e6889487f38b43897a9c9592b5ab6b2c7dac69f744279bf739e1ea345212602f159b1b6f4ef562b8cd5f92ea9ef51b", 11 | "token_network_address": "0x6e6574776f726b6e6574776f726b6e6574776f72", 12 | "transferred_amount": "50", 13 | "type": "LockExpired" 14 | } 15 | -------------------------------------------------------------------------------- /raiden/tests/unit/serialized_messages/LockedTransfer.json: -------------------------------------------------------------------------------- 1 | { 2 | "chain_id": "337", 3 | "channel_identifier": "1338", 4 | "initiator": "0x696e69746961746f72696e69746961746f72696e", 5 | "lock": { 6 | "amount": "50", 7 | "expiration": "5", 8 | "secrethash": "0xd4683a22c1ce39824d931eedc68ea8fa5259ceb03528b1a22f7075863ef8baf0" 9 | }, 10 | "locked_amount": "50", 11 | "locksroot": "0x07fe6255272aa923234b651199a2d7a277e3b1af3156f18c3bc0ba45d27fa380", 12 | "message_identifier": "1", 13 | "metadata": { 14 | "routes": [ 15 | { 16 | "route": [ 17 | "0x77952ce83ca3cad9f7adcfabeda85bd2f1f52008", 18 | "0x94622cc2a5b64a58c25a129d48a2beec4b65b779" 19 | ] 20 | } 21 | ] 22 | }, 23 | "nonce": "1", 24 | "payment_identifier": "1", 25 | "recipient": "0x7461726765747461726765747461726765747461", 26 | "signature": "0xcf304f3401934837830cff2086a29987eb7035bdfec12b1c73de8793625f982b4ec35504cc40db951ed8eebb44123a87679a615300177858babd950b2da3e4391b", 27 | "target": "0x7461726765747461726765747461726765747461", 28 | "token": "0x746f6b656e746f6b656e746f6b656e746f6b656e", 29 | "token_network_address": "0x6e6574776f726b6e6574776f726b6e6574776f72", 30 | "transferred_amount": "0", 31 | "type": "LockedTransfer" 32 | } 33 | -------------------------------------------------------------------------------- /raiden/tests/unit/serialized_messages/PFSCapacityUpdate.json: -------------------------------------------------------------------------------- 1 | { 2 | "canonical_identifier": { 3 | "chain_identifier": "337", 4 | "channel_identifier": "38668238610038203118951563161123284666404871401217031836881793011358969684409", 5 | "token_network_address": "0x6e6574776f726b6e6574776f726b6e6574776f72" 6 | }, 7 | "other_capacity": "37212122603139859625093818195987085041499540758880441738811832487830015056538", 8 | "other_nonce": "14048911142452016106", 9 | "other_participant": "0x20744f2a506d5a783b477c73783b7b765b333c50", 10 | "reveal_timeout": "69187344907585392401104993516842516295179495050250445262298155859068795299032", 11 | "signature": "0xc26badc766938922d0ab0f1eaaecbceb8b1e252bfe4a77cbc705bee79c3828d14d12eadacb1768fe21851b90dd23e187012894960a410e1b5766e69d6e703fa61c", 12 | "type": "PFSCapacityUpdate", 13 | "updating_capacity": "83157032281535436592742485053875373772344953078307910801568052349361129029306", 14 | "updating_nonce": "17118534853349346795", 15 | "updating_participant": "0x383960255b4c58242a20366c4340207c2a794a3b" 16 | } 17 | -------------------------------------------------------------------------------- /raiden/tests/unit/serialized_messages/PFSFeeUpdate.json: -------------------------------------------------------------------------------- 1 | { 2 | "canonical_identifier": { 3 | "chain_identifier": "337", 4 | "channel_identifier": "76772029580080880065711384192941278419613434731761409767757841232989966310411", 5 | "token_network_address": "0x6e6574776f726b6e6574776f726b6e6574776f72" 6 | }, 7 | "fee_schedule": { 8 | "cap_fees": true, 9 | "flat": "0", 10 | "imbalance_penalty": null, 11 | "proportional": "0" 12 | }, 13 | "signature": "0x9c487afcb4c9c8db7358bbb5633e8dafd41d624198f7e84ac9d26d9caeb11f721c476ee2329b4a350d1f73d28f439f62f1e1d431419e6ba7147f9c730d85b2f11c", 14 | "timestamp": "2000-01-01T00:00:00", 15 | "type": "PFSFeeUpdate", 16 | "updating_participant": "0x4e473d79783f3e7676372e2e6d49533a602a3e25" 17 | } 18 | -------------------------------------------------------------------------------- /raiden/tests/unit/serialized_messages/Processed.json: -------------------------------------------------------------------------------- 1 | { 2 | "message_identifier": "17841880388872081916", 3 | "signature": "0x7cde765d5815eb67fd4e2b798f212580d487f051f05433058674490d225e43ca16a8d32d2e7d5a5dff3f136961c0c47790d97cbbdcecefbf8dd909cbb94fd3611c", 4 | "type": "Processed" 5 | } 6 | -------------------------------------------------------------------------------- /raiden/tests/unit/serialized_messages/RefundTransfer.json: -------------------------------------------------------------------------------- 1 | { 2 | "chain_id": "337", 3 | "channel_identifier": "1338", 4 | "initiator": "0x696e69746961746f72696e69746961746f72696e", 5 | "lock": { 6 | "amount": "50", 7 | "expiration": "5", 8 | "secrethash": "0xd4683a22c1ce39824d931eedc68ea8fa5259ceb03528b1a22f7075863ef8baf0" 9 | }, 10 | "locked_amount": "50", 11 | "locksroot": "0x07fe6255272aa923234b651199a2d7a277e3b1af3156f18c3bc0ba45d27fa380", 12 | "message_identifier": "1", 13 | "metadata": { 14 | "routes": [ 15 | { 16 | "route": [ 17 | "0x77952ce83ca3cad9f7adcfabeda85bd2f1f52008", 18 | "0x94622cc2a5b64a58c25a129d48a2beec4b65b779" 19 | ] 20 | } 21 | ] 22 | }, 23 | "nonce": "1", 24 | "payment_identifier": "1", 25 | "recipient": "0x7461726765747461726765747461726765747461", 26 | "signature": "0x047a559bc8103fa39c10c572fa67f7ddb31a06bee2084d8d80e2b32311fddb2f7f2c8d93ac32f779987feb6531ebcacc66cc6ca5f4bedc932e5a80a23d4d33ca1c", 27 | "target": "0x7461726765747461726765747461726765747461", 28 | "token": "0x746f6b656e746f6b656e746f6b656e746f6b656e", 29 | "token_network_address": "0x6e6574776f726b6e6574776f726b6e6574776f72", 30 | "transferred_amount": "0", 31 | "type": "RefundTransfer" 32 | } 33 | -------------------------------------------------------------------------------- /raiden/tests/unit/serialized_messages/RequestMonitoring.json: -------------------------------------------------------------------------------- 1 | { 2 | "balance_proof": { 3 | "additional_hash": "0xd4683a22c1ce39824d931eedc68ea8fa5259ceb03528b1a22f7075863ef8baf0", 4 | "balance_hash": "0xefe746ea6709080801a054596599dc5a289595c49ad58940f7daf0b70a79ea0e", 5 | "chain_id": "337", 6 | "channel_identifier": "1338", 7 | "non_closing_signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 8 | "nonce": "1", 9 | "signature": "0x971fbf74da6c04bfefa830ccadedf49a5722c734fb88fb357ff272655b6cc66d3f024b28ce6c5520918c3f8ab8cba7187ec2f253294e14cf499f49ac6763351c1b", 10 | "token_network_address": "0x6e6574776f726b6e6574776f726b6e6574776f72" 11 | }, 12 | "monitoring_service_contract_address": "0x232a714748244f2d5a64674056262a7d2d602527", 13 | "non_closing_participant": "0x3342096b704c4e2546635149672c383543402745", 14 | "non_closing_signature": "0x9e19a6e592a6c112564a66f689b01d9c0dfefc6c6b578c9fec50873099fe742e7ad040c10f53f55ad642e0b610f2ba328e6820bbabb6e67fa9dcd3d5f03dedb21b", 15 | "reward_amount": "97425888683892750587945680783138836924292540737961888655734476399742293263563", 16 | "signature": "0x80014a840660dbe04b3c67401c8e1ef1b9475a17f1560519e17e6b91e88fdcc9514c0348f3feeba527d35c4ea320117b301471dfb99cba549570cb0358e71ae31b", 17 | "type": "RequestMonitoring" 18 | } 19 | -------------------------------------------------------------------------------- /raiden/tests/unit/serialized_messages/RevealSecret.json: -------------------------------------------------------------------------------- 1 | { 2 | "message_identifier": "105314804548892799", 3 | "secret": "0x202467250c2a7153375a4b2b29702351214a52493027283c3b47572f33743e6d", 4 | "signature": "0x195dbf16fce68b5513c960a9cb7849bba7607e77c0f402438c86966a33b0ea1d30c9a701c1e1c366cc6c8c0801f869d645570c8d61dd69d2b657077591b9ffc31b", 5 | "type": "RevealSecret" 6 | } 7 | -------------------------------------------------------------------------------- /raiden/tests/unit/serialized_messages/SecretRequest.json: -------------------------------------------------------------------------------- 1 | { 2 | "amount": "45165925230536924971228959719563392686463124759322136463215771173163898360255", 3 | "expiration": "104175626816703061124219718333625999849758664982862800987655596244991229111856", 4 | "message_identifier": "9529579048853756671", 5 | "payment_identifier": "14819846930052064268", 6 | "secrethash": "0x5b3d533720436772364439394443096b522b7767312a342e722b576c0c7b3c24", 7 | "signature": "0x85042a09b9c3d3ac951b88650d4429b42b7ff77aa4a3f2f504ef8faf3fa61d4d47c73b606b64b1355329de2a2c6a65162965418afad38d7a567c4dc14a4f65161c", 8 | "type": "SecretRequest" 9 | } 10 | -------------------------------------------------------------------------------- /raiden/tests/unit/serialized_messages/Unlock.json: -------------------------------------------------------------------------------- 1 | { 2 | "chain_id": "337", 3 | "channel_identifier": "1338", 4 | "locked_amount": "0", 5 | "locksroot": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 6 | "message_identifier": "1", 7 | "nonce": "1", 8 | "payment_identifier": "1", 9 | "secret": "0x7365637265747365637265747365637265747365637265747365637265747365", 10 | "signature": "0x765f22e214d06a6fbb3da00f5a0f4cabdeb4cf5907290ae67f416e9e99f37cf3535f63450884340c27b727d5aebdc454be3d43eb577f0161c31ef3530006a5601c", 11 | "token_network_address": "0x6e6574776f726b6e6574776f726b6e6574776f72", 12 | "transferred_amount": "50", 13 | "type": "Unlock" 14 | } 15 | -------------------------------------------------------------------------------- /raiden/tests/unit/serialized_messages/WithdrawConfirmation.json: -------------------------------------------------------------------------------- 1 | { 2 | "chain_id": "2912684545706367143", 3 | "channel_identifier": "66071387364709377324340632089415933825246396031105621660715966884891198244925", 4 | "expiration": "43676510842380411296551768036011944070757570786456846967204732529082316946061", 5 | "message_identifier": "5514218360736026303", 6 | "nonce": "10968024639665321709", 7 | "participant": "0x375f4a2d682e6768787a4f2b506d3b627421306d", 8 | "signature": "0x29038edf470b9dbc254e8c8ebf0c8a88018753966b8650829842e3f794bc1b5e4b883148b322585ce0fdd586882894b47d8e0e8d17b25ddc90d3ffca2792d4b31c", 9 | "token_network_address": "0x7c5c76770c385e5654297728552757314f486c78", 10 | "total_withdraw": "43030000375053848483360010892688128125882955220842751483463799296695399817914", 11 | "type": "WithdrawConfirmation" 12 | } 13 | -------------------------------------------------------------------------------- /raiden/tests/unit/serialized_messages/WithdrawExpired.json: -------------------------------------------------------------------------------- 1 | { 2 | "chain_id": "4803472963369719645", 3 | "channel_identifier": "61183841624386915999502396675066499823249760999523346270508542183801661676912", 4 | "expiration": "46924984437646860423954278244473534738405341550153175805816147344916305178369", 5 | "message_identifier": "11171856277818869245", 6 | "nonce": "3640670159341665926", 7 | "participant": "0x3066557c5649442850487e5e2c22653f4d4d712a", 8 | "signature": "0xc0da2cf2c1f24e5c230b068dcd43638c6be1eeea92b4c3715057592c09807c547a1e468cffd8bc343abb6872c4d99179dd23268087e40226f72ff608cd7071c91b", 9 | "token_network_address": "0x6d4169287079442d0a775e566c284a2152660b71", 10 | "total_withdraw": "81389322072213456958274814836804202981957383964284380743558564309315106239433", 11 | "type": "WithdrawExpired" 12 | } 13 | -------------------------------------------------------------------------------- /raiden/tests/unit/serialized_messages/WithdrawRequest.json: -------------------------------------------------------------------------------- 1 | { 2 | "chain_id": "16431771713230441611", 3 | "channel_identifier": "11594163355810217856318167182919743079795725721393805436471231120230427092116", 4 | "coop_settle": false, 5 | "expiration": "4753526607684168146253831162495710736850402412564075623041867780183880773985", 6 | "message_identifier": "17565842506374783049", 7 | "nonce": "6649379561686619790", 8 | "participant": "0x4045353331427d2f45564f45503838452f576577", 9 | "signature": "0x292459ed72dd98cd2f58c9e8aae09ce2bc68dca55950106452fe22c3ea3cb1096b124584d74842978a22658c7a3b4149e16acf4017392cabd3db6a44b7d79e951b", 10 | "token_network_address": "0x2b2765577a643542313b5c31625165356f752e52", 11 | "total_withdraw": "35585354725113231262395347697090524356626150773896569326961510485817666774489", 12 | "type": "WithdrawRequest" 13 | } 14 | -------------------------------------------------------------------------------- /raiden/tests/unit/storage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/unit/storage/__init__.py -------------------------------------------------------------------------------- /raiden/tests/unit/storage/test_versions.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import List 3 | 4 | import pytest 5 | 6 | from raiden.storage.versions import filter_db_names, latest_db_file 7 | 8 | 9 | def test_latest_db_file(): 10 | assert latest_db_file([Path("v10_log.db"), Path("v9_log.db")]) == Path("v10_log.db") 11 | assert latest_db_file([Path("v9_log.db"), Path("v10_log.db")]) == Path("v10_log.db") 12 | assert latest_db_file([Path("v1_log.db"), Path("v9_log.db")]) == Path("v9_log.db") 13 | assert latest_db_file([Path("v9_log.db"), Path("v1_log.db")]) == Path("v9_log.db") 14 | assert latest_db_file([]) is None 15 | 16 | values = ["a", ".db", "v9.db", "9_log.db", "va9_log.db", "v9a_log.db"] 17 | for invalid_value in values: 18 | with pytest.raises(AssertionError): 19 | latest_db_file([Path(invalid_value)]) 20 | 21 | 22 | def test_filter_db_names(): 23 | assert filter_db_names(["v10_log.db", "v9_log.db"]) == [Path("v10_log.db"), Path("v9_log.db")] 24 | assert filter_db_names(["v9_log.db", "v10_log.db"]) == [Path("v9_log.db"), Path("v10_log.db")] 25 | assert filter_db_names(["v1_log.db", "v9_log.db"]) == [Path("v1_log.db"), Path("v9_log.db")] 26 | assert filter_db_names(["v9_log.db", "v1_log.db"]) == [Path("v9_log.db"), Path("v1_log.db")] 27 | 28 | values: List[List[str]] = [ 29 | [], 30 | ["a"], 31 | [".db"], 32 | ["v9.db"], 33 | ["9_log.db"], 34 | ["va9_log.db"], 35 | ["v9a_log.db"], 36 | ] 37 | for invalid_value in values: 38 | assert filter_db_names(invalid_value) == [] 39 | -------------------------------------------------------------------------------- /raiden/tests/unit/test_app.py: -------------------------------------------------------------------------------- 1 | from raiden.ui.app import rpc_normalized_endpoint 2 | 3 | 4 | def test_rpc_normalized_endpoint(): 5 | """Test that the rpc_normalized_endpoint function works as expected""" 6 | # Infura should always be forced to https scheme 7 | res = rpc_normalized_endpoint("goerli.infura.io/v3/11111111111111111111111111111111") 8 | assert res == "https://goerli.infura.io/v3/11111111111111111111111111111111" 9 | res = rpc_normalized_endpoint("http://goerli.infura.io/v3/11111111111111111111111111111111") 10 | assert res == "https://goerli.infura.io/v3/11111111111111111111111111111111" 11 | res = rpc_normalized_endpoint("https://goerli.infura.io/v3/11111111111111111111111111111111") 12 | assert res == "https://goerli.infura.io/v3/11111111111111111111111111111111" 13 | 14 | # if the endpoint does not have a scheme, append http scheme 15 | res = rpc_normalized_endpoint("//127.0.0.1:5454") 16 | assert res == "http://127.0.0.1:5454" 17 | res = rpc_normalized_endpoint("http://127.0.0.1:5454") 18 | assert res == "http://127.0.0.1:5454" 19 | -------------------------------------------------------------------------------- /raiden/tests/unit/test_dict_encoding.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from raiden.constants import UINT64_MAX, UINT256_MAX 4 | from raiden.storage.serialization import JSONSerializer 5 | from raiden.tests.utils import factories 6 | from raiden.utils.signer import LocalSigner 7 | 8 | PRIVKEY, ADDRESS = factories.make_privkey_address() 9 | signer = LocalSigner(PRIVKEY) 10 | 11 | 12 | @pytest.mark.parametrize("amount", [0, UINT256_MAX]) 13 | @pytest.mark.parametrize("payment_identifier", [0, UINT64_MAX]) 14 | @pytest.mark.parametrize("nonce", [1, UINT64_MAX]) 15 | @pytest.mark.parametrize("transferred_amount", [0, UINT256_MAX]) 16 | def test_mediated_transfer_min_max(amount, payment_identifier, nonce, transferred_amount): 17 | mediated_transfer = factories.create( 18 | factories.LockedTransferProperties( 19 | amount=amount, 20 | payment_identifier=payment_identifier, 21 | nonce=nonce, 22 | transferred_amount=transferred_amount, 23 | ) 24 | ) 25 | 26 | mediated_transfer.sign(signer) 27 | data = JSONSerializer.serialize(mediated_transfer) 28 | assert JSONSerializer.deserialize(data) == mediated_transfer 29 | 30 | 31 | @pytest.mark.parametrize("amount", [0, UINT256_MAX]) 32 | @pytest.mark.parametrize("payment_identifier", [0, UINT64_MAX]) 33 | @pytest.mark.parametrize("nonce", [1, UINT64_MAX]) 34 | @pytest.mark.parametrize("transferred_amount", [0, UINT256_MAX]) 35 | def test_refund_transfer_min_max(amount, payment_identifier, nonce, transferred_amount): 36 | refund_transfer = factories.create( 37 | factories.RefundTransferProperties( 38 | amount=amount, 39 | payment_identifier=payment_identifier, 40 | nonce=nonce, 41 | transferred_amount=transferred_amount, 42 | ) 43 | ) 44 | 45 | refund_transfer.sign(signer) 46 | 47 | data = JSONSerializer.serialize(refund_transfer) 48 | assert JSONSerializer.deserialize(data) == refund_transfer 49 | -------------------------------------------------------------------------------- /raiden/tests/unit/test_notifyingqueue.py: -------------------------------------------------------------------------------- 1 | from raiden.utils.notifying_queue import NotifyingQueue 2 | 3 | 4 | def add_element_to_queue(queue, element): 5 | queue.put(element) 6 | 7 | 8 | def test_queue(): 9 | queue = NotifyingQueue() 10 | assert queue.copy() == [] 11 | 12 | queue.put(1) 13 | assert queue.copy() == [1] 14 | assert queue.peek() == 1, "copy must preserve the queue" 15 | 16 | queue.put(2) 17 | assert queue.copy() == [1, 2], "copy must preserve the items order" 18 | assert queue.peek() == 1, "copy must preserve the queue" 19 | 20 | assert queue.get() == 1, "get should return first item" 21 | assert queue.peek() == 2, "get must remove first item" 22 | 23 | 24 | def test_not_empty(): 25 | queue = NotifyingQueue() 26 | assert not queue.is_set() 27 | 28 | queue = NotifyingQueue(items=[1, 2]) 29 | assert queue.is_set() 30 | -------------------------------------------------------------------------------- /raiden/tests/unit/test_pending_locks.py: -------------------------------------------------------------------------------- 1 | from raiden.constants import LOCKSROOT_OF_NO_LOCKS 2 | from raiden.transfer.channel import compute_locksroot 3 | from raiden.transfer.state import PendingLocksState 4 | 5 | 6 | def test_empty(): 7 | locks = PendingLocksState([]) 8 | assert compute_locksroot(locks) == LOCKSROOT_OF_NO_LOCKS 9 | -------------------------------------------------------------------------------- /raiden/tests/unit/test_rpc.py: -------------------------------------------------------------------------------- 1 | from raiden.constants import EthClient 2 | from raiden.network.rpc.client import ClientErrorInspectResult, inspect_client_error 3 | 4 | 5 | def test_inspect_client_error(): 6 | """Regression test for issue https://github.com/raiden-network/raiden/issues/3005""" 7 | errorstr = ( 8 | "{'code': -32015, 'message': 'Transaction execution error.', 'data': " 9 | "'Internal(\"Requires higher than upper limit of 80000000\")'}" 10 | ) 11 | exception = ValueError(errorstr) 12 | 13 | result = inspect_client_error(exception, EthClient.PARITY) 14 | assert result == ClientErrorInspectResult.ALWAYS_FAIL 15 | -------------------------------------------------------------------------------- /raiden/tests/unit/test_runnable.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | import gevent 4 | from gevent.event import AsyncResult 5 | 6 | from raiden.utils.runnable import Runnable 7 | 8 | 9 | class RunnableTest(Runnable): 10 | def __init__(self): 11 | super().__init__() 12 | 13 | def start(self): 14 | self._stop_event = AsyncResult() 15 | super().start() 16 | 17 | def _run(self, *args: Any, **kwargs: Any) -> None: 18 | while self._stop_event and self._stop_event.wait(0.5) is not True: 19 | gevent.sleep(0.1) 20 | return 21 | 22 | def stop(self): 23 | if self._stop_event: 24 | self._stop_event.set(True) 25 | 26 | 27 | def test_runnable_and_gevent_join_all(): 28 | """Test that runnable adheres to the greenlet interface for gevent.joinall() 29 | 30 | Regression test for https://github.com/raiden-network/raiden/issues/5327 31 | """ 32 | a = RunnableTest() 33 | a.start() 34 | a.stop() 35 | gevent.joinall({a}, raise_error=True) 36 | -------------------------------------------------------------------------------- /raiden/tests/unit/test_state.py: -------------------------------------------------------------------------------- 1 | from raiden.transfer.state import TransactionChannelDeposit 2 | 3 | 4 | def test_transaction_channel_new_balance_ordering(): 5 | a = TransactionChannelDeposit(bytes(1), 1, 1) 6 | b = TransactionChannelDeposit(bytes(2), 2, 2) 7 | assert a != b 8 | assert a < b 9 | assert b > a 10 | 11 | a = TransactionChannelDeposit(bytes(1), 1, 1) 12 | b = TransactionChannelDeposit(bytes(2), 2, 1) 13 | assert a != b 14 | assert a < b 15 | assert b > a 16 | 17 | a = TransactionChannelDeposit(bytes(3), 3, 3) 18 | b = TransactionChannelDeposit(bytes(3), 3, 3) 19 | assert a == b 20 | assert not a > b 21 | assert not b > a 22 | -------------------------------------------------------------------------------- /raiden/tests/unit/transfer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/unit/transfer/__init__.py -------------------------------------------------------------------------------- /raiden/tests/unit/transfer/mediated_transfer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/unit/transfer/mediated_transfer/__init__.py -------------------------------------------------------------------------------- /raiden/tests/unit/transfer/test_architecture.py: -------------------------------------------------------------------------------- 1 | from os import urandom 2 | 3 | import pytest 4 | 5 | from raiden.constants import UINT64_MAX, UINT256_MAX 6 | from raiden.tests.utils import factories 7 | 8 | 9 | def test_balance_proof_invalid_attributes(): 10 | invalid_nonces = [-10, 0, UINT64_MAX + 1] 11 | invalid_transferred_amounts = [-1, UINT256_MAX + 1] 12 | invalid_locksroots = [urandom(31), urandom(33)] 13 | invalid_signatures = [urandom(64), urandom(66)] 14 | 15 | properties_unsigned = factories.BalanceProofProperties() 16 | properties_signed = factories.BalanceProofSignedStateProperties(signature=urandom(64)) 17 | 18 | for properties in (properties_unsigned, properties_signed): 19 | for nonce in invalid_nonces: 20 | with pytest.raises(ValueError): 21 | factories.create(factories.replace(properties, nonce=nonce)) 22 | 23 | for amount in invalid_transferred_amounts: 24 | with pytest.raises(ValueError): 25 | factories.create(factories.replace(properties, transferred_amount=amount)) 26 | 27 | for locksroot in invalid_locksroots: 28 | with pytest.raises(ValueError): 29 | factories.create(factories.replace(properties, locksroot=locksroot)) 30 | 31 | for signature in invalid_signatures: 32 | with pytest.raises(ValueError): 33 | factories.create(factories.replace(properties_signed, signature=signature)) 34 | -------------------------------------------------------------------------------- /raiden/tests/unit/transfer/test_events.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from raiden.constants import UINT256_MAX 4 | from raiden.tests.utils import factories 5 | from raiden.transfer.events import EventPaymentReceivedSuccess 6 | 7 | 8 | def test_invalid_instantiation_event_payment_received_success(): 9 | kwargs = dict( 10 | token_network_registry_address=factories.UNIT_TOKEN_NETWORK_REGISTRY_ADDRESS, 11 | token_network_address=factories.UNIT_TOKEN_NETWORK_ADDRESS, 12 | identifier=factories.UNIT_TRANSFER_IDENTIFIER, 13 | initiator=factories.make_address(), 14 | ) 15 | 16 | with pytest.raises(ValueError): 17 | EventPaymentReceivedSuccess(amount=UINT256_MAX + 1, **kwargs) 18 | 19 | with pytest.raises(ValueError): 20 | EventPaymentReceivedSuccess(amount=-5, **kwargs) 21 | -------------------------------------------------------------------------------- /raiden/tests/unit/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/unit/utils/__init__.py -------------------------------------------------------------------------------- /raiden/tests/unit/utils/test_dynamic_block_batch_size.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from eth_typing import BlockNumber 3 | 4 | from raiden.blockchain.exceptions import BlockBatchSizeTooSmall 5 | from raiden.blockchain.utils import BlockBatchSizeAdjuster 6 | from raiden.settings import BlockBatchSizeConfig 7 | 8 | 9 | def test_dynamic_block_batch_size_adjuster(): 10 | config = BlockBatchSizeConfig( 11 | min=BlockNumber(5), 12 | warn_threshold=BlockNumber(50), 13 | initial=BlockNumber(1000), 14 | max=BlockNumber(100_000), 15 | ) 16 | adjuster = BlockBatchSizeAdjuster(config, base=2, step_size=1) 17 | 18 | # Check initial value 19 | assert adjuster.batch_size == 1000 20 | 21 | adjuster.increase() 22 | assert adjuster.batch_size == 2000 23 | 24 | # Increase all the way to the max value 25 | for _ in range(6): 26 | adjuster.increase() 27 | assert adjuster.batch_size == config.max 28 | 29 | # Ensure we're clamped to the max value 30 | adjuster.increase() 31 | assert adjuster.batch_size == config.max 32 | 33 | # Decrease back down to the minimum 34 | for _ in range(15): 35 | adjuster.decrease() 36 | assert adjuster.batch_size == config.min 37 | 38 | # Decreasing below the minimum must raise an exception 39 | with pytest.raises(BlockBatchSizeTooSmall): 40 | adjuster.decrease() 41 | -------------------------------------------------------------------------------- /raiden/tests/unit/utils/test_formatting.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from eth_utils import to_checksum_address as eth_utils_checksum 4 | 5 | from raiden.utils.formatting import to_checksum_address 6 | from raiden.utils.typing import Address 7 | 8 | 9 | def test_random_addresses(): 10 | for _ in range(100): 11 | address_bytes = Address(os.urandom(20)) 12 | assert eth_utils_checksum(address_bytes) == to_checksum_address(address_bytes) 13 | -------------------------------------------------------------------------------- /raiden/tests/unit/utils/test_functions.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from raiden.utils.http import split_endpoint 4 | from raiden.utils.typing import Endpoint 5 | 6 | 7 | def test_split_endpoint_valid(): 8 | host, port = split_endpoint(Endpoint("https://rpc.slock.it/goerli")) 9 | assert host == "rpc.slock.it" 10 | assert port == 0 11 | 12 | host, port = split_endpoint(Endpoint("https://rpc.slock.it:443/goerli")) 13 | assert host == "rpc.slock.it" 14 | assert port == 443 15 | 16 | 17 | def test_split_endpoint_invalid(): 18 | with pytest.raises(ValueError): 19 | split_endpoint(Endpoint("/invalid/endpoint")) 20 | -------------------------------------------------------------------------------- /raiden/tests/unit/utils/test_nursery.py: -------------------------------------------------------------------------------- 1 | import gevent 2 | import pytest 3 | 4 | from raiden.utils.nursery import Janitor 5 | 6 | 7 | class NurseryException(Exception): 8 | def __init__(self, exit_code): 9 | super().__init__(self, exit_code) 10 | 11 | 12 | def die(timeout: int = 0, exit_code: int = 42): 13 | gevent.sleep(timeout) 14 | raise NurseryException(exit_code) 15 | 16 | 17 | def die_os(timeout: int = 0, exit_code: int = 42): 18 | return ["python", "-c", f"import time, sys; time.sleep({timeout}); sys.exit({exit_code})"] 19 | 20 | 21 | def test_nursery_detects_exit_code(): 22 | with pytest.raises(NurseryException): 23 | with Janitor() as nursery: 24 | p = nursery.spawn_under_watch(die, timeout=2) 25 | gevent.joinall({p}, raise_error=True, count=1) 26 | 27 | 28 | def test_nursery_dectect_exit_code_process(): 29 | with pytest.raises(SystemExit): 30 | with Janitor() as nursery: 31 | p = nursery.exec_under_watch(die_os(timeout=2)) 32 | gevent.joinall({p}, raise_error=True, count=1) 33 | 34 | 35 | def test_nursery_detects_failing_popen(): 36 | with pytest.raises(FileNotFoundError): 37 | with Janitor() as nursery: 38 | nursery.exec_under_watch(["nota_valid_program"]) 39 | -------------------------------------------------------------------------------- /raiden/tests/unit/utils/test_secrethash.py: -------------------------------------------------------------------------------- 1 | from raiden.utils.secrethash import sha256_secrethash 2 | from raiden.utils.typing import Secret 3 | 4 | 5 | def test_sha256_secrethash(): 6 | assert sha256_secrethash(Secret(b"")) == ( 7 | b"\xe3\xb0\xc4B\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99o\xb9$" 8 | b"'\xaeA\xe4d\x9b\x93L\xa4\x95\x99\x1bxR\xb8U" 9 | ) 10 | 11 | assert sha256_secrethash(Secret(b"a")) == ( 12 | b"\xca\x97\x81\x12\xca\x1b\xbd\xca\xfa\xc21\xb3\x9a#\xdcM" 13 | b"\xa7\x86\xef\xf8\x14|Nr\xb9\x80w\x85\xaf\xeeH\xbb" 14 | ) 15 | secret = Secret(b"secretsecretsecretsecretsecretse") 16 | assert sha256_secrethash(secret) == ( 17 | b'\xd4h:"\xc1\xce9\x82M\x93\x1e\xed\xc6\x8e\xa8\xfa' 18 | b"RY\xce\xb05(\xb1\xa2/pu\x86>\xf8\xba\xf0" 19 | ) 20 | -------------------------------------------------------------------------------- /raiden/tests/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/tests/utils/__init__.py -------------------------------------------------------------------------------- /raiden/tests/utils/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path 3 | 4 | from raiden.constants import RAIDEN_DB_VERSION 5 | 6 | 7 | def database_from_privatekey(base_dir, app_number): 8 | """Format a database path based on the private key and app number.""" 9 | dbpath = os.path.join(base_dir, f"app{app_number}", f"v{RAIDEN_DB_VERSION}_log.db") 10 | os.makedirs(os.path.dirname(dbpath)) 11 | 12 | return dbpath 13 | -------------------------------------------------------------------------------- /raiden/tests/utils/ci.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import Optional 3 | 4 | from raiden.utils.formatting import pex 5 | 6 | 7 | def get_artifacts_storage() -> Optional[str]: 8 | return os.environ.get("RAIDEN_TESTS_LOGSDIR") 9 | 10 | 11 | def shortened_artifacts_storage(test_node) -> Optional[str]: 12 | """Return a pathname based on the test details. 13 | 14 | Some platforms have a limit to the length of a file path. This function 15 | will compute a path name based on the test details, and if necessary trim 16 | it down to fit 300 characters. 17 | """ 18 | artifacts_dir = get_artifacts_storage() 19 | 20 | if artifacts_dir is None: 21 | return None 22 | 23 | path = os.path.join(artifacts_dir, test_node.name) 24 | 25 | # Paths longer than 286 will be reject on CircleCI 26 | if len(path) >= 286: 27 | original_name = test_node.originalname 28 | shortened_args = pex(test_node.name.encode("utf8")) 29 | path = os.path.join(artifacts_dir, f"{original_name}-{shortened_args}") 30 | 31 | msg = ( 32 | "Trimming the tests arguments didn't result in a path short enough, the " 33 | "base_dir has to be trimmed." 34 | ) 35 | assert len(path) < 286, msg 36 | 37 | return path 38 | -------------------------------------------------------------------------------- /raiden/tests/utils/cli.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict, Tuple 2 | from unittest import mock 3 | 4 | from click.core import ParameterSource # type: ignore 5 | 6 | from raiden.ui import cli 7 | 8 | 9 | def get_invoked_kwargs(cli_input, cli_runner, capture_function): 10 | 11 | cli_args = cli_input.split() 12 | command = cli_args[0] 13 | assert command == "raiden" 14 | call_args = cli_args[1:] or None 15 | 16 | with mock.patch(capture_function, autospec=True) as mock_run: 17 | result = cli_runner.invoke(cli.run, call_args) 18 | assert result.exit_code == 0 19 | assert not result.exception 20 | assert mock_run.called 21 | args, kwargs = mock_run.call_args 22 | 23 | return args, kwargs 24 | 25 | 26 | def get_cli_result(cli_input, cli_runner, capture_function): 27 | 28 | cli_args = cli_input.split() 29 | command = cli_args[0] 30 | assert command == "raiden" 31 | call_args = cli_args[1:] or None 32 | 33 | with mock.patch(capture_function, autospec=True): 34 | result = cli_runner.invoke(cli.run, call_args) 35 | return result 36 | 37 | 38 | def assert_invoked_kwargs( 39 | kwargs: Dict[str, Any], expected_args: Dict[str, Tuple[ParameterSource, Any]] 40 | ): 41 | ctx = kwargs["ctx"] 42 | 43 | dic = {} 44 | for k, v in kwargs.items(): 45 | if k in expected_args: 46 | dic[k] = (ctx.get_parameter_source(k), v) 47 | 48 | assert dic == expected_args 49 | -------------------------------------------------------------------------------- /raiden/tests/utils/client.py: -------------------------------------------------------------------------------- 1 | from raiden.constants import TRANSACTION_INTRINSIC_GAS 2 | from raiden.network.rpc.client import EthTransfer, JSONRPCClient 3 | from raiden.tests.utils.factories import HOP1 4 | from raiden.utils.typing import Address 5 | 6 | 7 | def burn_eth(rpc_client: JSONRPCClient, amount_to_leave: int = 0) -> None: 8 | """Burns all the ETH on the account of the given raiden service""" 9 | address = rpc_client.address 10 | web3 = rpc_client.web3 11 | gas_price = web3.eth.gas_price 12 | 13 | # Leave enough ETH to pay for the burn transaction. 14 | amount_to_leave = TRANSACTION_INTRINSIC_GAS + amount_to_leave 15 | 16 | amount_to_burn = web3.eth.get_balance(address) - gas_price * amount_to_leave 17 | burn_transfer = EthTransfer( 18 | to_address=Address(HOP1), value=amount_to_burn, gas_price=gas_price 19 | ) 20 | 21 | transaction_hash = rpc_client.transact(burn_transfer) 22 | rpc_client.poll_transaction(transaction_hash) 23 | -------------------------------------------------------------------------------- /raiden/tests/utils/migrations.py: -------------------------------------------------------------------------------- 1 | from hexbytes import HexBytes 2 | 3 | from raiden.tests.utils.factories import make_block_hash 4 | from raiden.utils.typing import Any, Dict, Tuple 5 | 6 | 7 | class FakeEth: 8 | def __init__(self, block_to_blockhash: Dict[int, Any]): 9 | self.block_to_blockhash = block_to_blockhash 10 | 11 | def get_block(self, number: int) -> Dict[str, Any]: 12 | block_hash = self.block_to_blockhash[number] 13 | return {"hash": block_hash} 14 | 15 | 16 | class FakeWeb3: 17 | def __init__(self, block_to_blockhash: Dict[int, Any]): 18 | self.eth = FakeEth(block_to_blockhash) 19 | 20 | 21 | def create_fake_web3_for_block_hash(number_of_blocks: int = 0) -> Tuple[FakeWeb3, Dict[int, Any]]: 22 | block_to_blockhash = {} 23 | for block in range(0, number_of_blocks): 24 | block_to_blockhash[block] = HexBytes(make_block_hash()) 25 | 26 | fake_web3 = FakeWeb3(block_to_blockhash) 27 | 28 | return fake_web3, block_to_blockhash 29 | -------------------------------------------------------------------------------- /raiden/transfer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/transfer/__init__.py -------------------------------------------------------------------------------- /raiden/transfer/identifiers.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from raiden.constants import EMPTY_ADDRESS, UINT256_MAX 4 | from raiden.utils.formatting import to_checksum_address 5 | from raiden.utils.typing import ( 6 | Address, 7 | ChainID, 8 | ChannelID, 9 | T_Address, 10 | T_ChainID, 11 | T_ChannelID, 12 | TokenNetworkAddress, 13 | typecheck, 14 | ) 15 | 16 | 17 | @dataclass(frozen=True, order=True) 18 | class CanonicalIdentifier: 19 | chain_identifier: ChainID 20 | token_network_address: TokenNetworkAddress 21 | channel_identifier: ChannelID 22 | 23 | def validate(self) -> None: 24 | typecheck(self.chain_identifier, T_ChainID) 25 | typecheck(self.token_network_address, T_Address) 26 | typecheck(self.channel_identifier, T_ChannelID) 27 | 28 | if self.channel_identifier < 0 or self.channel_identifier > UINT256_MAX: 29 | raise ValueError("channel id is invalid") 30 | 31 | def __str__(self) -> str: 32 | return ( 33 | "CanonicalIdentifier(" 34 | f"chain_identifier={self.chain_identifier}, " 35 | f"token_network_address={to_checksum_address(self.token_network_address)}, " 36 | f"channel_identifier={self.channel_identifier}" 37 | ")" 38 | ) 39 | 40 | 41 | @dataclass(frozen=True) 42 | class QueueIdentifier: 43 | recipient: Address 44 | canonical_identifier: CanonicalIdentifier 45 | 46 | def __str__(self) -> str: 47 | return ( 48 | "QueueIdentifier(" 49 | f"recipient={to_checksum_address(self.recipient)}, " 50 | f"canonical_identifier={self.canonical_identifier}" 51 | ")" 52 | ) 53 | 54 | 55 | CANONICAL_IDENTIFIER_UNORDERED_QUEUE = CanonicalIdentifier( 56 | ChainID(0), TokenNetworkAddress(EMPTY_ADDRESS), ChannelID(0) 57 | ) 58 | -------------------------------------------------------------------------------- /raiden/transfer/mediated_transfer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/transfer/mediated_transfer/__init__.py -------------------------------------------------------------------------------- /raiden/transfer/mediated_transfer/tasks.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, field 2 | from typing import ClassVar 3 | 4 | from raiden.transfer.architecture import TransferRole, TransferTask 5 | from raiden.transfer.identifiers import CanonicalIdentifier 6 | from raiden.transfer.mediated_transfer.state import ( 7 | InitiatorPaymentState, 8 | MediatorTransferState, 9 | TargetTransferState, 10 | ) 11 | from raiden.utils.typing import ChannelID, TokenNetworkAddress 12 | 13 | 14 | @dataclass 15 | class InitiatorTask(TransferTask): 16 | role: ClassVar[TransferRole] = TransferRole.INITIATOR 17 | 18 | manager_state: InitiatorPaymentState = field(repr=False) 19 | 20 | 21 | @dataclass 22 | class MediatorTask(TransferTask): 23 | role: ClassVar[TransferRole] = TransferRole.MEDIATOR 24 | 25 | mediator_state: MediatorTransferState = field(repr=False) 26 | 27 | 28 | @dataclass 29 | class TargetTask(TransferTask): 30 | role: ClassVar[TransferRole] = TransferRole.TARGET 31 | 32 | token_network_address: TokenNetworkAddress = field(init=False, repr=False) 33 | canonical_identifier: CanonicalIdentifier 34 | target_state: TargetTransferState = field(repr=False) 35 | 36 | def __post_init__(self) -> None: 37 | # Mypy does not allow overringing the `token_network_address` field 38 | # with a property, right now (see 39 | # https://github.com/python/mypy/issues/4125). So we use this 40 | # combination of `init=False` and `__post_init__`. 41 | self.token_network_address = self.canonical_identifier.token_network_address 42 | 43 | @property 44 | def channel_identifier(self) -> ChannelID: 45 | return self.canonical_identifier.channel_identifier 46 | -------------------------------------------------------------------------------- /raiden/transfer/routes.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Tuple 2 | 3 | from raiden.transfer.state import NettingChannelState, NetworkState, RouteState 4 | from raiden.utils.typing import Address, ChannelID, List, NodeNetworkStateMap, TokenNetworkAddress 5 | 6 | 7 | def filter_reachable_routes( 8 | route_states: List[RouteState], 9 | nodeaddresses_to_networkstates: NodeNetworkStateMap, 10 | our_address: Address, 11 | ) -> List[RouteState]: 12 | """This function makes sure we use reachable routes only.""" 13 | # TODO this function is not used anymore (only in tests), probably can be removed 14 | 15 | filtered_routes = [] 16 | for route in route_states: 17 | next_hop = route.hop_after(our_address) 18 | if not next_hop: 19 | continue 20 | if nodeaddresses_to_networkstates.get(next_hop) == NetworkState.REACHABLE: 21 | filtered_routes.append(route) 22 | return filtered_routes 23 | 24 | 25 | # TODO: change function for swaps 26 | # * use token network address in route state 27 | # * check if token_network_address parameter is still needed 28 | # * if yes, check that the right one is passed by all callers 29 | # * change blacklisted_channel_ids to contain the TN, too 30 | def filter_acceptable_routes( 31 | route_states: List[RouteState], 32 | blacklisted_channel_ids: List[ChannelID], 33 | addresses_to_channel: Dict[Tuple[TokenNetworkAddress, Address], NettingChannelState], 34 | token_network_address: TokenNetworkAddress, 35 | our_address: Address, 36 | ) -> List[RouteState]: 37 | """Keeps only routes whose forward_channel is not in the list of blacklisted channels""" 38 | 39 | acceptable_routes = [] 40 | for route in route_states: 41 | next_hop = route.hop_after(our_address) 42 | if not next_hop: 43 | continue 44 | channel = addresses_to_channel.get((token_network_address, next_hop)) 45 | if channel is None: 46 | continue 47 | if channel.identifier not in blacklisted_channel_ids: 48 | acceptable_routes.append(route) 49 | return acceptable_routes 50 | -------------------------------------------------------------------------------- /raiden/transfer/secret_registry.py: -------------------------------------------------------------------------------- 1 | from raiden.transfer.architecture import Event 2 | from raiden.transfer.channel import get_status 3 | from raiden.transfer.events import ContractSendSecretReveal 4 | from raiden.transfer.state import CHANNEL_STATES_UP_TO_CLOSED, NettingChannelState 5 | from raiden.utils.typing import BlockExpiration, BlockHash, List, Secret, T_Secret, typecheck 6 | 7 | 8 | def events_for_onchain_secretreveal( 9 | channel_state: NettingChannelState, 10 | secret: Secret, 11 | expiration: BlockExpiration, 12 | block_hash: BlockHash, 13 | ) -> List[Event]: 14 | events: List[Event] = [] 15 | 16 | typecheck(secret, T_Secret) 17 | 18 | if get_status(channel_state) in CHANNEL_STATES_UP_TO_CLOSED: 19 | reveal_event = ContractSendSecretReveal( 20 | expiration=expiration, secret=secret, triggered_by_block_hash=block_hash 21 | ) 22 | events.append(reveal_event) 23 | 24 | return events 25 | -------------------------------------------------------------------------------- /raiden/transfer/utils/__init__.py: -------------------------------------------------------------------------------- 1 | import random 2 | from random import Random 3 | from typing import TYPE_CHECKING 4 | 5 | from eth_hash.auto import keccak 6 | 7 | from raiden.constants import EMPTY_HASH, LOCKSROOT_OF_NO_LOCKS 8 | from raiden.utils.typing import ( 9 | Any, 10 | BalanceHash, 11 | LockedAmount, 12 | Locksroot, 13 | SecretHash, 14 | TokenAmount, 15 | Union, 16 | ) 17 | 18 | 19 | if TYPE_CHECKING: 20 | # pylint: disable=unused-import 21 | from raiden.transfer.mediated_transfer.state_change import ReceiveSecretReveal # noqa: F401 22 | from raiden.transfer.state_change import ContractReceiveSecretReveal # noqa: F401 23 | 24 | 25 | def hash_balance_data( 26 | transferred_amount: TokenAmount, locked_amount: LockedAmount, locksroot: Locksroot 27 | ) -> BalanceHash: 28 | assert locksroot != b"", "Can't hash empty locksroot" 29 | assert len(locksroot) == 32, "Locksroot has wrong length" 30 | if transferred_amount == 0 and locked_amount == 0 and locksroot == LOCKSROOT_OF_NO_LOCKS: 31 | return BalanceHash(EMPTY_HASH) 32 | 33 | return BalanceHash( 34 | keccak( 35 | transferred_amount.to_bytes(32, byteorder="big") 36 | + locked_amount.to_bytes(32, byteorder="big") 37 | + locksroot 38 | ) 39 | ) 40 | 41 | 42 | def pseudo_random_generator_from_json(data: Any) -> Random: 43 | # JSON serializes a tuple as a list 44 | pseudo_random_generator = random.Random() 45 | state = list(data["pseudo_random_generator"]) # copy 46 | state[1] = tuple(state[1]) # fix type 47 | pseudo_random_generator.setstate(tuple(state)) 48 | 49 | return pseudo_random_generator 50 | 51 | 52 | def is_valid_secret_reveal( 53 | state_change: Union["ContractReceiveSecretReveal", "ReceiveSecretReveal"], 54 | transfer_secrethash: SecretHash, 55 | ) -> bool: 56 | return state_change.secrethash == transfer_secrethash 57 | -------------------------------------------------------------------------------- /raiden/ui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/ui/__init__.py -------------------------------------------------------------------------------- /raiden/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/utils/__init__.py -------------------------------------------------------------------------------- /raiden/utils/copy.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | from typing import TypeVar 3 | 4 | T = TypeVar("T") 5 | 6 | 7 | def deepcopy(data: T) -> T: 8 | """Profiling `deepcopy.deepcopy` show that this function is very slow for 9 | largish objects (around 1MB of data). Since most of our objects don't use 10 | nested classes, this can be circumvented by using pickle to serialize and 11 | deserialize a new copy of the objects. 12 | """ 13 | return pickle.loads(pickle.dumps(data, pickle.HIGHEST_PROTOCOL)) 14 | -------------------------------------------------------------------------------- /raiden/utils/datastructures.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Mapping 2 | from itertools import zip_longest 3 | from typing import Iterable, Tuple 4 | 5 | 6 | def merge_dict(to_update: dict, other_dict: dict) -> None: 7 | """merges b into a""" 8 | for key, value in other_dict.items(): 9 | has_map = isinstance(value, Mapping) and isinstance(to_update.get(key, None), Mapping) 10 | 11 | if has_map: 12 | merge_dict(to_update[key], value) 13 | else: 14 | to_update[key] = value 15 | 16 | 17 | def split_in_pairs(arg: Iterable) -> Iterable[Tuple]: 18 | """Split given iterable in pairs [a, b, c, d, e] -> [(a, b), (c, d), (e, None)]""" 19 | # We are using zip_longest with one clever hack: 20 | # https://docs.python.org/3/library/itertools.html#itertools.zip_longest 21 | # We create an iterator out of the list and then pass the same iterator to 22 | # the function two times. Thus the function consumes a different element 23 | # from the iterator each time and produces the desired result. 24 | iterator = iter(arg) 25 | return zip_longest(iterator, iterator) 26 | -------------------------------------------------------------------------------- /raiden/utils/gevent.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Callable 2 | 3 | from gevent import Greenlet 4 | 5 | 6 | def spawn_named(name: str, task: Callable, *args: Any, **kwargs: Any) -> Greenlet: 7 | """Helper function to spawn a greenlet with a name.""" 8 | 9 | greenlet = Greenlet(task, *args, **kwargs) 10 | greenlet.name = name 11 | 12 | greenlet.start() 13 | 14 | return greenlet 15 | -------------------------------------------------------------------------------- /raiden/utils/keys.py: -------------------------------------------------------------------------------- 1 | from eth_keys import keys 2 | 3 | from raiden.utils.predicates import ishash 4 | from raiden.utils.typing import Address, PublicKey 5 | 6 | 7 | def privatekey_to_publickey(private_key_bin: bytes) -> PublicKey: 8 | """Returns public key in bitcoins 'bin' encoding.""" 9 | if not ishash(private_key_bin): 10 | raise ValueError("private_key_bin format mismatch. maybe hex encoded?") 11 | return keys.PrivateKey(private_key_bin).public_key.to_bytes() 12 | 13 | 14 | def privatekey_to_address(private_key_bin: bytes) -> Address: 15 | return keys.PrivateKey(private_key_bin).public_key.to_canonical_address() 16 | -------------------------------------------------------------------------------- /raiden/utils/logging.py: -------------------------------------------------------------------------------- 1 | from copy import deepcopy 2 | 3 | from raiden.utils.typing import Dict 4 | 5 | 6 | def redact_secret(data: Dict) -> Dict: 7 | """Modify `data` and replace keys named `secret`.""" 8 | if not isinstance(data, dict): 9 | raise ValueError("data must be a dict.") 10 | 11 | # FIXME: assess performance impact of this deepcopy 12 | data_copy = deepcopy(data) 13 | stack = [data_copy] 14 | 15 | while stack: 16 | current = stack.pop() 17 | 18 | if "secret" in current: 19 | current["secret"] = "" 20 | else: 21 | stack.extend(value for value in current.values() if isinstance(value, dict)) 22 | 23 | return data_copy 24 | -------------------------------------------------------------------------------- /raiden/utils/notifying_queue.py: -------------------------------------------------------------------------------- 1 | from typing import Generic, Iterable, List, TypeVar 2 | 3 | from gevent.event import Event 4 | from gevent.queue import Queue 5 | 6 | T = TypeVar("T") 7 | 8 | 9 | class NotifyingQueue(Event, Generic[T]): 10 | """This is not the same as a JoinableQueue. Here, instead of waiting for 11 | all the work to be processed, the wait is for work to be available. 12 | """ 13 | 14 | def __init__(self, maxsize: int = None, items: Iterable[T] = ()) -> None: 15 | super().__init__() 16 | self.queue = Queue(maxsize, items) 17 | 18 | if items: 19 | self.set() 20 | 21 | def put(self, item: T) -> None: 22 | """Add new item to the queue.""" 23 | self.queue.put(item) 24 | self.set() 25 | 26 | def get(self, block: bool = True, timeout: float = None) -> T: 27 | """Removes and returns an item from the queue.""" 28 | value = self.queue.get(block, timeout) 29 | if self.queue.empty(): 30 | self.clear() 31 | return value 32 | 33 | def peek(self, block: bool = True, timeout: float = None) -> T: 34 | return self.queue.peek(block, timeout) 35 | 36 | def __len__(self) -> int: 37 | return len(self.queue) 38 | 39 | def copy(self) -> List[T]: 40 | """Copies the current queue items.""" 41 | copy = self.queue.copy() 42 | 43 | result = [] 44 | while not copy.empty(): 45 | result.append(copy.get_nowait()) 46 | return result 47 | 48 | def __repr__(self) -> str: 49 | return f"NotifyingQueue(id={id(self)}, num_items={len(self.queue)})" 50 | -------------------------------------------------------------------------------- /raiden/utils/predicates.py: -------------------------------------------------------------------------------- 1 | def ishash(data: bytes) -> bool: 2 | return len(data) == 32 3 | -------------------------------------------------------------------------------- /raiden/utils/profiling/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/raiden/utils/profiling/__init__.py -------------------------------------------------------------------------------- /raiden/utils/profiling/constants.py: -------------------------------------------------------------------------------- 1 | from datetime import timedelta 2 | 3 | MEGA = float(2**20) 4 | SECOND = 1 5 | MINUTE = 60 * SECOND 6 | ONESECOND_TIMEDELTA = timedelta(days=0, seconds=1) 7 | INTERVAL_SECONDS = 1.0 8 | -------------------------------------------------------------------------------- /raiden/utils/profiling/cpu.py: -------------------------------------------------------------------------------- 1 | import os 2 | from datetime import datetime 3 | 4 | import gevent_profiler 5 | import GreenletProfiler 6 | 7 | 8 | class CpuProfiler: 9 | def __init__(self, datadir: str) -> None: 10 | # create a new file every time instead of overwritting the latest profiling 11 | summary_file = "{:%Y%m%d_%H%M}_profile_summary".format(datetime.now()) 12 | stats_file = "{:%Y%m%d_%H%M}_profile_stats".format(datetime.now()) 13 | 14 | summary_path = os.path.join(datadir, summary_file) 15 | stats_path = os.path.join(datadir, stats_file) 16 | 17 | gevent_profiler.set_trace_output(None) 18 | gevent_profiler.set_summary_output(summary_path) 19 | gevent_profiler.set_stats_output(stats_path) 20 | 21 | GreenletProfiler.set_clock_type("cpu") 22 | GreenletProfiler.start() 23 | # gevent_profiler.attach() 24 | 25 | self.datadir = datadir 26 | 27 | def stop(self) -> None: 28 | GreenletProfiler.stop() 29 | # gevent_profiler.detach() 30 | 31 | greenlet_file = "{:%Y%m%d_%H%M}_profile_greenlet.callgrind".format(datetime.now()) 32 | greenlet_path = os.path.join(self.datadir, greenlet_file) 33 | 34 | stats = GreenletProfiler.get_func_stats() 35 | stats.print_all() 36 | stats.save(greenlet_path, type="callgrind") 37 | -------------------------------------------------------------------------------- /raiden/utils/profiling/memory.py: -------------------------------------------------------------------------------- 1 | import guppy 2 | from gevent import Greenlet 3 | from gevent.event import Event 4 | from structlog import get_logger 5 | 6 | log = get_logger(__name__) 7 | 8 | 9 | class MemoryLogger: 10 | def __init__(self, interval: float) -> None: 11 | self._interval = interval 12 | self._greenlet = Greenlet(self._run) 13 | self._greenlet.name = "MemoryLogger" 14 | self._stop = Event() 15 | 16 | def start(self) -> Greenlet: 17 | self._greenlet.start() 18 | return self._greenlet 19 | 20 | def stop(self): 21 | self._stop.set() 22 | 23 | def _run(self) -> None: 24 | while not self._stop.is_set(): 25 | heap = guppy.hpy().heap() 26 | log.debug("Memory report", size=heap.domisize, objects=heap.count) 27 | self._stop.wait(self._interval) 28 | -------------------------------------------------------------------------------- /raiden/utils/profiling/timer.py: -------------------------------------------------------------------------------- 1 | import signal 2 | from types import FrameType 3 | from typing import Callable 4 | 5 | from .constants import INTERVAL_SECONDS 6 | 7 | # TIMER_SIGNAL = signal.SIGALRM 8 | # TIMER = signal.ITIMER_REAL 9 | # TIMER_SIGNAL = signal.SIGVTALRM 10 | # TIMER = signal.ITIMER_VIRTUAL 11 | TIMER = signal.ITIMER_PROF 12 | TIMER_SIGNAL = signal.SIGPROF 13 | 14 | SignalHandler = Callable[[int, FrameType], None] 15 | 16 | 17 | class Timer: 18 | def __init__( 19 | self, 20 | callback: SignalHandler, 21 | timer: int = TIMER, 22 | interval: float = INTERVAL_SECONDS, 23 | timer_signal: int = TIMER_SIGNAL, 24 | ) -> None: 25 | 26 | assert callable(callback), "callback must be callable" 27 | 28 | signal.signal(timer_signal, self.callback) # type: ignore 29 | signal.setitimer(timer, interval, interval) 30 | 31 | self._callback = callback 32 | 33 | def callback(self, signum: int, stack: FrameType) -> None: 34 | self._callback(signum, stack) 35 | 36 | def stop(self) -> None: 37 | del self._callback 38 | 39 | signal.signal(TIMER_SIGNAL, signal.SIG_IGN) 40 | 41 | def __bool__(self) -> bool: 42 | # we're always truthy 43 | return True 44 | -------------------------------------------------------------------------------- /raiden/utils/profiling/trace.py: -------------------------------------------------------------------------------- 1 | import gc 2 | import os 3 | import pickle 4 | import time 5 | import tracemalloc 6 | from datetime import datetime 7 | from types import FrameType 8 | 9 | from .constants import MINUTE 10 | from .timer import Timer 11 | 12 | 13 | def _serialize_statistics(statistics): 14 | traceback = [ 15 | frame._frame for frame in statistics.traceback # pylint: disable=protected-access 16 | ] 17 | return (statistics.count, statistics.size, traceback) 18 | 19 | 20 | class TraceProfiler: 21 | def __init__(self, datadir: str) -> None: 22 | self.datadir = datadir 23 | self.profiling = True 24 | 25 | now = datetime.now() 26 | trace_file = "{:%Y%m%d_%H%M}_trace.pickle".format(now) 27 | trace_path = os.path.join(self.datadir, trace_file) 28 | self.trace_stream = open(trace_path, "wb") 29 | tracemalloc.start(15) 30 | 31 | # Take snapshots at slower pace because the size of the samples is not 32 | # negligible, the de/serialization is slow and uses lots of memory. 33 | self.timer = Timer(self._trace, interval=MINUTE * 5) 34 | 35 | def _trace(self, signum: int, frame: FrameType) -> None: # pylint: disable=unused-argument 36 | """Signal handler used to take snapshots of the running process.""" 37 | 38 | # the last pending signal after trace_stop 39 | if not self.profiling: 40 | return 41 | 42 | gc.collect() 43 | 44 | snapshot = tracemalloc.take_snapshot() 45 | timestamp = time.time() 46 | sample_data = (timestamp, snapshot) 47 | 48 | # *Must* use the HIGHEST_PROTOCOL, otherwise the serialization will 49 | # use GBs of memory 50 | pickle.dump(sample_data, self.trace_stream, protocol=pickle.HIGHEST_PROTOCOL) 51 | self.trace_stream.flush() 52 | 53 | def stop(self): 54 | self.profiling = False 55 | 56 | tracemalloc.stop() 57 | self.timer.stop() 58 | self.trace_stream.close() 59 | del self.trace_stream 60 | del self.timer 61 | -------------------------------------------------------------------------------- /raiden/utils/secrethash.py: -------------------------------------------------------------------------------- 1 | from hashlib import sha256 2 | 3 | from raiden.utils.typing import Secret, SecretHash 4 | 5 | 6 | def sha256_secrethash(secret: Secret) -> SecretHash: 7 | """Compute the secret hash using sha256.""" 8 | return SecretHash(sha256(secret).digest()) 9 | -------------------------------------------------------------------------------- /raiden/utils/smart_contracts.py: -------------------------------------------------------------------------------- 1 | from raiden import constants 2 | 3 | 4 | def safe_gas_limit(*estimates: int) -> int: 5 | """Calculates a safe gas limit for a number of gas estimates 6 | including a security margin 7 | """ 8 | assert None not in estimates, "if estimateGas returned None it should not reach here" 9 | calculated_limit = max(estimates) 10 | return int(calculated_limit * constants.GAS_FACTOR) 11 | -------------------------------------------------------------------------------- /raiden/utils/system.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from typing import Any, Dict 4 | 5 | import raiden 6 | from raiden import constants 7 | 8 | 9 | def get_project_root() -> str: 10 | return os.path.dirname(raiden.__file__) 11 | 12 | 13 | def get_system_spec() -> Dict[str, Any]: 14 | """Collect information about the system and installation.""" 15 | import platform 16 | 17 | import pkg_resources 18 | 19 | if sys.platform == "darwin": 20 | system_info = "macOS {} {}".format(platform.mac_ver()[0], platform.architecture()[0]) 21 | else: 22 | system_info = "{} {} {}".format( 23 | platform.system(), 24 | "_".join(part for part in platform.architecture() if part), 25 | platform.release(), 26 | ) 27 | 28 | try: 29 | version = pkg_resources.require(raiden.__name__)[0].version 30 | except (pkg_resources.VersionConflict, pkg_resources.DistributionNotFound): 31 | raise RuntimeError( 32 | "Cannot detect Raiden version. Did you do python setup.py? " 33 | "Refer to https://raiden-network.readthedocs.io/en/latest/" 34 | "overview_and_guide.html#for-developers" 35 | ) 36 | 37 | system_spec = { 38 | "raiden": version, 39 | "raiden_db_version": constants.RAIDEN_DB_VERSION, 40 | "python_implementation": platform.python_implementation(), 41 | "python_version": platform.python_version(), 42 | "system": system_info, 43 | "architecture": platform.machine(), 44 | "distribution": "bundled" if getattr(sys, "frozen", False) else "source", 45 | } 46 | return system_spec 47 | -------------------------------------------------------------------------------- /raiden/utils/timeout.py: -------------------------------------------------------------------------------- 1 | import gevent 2 | from gevent import Greenlet 3 | 4 | from raiden.raiden_service import RaidenService 5 | from raiden.utils.typing import Any, BlockNumber, Callable, Optional 6 | from raiden.waiting import wait_for_block 7 | 8 | 9 | def _timeout_task( # pragma: no unittest 10 | throw: Callable, 11 | exception_to_throw: Exception, 12 | raiden: RaidenService, 13 | block_number: BlockNumber, 14 | retry_timeout: float, 15 | ) -> None: 16 | wait_for_block(raiden, block_number, retry_timeout) 17 | throw(exception_to_throw) 18 | 19 | 20 | class BlockTimeout: # pragma: no unittest 21 | def __init__( 22 | self, 23 | exception_to_throw: Exception, 24 | raiden: RaidenService, 25 | block_number: BlockNumber, 26 | retry_timeout: float, 27 | ) -> None: 28 | self.exception_to_throw = exception_to_throw 29 | self.raiden = raiden 30 | self.block_number = block_number 31 | self.retry_timeout = retry_timeout 32 | self._task: Optional[Greenlet] = None 33 | 34 | def __enter__(self) -> None: 35 | self._task = gevent.spawn( 36 | _timeout_task, 37 | gevent.getcurrent().throw, 38 | self.exception_to_throw, 39 | self.raiden, 40 | self.block_number, 41 | self.retry_timeout, 42 | ) 43 | self._task.name = "timout_task" 44 | 45 | def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: 46 | if self._task: 47 | self._task.kill() 48 | -------------------------------------------------------------------------------- /raiden/utils/tracing.py: -------------------------------------------------------------------------------- 1 | import opentracing 2 | from requests_opentracing import SessionTracing 3 | 4 | from raiden.network.transport.matrix.client import GMatrixClient 5 | 6 | 7 | def matrix_client_enable_requests_tracing(client: GMatrixClient) -> None: 8 | """ 9 | Enables requests tracing the the passed client. 10 | This is done by replacing the ``GMatrixClient.GMatrixHttpApi.session`` attribute with a 11 | ``SessionTracing`` replacement. 12 | """ 13 | 14 | new_session = SessionTracing(propagate=False, span_tags={"target": "matrix"}) 15 | 16 | new_session.adapters = client.api.session.adapters 17 | new_session.hooks = client.api.session.hooks 18 | 19 | client.api.session = new_session 20 | 21 | 22 | def enable_pfs_request_tracing() -> None: 23 | """Enable tracing for pathfinding requests 24 | 25 | This is done by replacing the `Session` object in `raiden.network.pathfinding`. 26 | """ 27 | 28 | from raiden.network import pathfinding 29 | 30 | # Propagate traces to the PFS 31 | tracing_session = SessionTracing(opentracing.tracer, propagate=True) 32 | tracing_session.headers = pathfinding.session.headers 33 | tracing_session.adapters = pathfinding.session.adapters 34 | 35 | pathfinding.session = tracing_session 36 | -------------------------------------------------------------------------------- /raiden/utils/transfers.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | 4 | from raiden import constants 5 | from raiden.utils.typing import PaymentID, Secret 6 | 7 | 8 | def random_secret() -> Secret: 9 | """Return a random 32 byte secret""" 10 | return Secret(os.urandom(constants.SECRET_LENGTH)) 11 | 12 | 13 | def create_default_identifier() -> PaymentID: 14 | """Generates a random identifier.""" 15 | return PaymentID(random.randint(0, constants.UINT64_MAX)) 16 | 17 | 18 | def to_rdn(rei: int) -> float: 19 | """Convert REI value to RDN.""" 20 | return rei / 10**18 21 | -------------------------------------------------------------------------------- /raiden_errors_key.gpg: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | mQENBFwaQ70BCACmTEvAKjtEOt6L9BaonxUdKPsgz7hLWyDz93v4JDq729wSCJT1 4 | bu0A1g0oMNCcin0HYJd7LXLXdIBDN6THl6+W/5yLn7MJlrOfTlSjptHxawwWXMTc 5 | BUCW14QpdPixMYdLJz9wNHt0v3OWSWJLv0phPKBQWicM7iS3vLUCHassc5m7xrSw 6 | iWwpr8DYFq+rS5Z1nIKSHoGCX28Fc5C1S01Y5f/rDngjxZrMcfHreFCLFMzvjZDH 7 | +mZ6RIO7h95IOZKNl08OxaWNbY0l/5QFjpN0Un/+hheWaHM81HN8Ltr7q5CebJvz 8 | 1o1IqhtYIXiaW5g4Gs+HvXBCdLA7wN0DPNs3ABEBAAG0LWJyYWluYm90IGxhYnMg 9 | RXN0LiA8UmFpZGVuRXJyb3JzQGJyYWluYm90LmxpPokBTgQTAQgAOBYhBM5JTzAD 10 | IjO3qTu01uQOVyDVvrI5BQJcGkO9AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheA 11 | AAoJEOQOVyDVvrI5UfEH/2R6qaHnXo5/gRC27BXcGFMgiVRC9//iOUvFmrVE/Xiz 12 | qTPUYZJFA3ZDnZOf3jcoffVa4q2sk+8/p0Vttxpx+1fPFPTwc4fHg/vbWWYVoZGD 13 | DKepRdQDjuTqf4LM+SWC/tZTsXtU1EyZgnuD24uTy/7LTgBWqmm46laHC9VYGNz7 14 | ux5jzgzTJRJlP854Hodqp+Nb46bTQh5cfa92699DPLnu45so1mzKpSNRs1kSYdS0 15 | b1/ZU2cyP7cOb3lMyBQLhaQfrt8xmMRgCxufYSH3O7R3CbWTNccHBAM7uXwMr8gB 16 | iGQmv+OpHsy1QQ3teKhq2yJb8JQQara63Flk/IEK8/C5AQ0EXBpDvQEIANYPXszr 17 | FlICZ/GecyH7W3VYd/Lsp2Olj4oCh9z1E/zOsL5JHGCQusi6d2J1fyHfc3SV6Gtg 18 | 8BDWW9hQ31Tx5RpOdLpqz1F4jjxCeEQZIZCvTuF1Y7CwXv+tXxROxIXenomglLu7 19 | kMPVHfsKlU8HFa7KJk69t3Q0Bs5a4Zj2LCGQcKd5r+GCccNSlvqtK5dn6IFSCNkT 20 | CvSSjPNsf6mCznk8pG/8r1ehrSqHTD5IUtAG4VJUAOqDiet4eDw0N6BmaO0g8hL2 21 | uakyzIGikcEk1kfc+GUuZFRokGNpVfWCIv0tk1cUxXFW3mMJpmpapfM1+w4AKPj/ 22 | tcWVhewJ70jZkZsAEQEAAYkBNgQYAQgAIBYhBM5JTzADIjO3qTu01uQOVyDVvrI5 23 | BQJcGkO9AhsMAAoJEOQOVyDVvrI5FqIH/jXnxR1GZJowVfrg4fCEzxaDwpY9CKtt 24 | gxywf9UQs6fXEQWNwPia94tMEy4N+TEB1YCdCfDuzjyaVr8bPx0frd0FsK0P0+aN 25 | lrC96p6CsQNy9Iy8IZQs46O3dkQT1dVzn9PSGMQaVLKhbwbU4ZgARi0rT+JzSC/6 26 | hY9OYMxjaWzEyLe4CtXUC9jRxAMd8tdiI/1lg92QIRPJ7Zbkvi5VCjQIDGcNz8HZ 27 | SHM4xKbrzTQn8VLpBBoo2Uk5qVhRtL4GUDwnO2YvkvTRa2lcVjamQSFehWmYJaNQ 28 | fydNZVdkP8RxrkddeCjaj7sIqyFdK1nWquplvYqA/nKPk1y310PHRG4= 29 | =0RBq 30 | -----END PGP PUBLIC KEY BLOCK----- 31 | -------------------------------------------------------------------------------- /readthedocs.yml: -------------------------------------------------------------------------------- 1 | build: 2 | image: latest 3 | 4 | python: 5 | version: 3.6 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Fallback for tools expecting this file in the project root 2 | -r requirements/requirements.txt 3 | -------------------------------------------------------------------------------- /requirements/_raiden-dev.txt: -------------------------------------------------------------------------------- 1 | -e .. 2 | -------------------------------------------------------------------------------- /requirements/_raiden.txt: -------------------------------------------------------------------------------- 1 | .. 2 | -------------------------------------------------------------------------------- /requirements/deps: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | cd "${BASH_SOURCE%/*}" 7 | _SCRIPT_NAME=$0 python3 ../tools/pip-compile-wrapper.py "$@" 8 | -------------------------------------------------------------------------------- /requirements/requirements-ci.in: -------------------------------------------------------------------------------- 1 | -r requirements-dev.txt 2 | 3 | pyinstaller>=3.6 4 | macholib # only required on MacOS, but we want to have the same list of deps across all systems 5 | s3cmd 6 | -------------------------------------------------------------------------------- /requirements/requirements-dev.in: -------------------------------------------------------------------------------- 1 | # We need to include the compiled base requirements to make sure we're not pulling in 2 | # incompatible dependencies 3 | -r requirements.txt 4 | 5 | # split out to allow faster building of docs and to not require python 3.7 6 | # since they don't support it in RTD yet: https://github.com/rtfd/readthedocs.org/issues/4713 7 | # FIXME: Temporarily remove docs requirements from dev until releases removes the pin of 8 | # `semantic-version<2.7`. See: https://github.com/bitprophet/releases/pull/86 9 | # -r requirements-docs.txt 10 | 11 | # Dependencies 12 | pip-tools~=6.6.0 # this is used by 'deps.py' 13 | 14 | # Linting 15 | flake8 16 | flake8-comprehensions 17 | flake8-bugbear 18 | flake8-tuple 19 | isort 20 | pylint 21 | mypy 22 | black==22.3.0 23 | types-requests 24 | types-flask 25 | types-pkg_resources 26 | types-toml 27 | types-PyYAML 28 | types-cachetools 29 | types-filelock 30 | 31 | # Testing 32 | pytest 33 | pytest-random 34 | pytest-select 35 | pytest-xdist 36 | grequests 37 | pexpect 38 | hypothesis 39 | raiden-api-client>=1.1.1 40 | responses 41 | flaky 42 | Faker 43 | 44 | # Debugging 45 | ipython 46 | pdbpp 47 | colour 48 | py-spy 49 | nest-asyncio 50 | 51 | # Continuous Integration 52 | coverage 53 | 54 | # Release 55 | bump2version 56 | 57 | # Test support 58 | matrix-synapse 59 | 60 | # Mac build failure 61 | importlib-metadata<4.12 62 | -------------------------------------------------------------------------------- /requirements/requirements-docs.in: -------------------------------------------------------------------------------- 1 | # Documentation 2 | sphinx 3 | sphinx_rtd_theme 4 | sphinxcontrib-httpdomain 5 | sphinxcontrib-httpexample 6 | sphinxcontrib-images 7 | releases 8 | -------------------------------------------------------------------------------- /requirements/requirements.in: -------------------------------------------------------------------------------- 1 | aiortc 2 | aiortc-pyav-stub>=0.1.1 3 | av>=8.0.3 4 | cachetools 5 | canonicaljson 6 | click>=8.0.0a1 7 | coincurve 8 | colorama 9 | eth-keyfile 10 | eth-keys 11 | eth-utils 12 | eciespy 13 | filelock 14 | Flask 15 | Flask-Cors 16 | Flask-RESTful 17 | gevent>=1.5a3 18 | guppy3 19 | marshmallow-dataclass[union] 20 | marshmallow-polyfield 21 | marshmallow 22 | marshmallow_enum 23 | matrix-client==0.3.2 24 | mirakuru==2.1.2 25 | packaging 26 | psutil 27 | pysha3 28 | toml 29 | raiden-contracts>=0.40.0,<0.41.0 30 | raiden-webui 31 | requests 32 | structlog 33 | web3 34 | typing_extensions>=3.10,<5.0 35 | objgraph 36 | ulid-py 37 | jaeger-client 38 | dbapi-opentracing 39 | Flask-Opentracing 40 | requests-opentracing 41 | -------------------------------------------------------------------------------- /stubs/gevent/__init__.pyi: -------------------------------------------------------------------------------- 1 | from signal import _HANDLER, _SIGNUM 2 | from typing import Any 3 | 4 | from gevent.greenlet import Greenlet as Greenlet, joinall as joinall 5 | from gevent.hub import GreenletExit as GreenletExit, sleep as sleep 6 | 7 | spawn = Greenlet.spawn 8 | 9 | class signal(object): 10 | def __init__( 11 | self, signalnum: _SIGNUM, handler: _HANDLER, *args: Any, **kwargs: Any 12 | ) -> _HANDLER: ... 13 | -------------------------------------------------------------------------------- /stubs/gevent/event.pyi: -------------------------------------------------------------------------------- 1 | from typing import Generic, TypeVar 2 | 3 | T = TypeVar("T") 4 | 5 | class AsyncResult(Generic[T]): 6 | def get(self, block: bool = True, timeout: float = None) -> T: ... 7 | def set(self, value: T = None) -> None: ... 8 | -------------------------------------------------------------------------------- /stubs/gevent/greenlet.pyi: -------------------------------------------------------------------------------- 1 | from sys import _OptExcInfo 2 | from typing import Any, Callable, Collection, Dict, Generic, Optional, Tuple, TypeVar 3 | 4 | from typing_extensions import Protocol 5 | 6 | RawlinkCallback = Callable[["Greenlet", Any], None] 7 | T = TypeVar("T") 8 | 9 | class WaitProtocol(Protocol): 10 | def rawlink(self, callback: RawlinkCallback): ... 11 | def unlink(self, callback: RawlinkCallback): ... 12 | 13 | def joinall( 14 | greenlets: Collection[WaitProtocol], 15 | timeout: float = None, 16 | raise_error: bool = False, 17 | count: int = None, 18 | ): ... 19 | 20 | class Greenlet(Generic[T]): 21 | name: str 22 | dead: bool 23 | value: Optional[T] 24 | exception: Optional[Exception] 25 | exc_info: _OptExcInfo 26 | args: Tuple[Any] 27 | kwargs: Dict[str, Any] 28 | def __init__(self, run: Callable = None, *args: Any, **kwargs: Any) -> None: ... 29 | def ready(self) -> bool: ... 30 | def successful(self) -> bool: ... 31 | def throw(self, *args: Any) -> None: ... 32 | def start(self) -> None: ... 33 | def start_later(self, seconds: float): ... 34 | @classmethod 35 | def spawn(cls, *args: Any, **kwargs: Any) -> "Greenlet": ... 36 | @classmethod 37 | def spawn_later(cls, seconds: float, *args: Any, **kwargs: Any) -> "Greenlet": ... 38 | def kill( 39 | self, exception: Exception = ..., block: bool = True, timeout: float = None 40 | ) -> None: ... 41 | def get(self, block: bool = True, timeout: float = None) -> T: ... 42 | def join(self, timeout: float = None) -> None: ... 43 | def run(self) -> None: ... 44 | def has_links(self) -> bool: ... 45 | def rawlink(self, callback: RawlinkCallback): ... 46 | def link(self, callback: Callable, SpawnedLink: Callable = ...) -> None: ... 47 | def unlink(self, callback: RawlinkCallback): ... 48 | def unlink_all(self) -> None: ... 49 | def link_value(self, callback: Callable, SpawnedLink: Callable = ...): ... 50 | def link_exception(self, callback: Callable, SpawnedLink: Callable = ...): ... 51 | -------------------------------------------------------------------------------- /stubs/gevent/hub.pyi: -------------------------------------------------------------------------------- 1 | from greenlet import GreenletExit as GreenletExit 2 | 3 | 4 | def sleep(seconds: float = 0, ref: bool = True) -> None: ... 5 | -------------------------------------------------------------------------------- /stubs/gevent/monkey.pyi: -------------------------------------------------------------------------------- 1 | def patch_all( 2 | socket: bool = True, 3 | dns: bool = True, 4 | time: bool = True, 5 | select: bool = True, 6 | thread: bool = True, 7 | os: bool = True, 8 | ssl: bool = True, 9 | httplib: bool = False, # Deprecated, to be removed. 10 | subprocess: bool = True, 11 | sys: bool = False, 12 | aggressive: bool = True, 13 | Event: bool = True, 14 | builtins: bool = True, 15 | signal: bool = True, 16 | queue: bool = True, 17 | **kwargs, 18 | ): ... 19 | -------------------------------------------------------------------------------- /stubs/greenlet.pyi: -------------------------------------------------------------------------------- 1 | class GreenletExit(Exception): 2 | pass 3 | -------------------------------------------------------------------------------- /stubs/pytest.pyi: -------------------------------------------------------------------------------- 1 | from typing import Any, Callable, List, Tuple, TypeVar, Union, overload 2 | 3 | from typing_extensions import Literal 4 | 5 | FuncType = Callable[..., Any] 6 | F = TypeVar("F", bound=FuncType) 7 | 8 | Scopes = Union[ 9 | Literal["function"], 10 | Literal["class"], 11 | Literal["module"], 12 | Literal["package"], 13 | Literal["session"], 14 | ] 15 | 16 | GenerativeParams = List[Tuple[Any, ...]] 17 | @overload 18 | def fixture(scope: F) -> F: ... 19 | @overload 20 | def fixture( 21 | scope: Scopes = "function", 22 | params: GenerativeParams = None, 23 | autouse: bool = False, 24 | ids: List[str] = None, 25 | name: str = None, 26 | ): ... 27 | -------------------------------------------------------------------------------- /stubs/structlog/__init__.pyi: -------------------------------------------------------------------------------- 1 | def get_logger(*args, **initial_values): ... 2 | -------------------------------------------------------------------------------- /tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/tools/__init__.py -------------------------------------------------------------------------------- /tools/_compat.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Functions for compatibility between GNU and BSD/macOS systems 4 | 5 | get_coreutils_command() { 6 | command_name=$1 7 | command_name_gprefix="g${command_name}" 8 | 9 | if [[ $(uname -s) == Darwin ]]; then 10 | if ! (type "${command_name_gprefix}" &> /dev/null); then 11 | >&2 echo -e "GNU ${command_name} is required. Install with:\n brew install coreutils" 12 | exit 1 13 | fi 14 | command_name="$command_name_gprefix" 15 | fi 16 | echo "$command_name" 17 | } 18 | -------------------------------------------------------------------------------- /tools/debugging/channels_with_minimum_balance_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "networks": { 3 | "0xf9BA8aDF7F7024D7de8eB37b4c981CFFe3C88Ea7": [ 4 | { 5 | "node1": "node0", 6 | "minimum_capacity1": 1130220, 7 | "node2": "node1", 8 | "minimum_capacity2": 1130220 9 | }, 10 | { 11 | "node1": "node1", 12 | "minimum_capacity1": 1130220, 13 | "node2": "node2", 14 | "minimum_capacity2": 1130220 15 | } 16 | ] 17 | }, 18 | "nodes": { 19 | "node0": { 20 | "address": "0x5c11cc525590d9Ff8C7E4Af1704A39Df6c3884b5", 21 | "endpoint": "http://127.0.0.1:5000" 22 | }, 23 | "node1": { 24 | "address": "0xa9FA88442c404716623155d150c526D8F1ae82E5", 25 | "endpoint": "http://127.0.0.1:5001" 26 | }, 27 | "node2": { 28 | "address": "0x9df878E37C4B851bbe44841e72299F6f317C685C", 29 | "endpoint": "http://127.0.0.1:5002" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tools/debugging/matrix/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raiden-network/raiden/90cd5a6cc27e31088a39fd35c0dccf89871dfa88/tools/debugging/matrix/__init__.py -------------------------------------------------------------------------------- /tools/debugging/matrix/api_shell.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | 4 | import click 5 | import IPython 6 | from eth_utils import encode_hex, to_normalized_address 7 | 8 | from raiden.accounts import AccountManager 9 | from raiden.network.transport.matrix.client import GMatrixHttpApi 10 | from raiden.utils.cli import ADDRESS_TYPE 11 | from raiden.utils.formatting import to_checksum_address 12 | from raiden.utils.signer import LocalSigner 13 | from raiden.utils.typing import Address 14 | 15 | 16 | @click.command() 17 | @click.option( 18 | "--address", 19 | help="The ethereum address for which to get a login", 20 | type=ADDRESS_TYPE, 21 | required=True, 22 | ) 23 | @click.password_option( 24 | "--password", confirmation_prompt=False, help="Password to unlock the keystore file." 25 | ) 26 | @click.option( 27 | "--server", help="Matrix server to connect to", default="https://transport01.raiden.network" 28 | ) 29 | def matrix_api_shell(address: Address, password: str, server: str) -> None: 30 | am = AccountManager(os.path.expanduser("~/.ethereum/keystore")) 31 | signer = LocalSigner(am.get_privkey(to_checksum_address(address), password)) 32 | server_name = server.split("//")[1] 33 | matrix_password = encode_hex(signer.sign(server_name.encode())) 34 | 35 | api = GMatrixHttpApi(server) 36 | resp = api.login( 37 | "m.login.password", user=to_normalized_address(address), password=matrix_password 38 | ) 39 | api.token = resp["access_token"] 40 | IPython.embed(header=f"Use the `api` object to interact with matrix on {server}.") 41 | 42 | 43 | if __name__ == "__main__": 44 | matrix_api_shell() # pylint: disable=no-value-for-parameter 45 | -------------------------------------------------------------------------------- /tools/debugging/matrix/get_login.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import sys 4 | 5 | import click 6 | from eth_utils import encode_hex, to_normalized_address 7 | 8 | from raiden.accounts import AccountManager 9 | from raiden.utils.cli import ADDRESS_TYPE 10 | from raiden.utils.formatting import to_checksum_address 11 | from raiden.utils.signer import LocalSigner 12 | from raiden.utils.typing import Address 13 | 14 | 15 | @click.command() 16 | @click.option( 17 | "--address", 18 | help="The ethereum address for which to get a login", 19 | type=ADDRESS_TYPE, 20 | required=True, 21 | ) 22 | @click.password_option( 23 | "--password", 24 | confirmation_prompt=False, 25 | help="Password to unlock the keystore file.", 26 | default="", 27 | ) 28 | def get_login(address: Address, password: str) -> None: 29 | path = os.path.expanduser("~/.ethereum/keystore") 30 | if sys.platform.startswith("darwin"): 31 | path = os.path.expanduser("~/Library/Ethereum/keystore") 32 | 33 | am = AccountManager(path) 34 | signer = LocalSigner(am.get_privkey(to_checksum_address(address), password)) 35 | 36 | print(f"Username: {to_normalized_address(address)}") 37 | print("Password:") 38 | for i in range(1, 5): 39 | print( 40 | f"\ttransport {i:02d}:", 41 | encode_hex(signer.sign(f"transport.transport{i:02d}.raiden.network".encode())), 42 | ) 43 | 44 | 45 | if __name__ == "__main__": 46 | get_login() # pylint: disable=no-value-for-parameter 47 | -------------------------------------------------------------------------------- /tools/debugging/matrix/presence_update.py: -------------------------------------------------------------------------------- 1 | import gevent.monkey 2 | 3 | gevent.monkey.patch_all() 4 | 5 | # isort: split 6 | 7 | from typing import Optional 8 | 9 | import gevent 10 | 11 | 12 | from raiden.network.transport.matrix.rtc.utils import setup_asyncio_event_loop 13 | 14 | 15 | setup_asyncio_event_loop() 16 | 17 | if True: 18 | import sys 19 | from raiden.network.transport.matrix.client import GMatrixClient 20 | from raiden.network.transport.matrix.utils import UserPresence 21 | 22 | USER_ID = "@xxx:server1" 23 | ACCESS_TOKEN = "REDACTED" 24 | ROOM_ALIAS = "#room_name:server1" 25 | 26 | 27 | def main() -> None: 28 | host = sys.argv[1] 29 | 30 | client = GMatrixClient(lambda x: False, host, user_id=USER_ID, token=ACCESS_TOKEN) 31 | client.join_room(ROOM_ALIAS) 32 | 33 | current_presence: Optional[str] = "offline" 34 | while True: 35 | if current_presence == "offline": 36 | client.set_presence_state(UserPresence.ONLINE.value) 37 | else: 38 | client.set_presence_state(UserPresence.OFFLINE.value) 39 | 40 | # Confirm user presence 41 | current_presence = client.get_user_presence(USER_ID) 42 | 43 | print("Change status to: ", current_presence) 44 | 45 | gevent.sleep(5) 46 | 47 | 48 | if __name__ == "__main__": 49 | main() 50 | -------------------------------------------------------------------------------- /tools/debugging/mint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | set -e 4 | 5 | info() { 6 | printf "$(tput bold)$(tput setaf 4) -> %s$(tput sgr0)\n" "$1" 7 | } 8 | 9 | die() { 10 | printf "$(tput bold)$(tput setaf 1) -> %s$(tput sgr0)\n" "$1" >&2 11 | exit 1 12 | } 13 | 14 | require_bin() { 15 | hash "$1" 2> /dev/null || { 16 | die "Required binary was not found ${1}" 17 | } 18 | } 19 | 20 | require_bin jq 21 | require_bin http 22 | require_bin parallel 23 | 24 | [ $# -lt 2 ] && die "${0} +" 25 | 26 | # shellcheck disable=SC2034 27 | UINT256_MAX=115792089237316195423570985008687907853269984665640564039457584007913129639935 28 | # shellcheck disable=SC2034 29 | UINT128_MAX=340282366920938463463374607431768211455 30 | UINT64_MAX=18446744073709551615 31 | MINT_AMOUNT=$UINT64_MAX 32 | 33 | mint(){ 34 | server=$1 35 | token=$2 36 | mint_amount=$3 37 | 38 | address_url="http://${server}/api/v1/address" 39 | mint_url="http://${server}/api/v1/_testing/tokens/${token}/mint" 40 | 41 | node_address=$(http GET "$address_url" | jq .our_address -r) 42 | 43 | http --ignore-stdin --timeout=600 POST "$mint_url" to="$node_address" value="$mint_amount" 44 | } 45 | 46 | 47 | TOKEN_ADDRES=$1 48 | shift 49 | 50 | # export the symbol to allow the subshell spawned by parallel to use it 51 | export -f mint 52 | 53 | for server in "$@"; do 54 | echo mint "$server" "$TOKEN_ADDRES" "$MINT_AMOUNT" 55 | done | parallel 56 | -------------------------------------------------------------------------------- /tools/debugging/plot/scatter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import argparse 3 | import csv 4 | import datetime 5 | import sys 6 | 7 | from matplotlib import dates, pyplot 8 | from matplotlib.axes import Axes 9 | 10 | 11 | parser = argparse.ArgumentParser() 12 | parser.add_argument("--width", default=1000, help="Configures width of the output in pixels.") 13 | parser.add_argument("--height", default=800, help="Configures height of the output in pixels.") 14 | parser.add_argument( 15 | "--header", help="If the csv does not have a header, use this to give a name to each column" 16 | ) 17 | parser.add_argument( 18 | "output", help="file name for the result image, filetype is inferred from this." 19 | ) 20 | parser.add_argument("x") 21 | parser.add_argument("y") 22 | 23 | args = parser.parse_args() 24 | 25 | 26 | def parse_datetime(data: str) -> datetime.datetime: 27 | return datetime.datetime.fromisoformat(data) 28 | 29 | 30 | def configure_axes(axes: Axes) -> None: 31 | hour_fmt = dates.DateFormatter("%H:%M") 32 | minutes_fmt = dates.DateFormatter("%M") 33 | 34 | axes.xaxis.set_major_locator(dates.HourLocator(interval=1)) 35 | axes.xaxis.set_major_formatter(hour_fmt) 36 | axes.xaxis.set_minor_locator(dates.MinuteLocator(interval=5)) 37 | axes.xaxis.set_minor_formatter(minutes_fmt) 38 | axes.xaxis.set_tick_params(which="major", rotation=90) 39 | axes.xaxis.set_tick_params(which="minor", rotation=90) 40 | 41 | 42 | x_axis = [] 43 | y_axis = [] 44 | 45 | if args.header: 46 | headers = args.header.split(",") 47 | reader = csv.DictReader(sys.stdin, fieldnames=headers) 48 | else: 49 | reader = csv.DictReader(sys.stdin) 50 | 51 | for line in reader: 52 | x_axis.append(parse_datetime(line[args.x])) 53 | y_axis.append(float(line[args.y])) 54 | 55 | dpi = 60 56 | pyplot.figure(figsize=(args.width / dpi, args.height / dpi), dpi=dpi) 57 | 58 | axes = pyplot.gca() 59 | 60 | configure_axes(axes) 61 | axes.set_xlabel(args.x) 62 | axes.set_ylabel(args.y) 63 | axes.set_xlim(min(x_axis), max(x_axis)) 64 | 65 | pyplot.scatter(x_axis, y_axis, alpha=0.2) 66 | pyplot.savefig(args.output) 67 | -------------------------------------------------------------------------------- /tools/debugging/sp-download-logs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | for server in "$@"; do 4 | ssh "$server" 'tar -cf - $(find /data -mtime -1) | gzip -c' | tar xzf - 5 | done 6 | -------------------------------------------------------------------------------- /tools/debugging/sp_download_scenario_logs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | cd /var/lib/scenario-player || exit 1; 6 | if [[ -f env.sh ]] ; then 7 | # shellcheck disable=SC1091 8 | source env.sh 9 | else 10 | echo "env.sh is missing" 11 | exit 1 12 | fi 13 | 14 | [[ -n ${DATA_DIR:?missing in env.sh} ]] 15 | [[ -n ${SCENARIOS_DIR:?missing in env.sh} ]] 16 | [[ -n ${SP:?missing in env.sh} ]] 17 | 18 | find_in_array() { 19 | local word=$1 20 | shift 21 | for entry in "$@"; do [[ "$entry" == "$word" ]] && return 0; done 22 | return 1 23 | } 24 | 25 | cd "${DATA_DIR}"/scenarios || exit 1; 26 | for scenario_path in "$SCENARIOS_DIR"/ci/"$SP"/*.yaml; do 27 | scenario_file=$(basename "$scenario_path") 28 | scenario=${scenario_file//.yaml} 29 | 30 | find_in_array "$scenario" "$@" || { 31 | run=$(cat "${scenario}"/run_number.txt) 32 | 33 | find "$scenario"/node_"$run"_* 34 | # shellcheck disable=SC2012 35 | ls "$scenario"/scenario-player-run* -1 -t | head -1 36 | } 37 | done | tar zcf - -T - 38 | -------------------------------------------------------------------------------- /tools/debugging/state_machine_report.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import json 3 | import sys 4 | 5 | for line in open(sys.argv[1]): 6 | data = json.loads(line) 7 | 8 | timestamp = data["timestamp"].split()[-1] 9 | if "state_changes" in data: 10 | state_changes = [sc["_type"].split(".")[-1] for sc in data["state_changes"]] 11 | print(f"> {timestamp}: {', '.join(state_changes)}") 12 | 13 | if "raiden_events" in data: 14 | events = [ev["_type"].split(".")[-1] for ev in data["raiden_events"]] 15 | print(f"< {timestamp}: {', '.join(events)}") 16 | -------------------------------------------------------------------------------- /tools/debugging/stress_test_transfers_config.ini: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | keystore-path = ./keys 3 | eth-rpc-endpoint = parity.goerli.ethnodes.brainbot.com:8545 4 | network-id = goerli 5 | token-address = 0xf9BA8aDF7F7024D7de8eB37b4c981CFFe3C88Ea7 6 | pathfinding-service-address = https://pfs-goerli.services-dev.raiden.network 7 | 8 | [node1] 9 | address = 0x5c11cc525590d9Ff8C7E4Af1704A39Df6c3884b5 10 | password-file = ./keys/pass-UTC--2019-11-22T13-47-42.489814312Z--5c11cc525590d9ff8c7e4af1704a39df6c3884b5 11 | 12 | [node2] 13 | address = 0xa9FA88442c404716623155d150c526D8F1ae82E5 14 | password-file = ./keys/pass-UTC--2019-11-22T13-49-45.745576867Z--a9fa88442c404716623155d150c526d8f1ae82e5 15 | 16 | [node3] 17 | address = 0x9df878E37C4B851bbe44841e72299F6f317C685C 18 | password-file = ./keys/pass-UTC--2019-11-22T13-50-19.904075843Z--9df878e37c4b851bbe44841e72299f6f317c685c 19 | -------------------------------------------------------------------------------- /tools/dummy_resolver_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import json 3 | import logging 4 | from hashlib import sha256 5 | from http.server import BaseHTTPRequestHandler, HTTPServer 6 | 7 | from eth_utils import to_bytes, to_hex 8 | 9 | # The code below simulates XUD resolver functionality. 10 | # It should only be used for testing and should not be used in 11 | # run time or production. 12 | 13 | 14 | def resolve(request): 15 | 16 | preimage = None 17 | 18 | if "secrethash" not in request: 19 | return preimage 20 | 21 | x_secret = "0x2ff886d47b156de00d4cad5d8c332706692b5b572adfe35e6d2f65e92906806e" 22 | x_secret_hash = to_hex(sha256(to_bytes(hexstr=x_secret)).digest()) 23 | 24 | if request["secrethash"] == x_secret_hash: 25 | preimage = {"secret": x_secret} 26 | 27 | return preimage 28 | 29 | 30 | def serve(): 31 | class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): 32 | def do_POST(self): 33 | try: 34 | content_len = int(self.headers.get("Content-Length")) 35 | body = self.rfile.read(content_len) 36 | 37 | preimage = resolve(json.loads(body.decode("utf8"))) 38 | if preimage is None: 39 | self.send_response(404) 40 | self.end_headers() 41 | else: 42 | response = to_bytes(text=json.dumps(preimage)) 43 | self.send_response(200) 44 | self.end_headers() 45 | self.wfile.write(response) 46 | except BaseException: 47 | self.send_response(400) 48 | self.end_headers() 49 | 50 | # TODO: accept port as runtime parameters to allow parallel execution 51 | # of multiple resolvers. 52 | 53 | httpd = HTTPServer(("localhost", 8000), SimpleHTTPRequestHandler) 54 | httpd.serve_forever() 55 | 56 | 57 | if __name__ == "__main__": 58 | logging.basicConfig() 59 | serve() 60 | -------------------------------------------------------------------------------- /tools/pyinstaller_hooks/hook-av.py: -------------------------------------------------------------------------------- 1 | from PyInstaller.utils.hooks import collect_submodules 2 | 3 | 4 | hiddenimports = collect_submodules("av") 5 | -------------------------------------------------------------------------------- /tools/pyinstaller_hooks/hook-coincurve.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import sys 3 | 4 | from PyInstaller.utils.hooks import get_module_file_attribute 5 | 6 | from raiden.utils.typing import List, Tuple 7 | 8 | binaries: List[Tuple[str, str]] = [] 9 | 10 | if sys.platform == "win32": 11 | coincurve_dir = os.path.dirname(get_module_file_attribute("coincurve")) 12 | binaries.append((os.path.join(coincurve_dir, "libsecp256k1.dll"), "coincurve")) 13 | -------------------------------------------------------------------------------- /tools/pyinstaller_hooks/hook-cytoolz.py: -------------------------------------------------------------------------------- 1 | hiddenimports = ["cytoolz.utils", "cytoolz._signatures"] 2 | -------------------------------------------------------------------------------- /tools/pyinstaller_hooks/hook-raiden.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import sys 4 | from typing import List, Tuple 5 | 6 | import pkg_resources 7 | 8 | datas = [] 9 | binaries: List[Tuple[str, str]] = [] 10 | 11 | 12 | # This is only needed until https://github.com/pyinstaller/pyinstaller/issues/3033 is fixed 13 | def copy_metadata(package_name: str) -> List[Tuple[str, str]]: 14 | dist = pkg_resources.get_distribution(package_name) 15 | metadata_dir = dist.egg_info # type: ignore 16 | return [(metadata_dir, metadata_dir[len(dist.location) + len(os.sep) :])] 17 | 18 | 19 | # Add metadata of all required packages to allow pkg_resources.require() to work 20 | required_packages: List[Tuple[str, Tuple[str, ...]]] = [("raiden", ())] 21 | processed_packages = set() # break out of circular dependencies 22 | while required_packages: 23 | req_name, req_extras = required_packages.pop() 24 | for req in pkg_resources.get_distribution(req_name).requires(req_extras): 25 | dep_tuple = (req.project_name, tuple(req.extras)) 26 | if dep_tuple in processed_packages: 27 | continue 28 | 29 | required_packages.append(dep_tuple) 30 | processed_packages.add(dep_tuple) 31 | try: 32 | datas.extend(copy_metadata(req_name)) 33 | except AssertionError: 34 | pass 35 | 36 | if sys.platform == "darwin": 37 | # Include newer (Homebrew) OpenSSL libs if available 38 | openssl_lib_paths = ["/usr/local/Cellar/openssl/"] 39 | for path in openssl_lib_paths: 40 | if os.path.exists(path): 41 | libpath = os.path.join(path, os.listdir(path)[-1], "lib") 42 | for lib in glob.glob("{}/*.dylib".format(libpath)): 43 | binaries.append((lib, ".")) 44 | -------------------------------------------------------------------------------- /tools/pyinstaller_hooks/hook-raiden_contracts.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | 4 | from raiden_contracts.contract_manager import _BASE 5 | 6 | datas = [] 7 | 8 | 9 | for subdir, _, _ in os.walk(_BASE): 10 | for file_path in Path(subdir).glob("*.json"): 11 | datas.append((str(file_path), os.path.basename(subdir))) 12 | -------------------------------------------------------------------------------- /tools/pyinstaller_hooks/hook-raiden_webui.py: -------------------------------------------------------------------------------- 1 | from PyInstaller.utils.hooks import collect_data_files 2 | 3 | datas = collect_data_files("raiden_webui") 4 | -------------------------------------------------------------------------------- /tools/pyinstaller_hooks/runtime_aiortc_pyav_stub.py: -------------------------------------------------------------------------------- 1 | import aiortc_pyav_stub 2 | 3 | 4 | aiortc_pyav_stub.install_as_av() 5 | -------------------------------------------------------------------------------- /tools/pyinstaller_hooks/runtime_encoding.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | """ 4 | File io can fail if there is no locale set. This adds a fallback to `en_US.UTF-8`. 5 | """ 6 | 7 | if not any(k in os.environ for k in ["LC_CTYPE", "LC_ALL", "LANG"]): 8 | print("Warning: No locale set. Falling back to 'en_US.UTF-8'.") 9 | os.environ["LANG"] = "en_US.UTF-8" 10 | -------------------------------------------------------------------------------- /tools/pyinstaller_hooks/runtime_gevent_monkey.py: -------------------------------------------------------------------------------- 1 | from gevent.monkey import patch_all 2 | 3 | patch_all() 4 | -------------------------------------------------------------------------------- /tools/pyinstaller_hooks/runtime_raiden_contracts.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | from raiden_contracts import contract_manager 5 | 6 | # `sys._MEIPASS` is the root of the extracted pyinstaller bundle 7 | base_path = Path(sys._MEIPASS) # type: ignore # pylint: disable=no-member 8 | 9 | # Patch location of compiled contracts. 10 | contract_manager._BASE = base_path 11 | -------------------------------------------------------------------------------- /tools/pylint/__init__.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache, wraps 2 | from typing import Any, Callable, Optional, TypeVar 3 | 4 | from astroid import Module 5 | from astroid.node_classes import NodeNG 6 | 7 | RAIDEN_TESTS_MODULE = "raiden.tests" 8 | 9 | T_NODE = TypeVar("T_NODE", bound=NodeNG) 10 | 11 | 12 | @lru_cache(maxsize=None) 13 | def find_parent(node: NodeNG, scope_type: T_NODE) -> Optional[T_NODE]: 14 | current_node = node 15 | while current_node is not None and not isinstance(current_node, scope_type): 16 | current_node = current_node.parent 17 | return current_node 18 | 19 | 20 | def ignore_tests(func: Callable) -> Callable: 21 | """Decorator that ignores nodes below the raiden.tests module.""" 22 | 23 | @wraps(func) 24 | def decorator(self, node: NodeNG) -> Any: 25 | module_node = find_parent(node, Module) 26 | if module_node is None or module_node.name.startswith(RAIDEN_TESTS_MODULE): 27 | return None 28 | return func(self, node) 29 | 30 | return decorator 31 | -------------------------------------------------------------------------------- /tools/pylint/assert_checker.py: -------------------------------------------------------------------------------- 1 | from pylint.checkers import BaseChecker 2 | from pylint.interfaces import IAstroidChecker 3 | 4 | from . import ignore_tests 5 | 6 | ASSERT_ID = "assert-message" 7 | ASSERT_MSG = "Every assert must have a message describing the error to aid debugging" 8 | 9 | 10 | def register(linter): 11 | linter.register_checker(AssertMessage(linter)) 12 | 13 | 14 | class AssertMessage(BaseChecker): 15 | __implements__ = IAstroidChecker 16 | 17 | name = "assert" 18 | priority = -1 19 | msgs = {"E6492": (ASSERT_MSG, ASSERT_ID, "Assert without message.")} 20 | 21 | @ignore_tests 22 | def visit_assert(self, node): 23 | if len(list(node.get_children())) != 2: 24 | self.add_message(ASSERT_ID, node=node) 25 | -------------------------------------------------------------------------------- /tools/pylint/del_method_checker.py: -------------------------------------------------------------------------------- 1 | from astroid import FunctionDef 2 | from pylint.checkers import BaseChecker 3 | from pylint.interfaces import IAstroidChecker 4 | 5 | from . import find_parent, ignore_tests 6 | 7 | ID_DEL_METHOD = "del-method-used" 8 | ID_EXCEPTION_IN_DEL = "exception-in-del" 9 | MSG_DEL_METHOD = "Avoid using __del__ methods." 10 | MSG_EXCEPTION_IN_DEL = "Don't raise Exceptions in __del__ methods." 11 | 12 | 13 | def register(linter): 14 | linter.register_checker(DelMethod(linter)) 15 | 16 | 17 | class DelMethod(BaseChecker): 18 | __implements__ = IAstroidChecker 19 | 20 | name = "del-method" 21 | priority = -1 22 | msgs = { 23 | "C6493": ( 24 | MSG_DEL_METHOD, 25 | ID_DEL_METHOD, 26 | ( 27 | "__del__ methods are unreliable and should be avoided. " 28 | "Consider using a context manager instead." 29 | ), 30 | ), 31 | "E6494": ( 32 | MSG_EXCEPTION_IN_DEL, 33 | ID_EXCEPTION_IN_DEL, 34 | ( 35 | "Exceptions raised in __del__ methods are not propagated. See the warning " 36 | "at the end of https://docs.python.org/3.7/reference/datamodel.html#object.__del__" 37 | ), 38 | ), 39 | } 40 | 41 | @ignore_tests 42 | def visit_functiondef(self, node): 43 | if node.name == "__del__" and node.is_method(): 44 | self.add_message(ID_DEL_METHOD, node=node) 45 | 46 | @ignore_tests 47 | def visit_raise(self, node): 48 | function_node = find_parent(node, FunctionDef) 49 | if function_node is not None: 50 | if function_node.name == "__del__" and function_node.is_method(): 51 | self.add_message(ID_EXCEPTION_IN_DEL, node=node) 52 | --------------------------------------------------------------------------------