def main() -> None: tmpdir = tempfile.mkdtemp() geth_nodes = [] for i in range(NUM_GETH_NODES): is_miner = i == 0 node_key = PrivateKey(sha3(f"node:{i}".encode())) p2p_port = Port(START_PORT + i) rpc_port = Port(START_RPCPORT + i) description = EthNodeDescription( private_key=node_key, rpc_port=rpc_port, p2p_port=p2p_port, miner=is_miner, extra_config={}, ) geth_nodes.append(description) rpc_endpoint = f"http://127.0.0.1:{START_RPCPORT}" web3 = Web3(HTTPProvider(rpc_endpoint)) random_marker = remove_0x_prefix(hex(random.getrandbits(100))) genesis_description = GenesisDescription( prefunded_accounts=DEFAULT_ACCOUNTS, random_marker=random_marker, chain_id=ChainID(NETWORKNAME_TO_ID["smoketest"]), ) private_chain: ContextManager[ List[JSONRPCExecutor]] = run_private_blockchain( web3=web3, eth_nodes=geth_nodes, base_datadir=tmpdir, log_dir=tmpdir, verbosity="info", genesis_description=genesis_description, ) with private_chain: from IPython import embed embed()
def test_user_deposit_proxy_withdraw( private_keys: List[bytes], web3: Web3, contract_manager: ContractManager, user_deposit_address: Address, ): c0_client = JSONRPCClient(web3, PrivateKey(private_keys[0])) c0_proxy_manager = ProxyManager( rpc_client=c0_client, contract_manager=contract_manager, metadata=ProxyManagerMetadata( token_network_registry_deployed_at=GENESIS_BLOCK_NUMBER, filters_start_at=GENESIS_BLOCK_NUMBER, ), ) c0_user_deposit_proxy = c0_proxy_manager.user_deposit( UserDepositAddress(user_deposit_address), BLOCK_ID_LATEST) withdraw_plan = c0_user_deposit_proxy.get_withdraw_plan( c0_client.address, BLOCK_ID_LATEST) # There should be no withdraw plan assert withdraw_plan.withdraw_block == 0 assert withdraw_plan.withdraw_amount == 0 current_deposit = c0_user_deposit_proxy.get_total_deposit( c0_client.address, BLOCK_ID_LATEST) # None of these are valid plan_withdraw amounts for value in [-1, 0, current_deposit + 1]: with pytest.raises(BrokenPreconditionError): c0_user_deposit_proxy.plan_withdraw(TokenAmount(value), BLOCK_ID_LATEST) # With no plan any withdraw must fail in the precondition check with pytest.raises(BrokenPreconditionError): c0_user_deposit_proxy.withdraw(TokenAmount(1), BLOCK_ID_LATEST) withdraw_amount = TokenAmount(current_deposit // 2) withdraw_block = c0_user_deposit_proxy.plan_withdraw( withdraw_amount, BLOCK_ID_LATEST) # The effective balance must take the planned withdraw into account effective_balance_after_withdraw_plan = c0_user_deposit_proxy.effective_balance( c0_client.address, BLOCK_ID_LATEST) assert effective_balance_after_withdraw_plan == current_deposit - withdraw_amount # Wait until target block - 1. # We set the retry timeout to 0.1 to make sure there is enough time for the failing case # below. c0_client.wait_until_block(BlockNumber(withdraw_block - 1), retry_timeout=0.1) # Withdraw should still fail with pytest.raises(BrokenPreconditionError): c0_user_deposit_proxy.withdraw(TokenAmount(withdraw_amount), BLOCK_ID_LATEST) # Wait the final block c0_user_deposit_proxy.client.wait_until_block(withdraw_block) # Now withdraw must succeed c0_user_deposit_proxy.withdraw(TokenAmount(withdraw_amount), BLOCK_ID_LATEST) # The total deposit must now match the reduced value new_current_deposit = c0_user_deposit_proxy.get_total_deposit( c0_client.address, BLOCK_ID_LATEST) assert new_current_deposit == current_deposit - withdraw_amount
CONTRACT_TOKEN_NETWORK_REGISTRY, CONTRACT_USER_DEPOSIT, TEST_SETTLE_TIMEOUT_MAX, TEST_SETTLE_TIMEOUT_MIN, ) from raiden_contracts.contract_manager import ContractManager, contracts_precompiled_path if TYPE_CHECKING: # pylint: disable=unused-import from raiden.tests.utils.transport import ParsedURL # noqa: F401 # the smoketest will assert that a different endpoint got successfully registered TEST_DEPOSIT_AMOUNT = TokenAmount(5) TEST_PRIVKEY = PrivateKey( b"\xad\xd4\xd3\x10\xba\x04$hy\x1d\xd7\xbf\x7fn\xae\x85\xac" b"\xc4\xdd\x14?\xfa\x81\x0e\xf1\x80\x9aj\x11\xf2\xbcD") TEST_ACCOUNT_ADDRESS = privatekey_to_address(TEST_PRIVKEY) class StepPrinter(Protocol): def __call__(self, description: str, error: bool = False) -> None: ... def ensure_executable(cmd): """look for the given command and make sure it can be executed""" if not shutil.which(cmd): raise ValueError( "Error: unable to locate %s binary.\n" "Make sure it is installed and added to the PATH variable." % cmd)
from raiden.utils.keys import privatekey_to_address from raiden.utils.signing import sha3 from raiden.utils.typing import ChainID, List, Port, PrivateKey, TokenAmount from raiden_contracts.constants import NETWORKNAME_TO_ID NUM_GETH_NODES = 3 NUM_RAIDEN_ACCOUNTS = 10 START_PORT = 30301 START_RPCPORT = 8101 DEFAULT_ACCOUNTS_SEEDS = [ "127.0.0.1:{}".format(START_PORT + i).encode() for i in range(NUM_RAIDEN_ACCOUNTS) ] DEFAULT_ACCOUNTS_KEYS: List[PrivateKey] = [ PrivateKey(keccak(seed)) for seed in DEFAULT_ACCOUNTS_SEEDS ] DEFAULT_ACCOUNTS = [ AccountDescription(privatekey_to_address(key), TokenAmount(DEFAULT_BALANCE)) for key in DEFAULT_ACCOUNTS_KEYS ] def main() -> None: tmpdir = tempfile.mkdtemp() geth_nodes = [] for i in range(NUM_GETH_NODES): is_miner = i == 0 node_key = PrivateKey(sha3(f"node:{i}".encode()))
def test_participant_selection(raiden_network, token_addresses): # pylint: disable=too-many-locals registry_address = raiden_network[0].raiden.default_registry.address one_to_n_address = raiden_network[0].raiden.default_one_to_n_address token_address = token_addresses[0] # connect the first node - this will register the token and open the first channel # Since there is no other nodes available to connect to this call will do nothing more RaidenAPI(raiden_network[0].raiden).token_network_connect( registry_address=registry_address, token_address=token_address, funds=TokenAmount(100)) # Test invalid argument values with pytest.raises(InvalidAmount): RaidenAPI(raiden_network[0].raiden).token_network_connect( registry_address=registry_address, token_address=token_address, funds=TokenAmount(-1)) with pytest.raises(InvalidAmount): RaidenAPI(raiden_network[0].raiden).token_network_connect( registry_address=registry_address, token_address=token_address, funds=TokenAmount(100), joinable_funds_target=2, ) with pytest.raises(InvalidAmount): RaidenAPI(raiden_network[0].raiden).token_network_connect( registry_address=registry_address, token_address=token_address, funds=TokenAmount(100), joinable_funds_target=-1, ) # Call the connect endpoint for all but the first node connect_greenlets = set( gevent.spawn( RaidenAPI(app.raiden).token_network_connect, registry_address, token_address, 100) for app in raiden_network[1:]) gevent.joinall(connect_greenlets, raise_error=True) token_network_address = views.get_token_network_address_by_token_address( views.state_from_raiden(raiden_network[0].raiden), token_network_registry_address=registry_address, token_address=token_address, ) connection_managers = [ app.raiden.connection_manager_for_token_network(token_network_address) for app in raiden_network ] unsaturated_connection_managers = connection_managers[:] exception = AssertionError("Unsaturated connection managers", unsaturated_connection_managers) with gevent.Timeout(120, exception): while unsaturated_connection_managers: for manager in unsaturated_connection_managers: if is_manager_saturated(manager, registry_address, token_address): unsaturated_connection_managers.remove(manager) gevent.sleep(1) assert saturated_count(connection_managers, registry_address, token_address) == len(connection_managers) # ensure unpartitioned network for app in raiden_network: node_state = views.state_from_raiden(app.raiden) network_state = views.get_token_network_by_token_address( node_state, registry_address, token_address) assert network_state is not None for target in raiden_network: if target.raiden.address == app.raiden.address: continue _, routes, _ = routing.get_best_routes( chain_state=node_state, token_network_address=network_state.address, one_to_n_address=one_to_n_address, from_address=app.raiden.address, to_address=target.raiden.address, amount=PaymentAmount(1), previous_address=None, pfs_config=None, privkey=PrivateKey(b""), # not used if pfs is not configured ) assert routes is not None # create a transfer to the leaving node, so we have a channel to settle for app in raiden_network: sender = app.raiden sender_channel = next( (channel_state for channel_state in RaidenAPI( sender).get_channel_list(registry_address=registry_address, token_address=token_address) if channel_state.our_state.contract_balance > 0 and channel_state.partner_state.contract_balance > 0), None, ) # choose a fully funded channel from sender if sender_channel: break assert sender_channel registry_address = sender.default_registry.address receiver = next( app.raiden for app in raiden_network if app.raiden.address == sender_channel.partner_state.address) # assert there is a direct channel receiver -> sender (vv) receiver_channel = RaidenAPI(receiver).get_channel_list( registry_address=registry_address, token_address=token_address, partner_address=sender.address, ) assert len(receiver_channel) == 1 with gevent.Timeout(30, exception=ValueError("partner not reachable")): waiting.wait_for_healthy(sender, receiver.address, PaymentAmount(1)) with watch_for_unlock_failures(*raiden_network): amount = PaymentAmount(1) RaidenAPI(sender).transfer_and_wait(registry_address, token_address, amount, receiver.address, transfer_timeout=10) with gevent.Timeout( 30, exception=ValueError( "timeout while waiting for incoming transaction")): wait_for_transaction(receiver, registry_address, token_address, sender.address) # test `leave()` method connection_manager = connection_managers[0] raiden = connection_manager.raiden blocks = BlockOffset(sender_channel.settle_timeout * 10) channels = views.list_channelstate_for_tokennetwork( chain_state=views.state_from_raiden(connection_manager.raiden), token_network_registry_address=registry_address, token_address=token_address, ) channel_identifiers = [channel.identifier for channel in channels] timeout = block_offset_timeout(raiden, "Timeout while waiting for leave", blocks) with timeout: RaidenAPI(sender).token_network_leave(registry_address, token_address) timeout.exception_to_throw = ValueError("Channels not settled in time") waiting.wait_for_settle( raiden=connection_manager.raiden, token_network_registry_address=registry_address, token_address=token_address, channel_ids=channel_identifiers, retry_timeout=0.1, )
def test_user_deposit_proxy_withdraw( private_keys: List[bytes], web3: Web3, contract_manager: ContractManager, user_deposit_address: Address, ): c0_client = JSONRPCClient(web3, PrivateKey(private_keys[0])) c0_proxy_manager = ProxyManager( rpc_client=c0_client, contract_manager=contract_manager, metadata=ProxyManagerMetadata( token_network_registry_deployed_at=GENESIS_BLOCK_NUMBER, filters_start_at=GENESIS_BLOCK_NUMBER, ), ) c0_user_deposit_proxy = c0_proxy_manager.user_deposit( UserDepositAddress(user_deposit_address), BLOCK_ID_LATEST) withdraw_plan = c0_user_deposit_proxy.get_withdraw_plan( c0_client.address, BLOCK_ID_LATEST) # There should be no withdraw plan assert withdraw_plan.withdraw_block == 0 assert withdraw_plan.withdraw_amount == 0 initial_deposit = c0_user_deposit_proxy.get_total_deposit( c0_client.address, BLOCK_ID_LATEST) # None of these are valid plan_withdraw amounts for value in [-1, 0, initial_deposit + 1]: with pytest.raises(BrokenPreconditionError): c0_user_deposit_proxy.plan_withdraw(TokenAmount(value), BLOCK_ID_LATEST) # With no plan any withdraw must fail in the precondition check with pytest.raises(BrokenPreconditionError): c0_user_deposit_proxy.withdraw(TokenAmount(1), BLOCK_ID_LATEST) withdraw_amount = TokenAmount(initial_deposit // 2) transaction_hash, withdraw_block = c0_user_deposit_proxy.plan_withdraw( withdraw_amount, BLOCK_ID_LATEST) assert is_tx_hash_bytes(transaction_hash) # The effective balance must take the planned withdraw into account effective_balance_after_withdraw_plan = c0_user_deposit_proxy.effective_balance( c0_client.address, BLOCK_ID_LATEST) assert effective_balance_after_withdraw_plan == initial_deposit - withdraw_amount # Wait until target block - 1. # We set the retry timeout to 0.1 to make sure there is enough time for the failing case # below. c0_client.wait_until_block(BlockNumber(withdraw_block - 1), retry_timeout=0.1) # Withdraw should still fail with pytest.raises(BrokenPreconditionError): c0_user_deposit_proxy.withdraw(TokenAmount(withdraw_amount), BLOCK_ID_LATEST) # Wait the final block c0_user_deposit_proxy.client.wait_until_block(withdraw_block) # Now withdraw must succeed transaction_hash = c0_user_deposit_proxy.withdraw( TokenAmount(withdraw_amount), BLOCK_ID_LATEST) assert is_tx_hash_bytes(transaction_hash) # The current balance must now match the reduced value new_current_balance = c0_user_deposit_proxy.get_balance( c0_client.address, BLOCK_ID_LATEST) assert new_current_balance == initial_deposit - withdraw_amount # Deposit again after the funds were withdrawn amount_to_deposit = 1 tasks = set() # Force a race condition between deposits, letting successive, concurrent calls # wait for the first inflight transaction for _ in range(3): task = gevent.spawn( c0_user_deposit_proxy.approve_and_deposit, beneficiary=c0_client.address, # the total deposit needs to increase monotonically in the contract total_deposit=initial_deposit + amount_to_deposit, given_block_identifier=BLOCK_ID_LATEST, ) tasks.add(task) results = gevent.joinall(tasks, raise_error=True) # All tx have the same deposit, # so one of them should successfully transact, # while all others should wait for the inflight transaction # All calls should then be associated to the same on-chain transaction tx_hashes = set(result.get() for result in results) assert len(tx_hashes) == 1 assert is_tx_hash_bytes(tx_hashes.pop())