def _first_allowed_block_to_monitor(token_network_address: TokenNetworkAddress,
                                    channel: Channel,
                                    context: Context) -> BlockNumber:
    # Get token_network_contract
    abi = CONTRACT_MANAGER.get_contract_abi(CONTRACT_TOKEN_NETWORK)
    token_network_contract = context.web3.eth.contract(
        abi=abi, address=Address(token_network_address))

    # Use the same assumptions as the MS contract, which can't get the real
    # channel timeout and close block.
    settle_block_number, _ = token_network_contract.functions.getChannelInfo(
        channel.identifier, channel.participant1, channel.participant2).call()
    assumed_settle_timeout = token_network_contract.functions.settlement_timeout_min(
    ).call()
    assumed_close_block = settle_block_number - assumed_settle_timeout

    # Call smart contract to use its firstBlockAllowedToMonitor calculation
    return BlockNumber(
        context.monitoring_service_contract.functions.
        firstBlockAllowedToMonitor(
            closed_at_block=assumed_close_block,
            settle_timeout=assumed_settle_timeout,
            participant1=channel.participant1,
            participant2=channel.participant2,
            monitoring_service_address=context.ms_state.address,
        ).call())
Exemple #2
0
def info(
    private_key: str,
    web3: Web3,
    contracts: Dict[str, Contract],
    start_block: BlockNumber,
) -> None:
    log.info("Using RPC endpoint", rpc_url=get_web3_provider_info(web3))
    service_registry_contract = contracts[CONTRACT_SERVICE_REGISTRY]
    deposit_token_address = service_registry_contract.functions.token().call()
    deposit_token_contract = web3.eth.contract(
        address=deposit_token_address,
        abi=CONTRACT_MANAGER.get_contract_abi(CONTRACT_CUSTOM_TOKEN),
    )
    caller_address = private_key_to_address(private_key)
    fmt_amount = get_token_formatter(deposit_token_contract)

    deposits = find_deposits(
        web3=web3,
        service_address=caller_address,
        service_registry_contract=service_registry_contract,
        start_block=start_block,
    )
    if not deposits:
        print("No deposits were made from this account.")
        return

    print("Deposits:")
    for dep in deposits:
        print(f" * block {dep['block_number']}", end=", ")
        print(f"amount: {fmt_amount(dep['amount'])}", end=", ")
        if dep["withdrawn"]:
            print("WITHDRAWN")
        else:
            print("increased validity till " + dep["valid_till"])
Exemple #3
0
def find_withdrawable_deposit(
    web3: Web3,
    service_address: Address,
    service_registry_contract: Contract,
    start_block: BlockNumber,
) -> Address:
    # Get formatter for token amounts
    deposit_token_address = service_registry_contract.functions.token().call()
    deposit_token_contract = web3.eth.contract(
        address=deposit_token_address,
        abi=CONTRACT_MANAGER.get_contract_abi(CONTRACT_CUSTOM_TOKEN),
    )
    fmt_amount = get_token_formatter(deposit_token_contract)

    # Find deposits
    deposits = find_deposits(web3, service_address, service_registry_contract, start_block)
    deposits = [d for d in deposits if not d["withdrawn"]]
    if not deposits:
        click.echo("No deposits found!", err=True)
        sys.exit(1)

    # Inform user
    print("Deposit found:")
    for deposit_event in deposits:
        valid_till = deposit_event["valid_till"]
        amount = deposit_event["amount"]
        print(f" * valid till {valid_till}, amount: {fmt_amount(amount)}")
    if len(deposits) > 1:
        print("I will withdraw the first (oldest) one. Run this script again for the next deposit")

    return to_canonical_address(deposits[0]["deposit_contract"])
Exemple #4
0
def connect_to_blockchain(
    eth_rpc: str, used_contracts: List[str], address_overwrites: Dict[str,
                                                                      Address]
) -> Tuple[Web3, Dict[str, Contract], BlockNumber]:
    try:
        provider = HTTPProvider(eth_rpc)
        web3 = Web3(provider)
        # Will throw ConnectionError on bad Ethereum client
        chain_id = ChainID(int(web3.net.version))
    except requests.exceptions.ConnectionError:
        log.error(
            "Can not connect to the Ethereum client. Please check that it is running and that "
            "your settings are correct.",
            eth_rpc=eth_rpc,
        )
        sys.exit(1)

    # Add POA middleware for geth POA chains, no/op for other chains
    web3.middleware_stack.inject(geth_poa_middleware, layer=0)

    # give web3 some time between retries before failing
    provider.middlewares.replace("http_retry_request",
                                 http_retry_with_backoff_middleware)

    addresses, start_block = get_contract_addresses_and_start_block(
        chain_id=chain_id,
        contracts=used_contracts,
        address_overwrites=address_overwrites)
    contracts = {
        c: web3.eth.contract(abi=CONTRACT_MANAGER.get_contract_abi(c),
                             address=address)
        for c, address in addresses.items()
    }

    return web3, contracts, start_block
def connect_to_blockchain(
    eth_rpc: URI,
    gas_price_strategy: Optional[Callable[[Web3, Any], Wei]],
    used_contracts: List[str],
    address_overwrites: Dict[str, Address],
    development_environment: ContractDevEnvironment,
) -> Tuple[Web3, Dict[str, Contract], BlockNumber]:
    try:
        provider = HTTPProvider(eth_rpc)
        web3 = Web3(provider)
        # Will throw ConnectionError on bad Ethereum client
        chain_id = ChainID(web3.eth.chain_id)
    except requests.exceptions.ConnectionError:
        log.error(
            "Can not connect to the Ethereum client. Please check that it is running and that "
            "your settings are correct.",
            eth_rpc=eth_rpc,
        )
        sys.exit(1)

    # Add POA middleware for geth POA chains, no/op for other chains
    web3.middleware_onion.inject(geth_poa_middleware, layer=0)

    # Set gas price strategy
    # for that we also need a cache middleware, otherwise sampling is expensive
    web3.middleware_onion.add(simple_cache_middleware)

    if not gas_price_strategy:
        chain_id = ChainID(web3.eth.chain_id)
        gas_price_strategy = (rpc_gas_price_strategy
                              if "arbitrum" in ID_TO_CHAINNAME.get(
                                  chain_id, "") else fast_gas_price_strategy)

    web3.eth.setGasPriceStrategy(gas_price_strategy)

    # give web3 some time between retries before failing
    # TODO: find a way to to this type safe
    provider.middlewares.replace(  # type: ignore
        "http_retry_request", http_retry_with_backoff_middleware)

    addresses, start_block = get_contract_addresses_and_start_block(
        chain_id=chain_id,
        contracts=used_contracts,
        address_overwrites=address_overwrites,
        development_environment=development_environment,
    )
    contracts = {
        c: web3.eth.contract(abi=CONTRACT_MANAGER.get_contract_abi(c),
                             address=address)
        for c, address in addresses.items()
    }

    return web3, contracts, start_block
Exemple #6
0
def withdraw(
    private_key: str,
    web3: Web3,
    contracts: Dict[str, Contract],
    start_block: BlockNumber,
    to: Optional[Address],
) -> None:
    log.info("Using RPC endpoint", rpc_url=get_web3_provider_info(web3))
    service_registry_contract = contracts[CONTRACT_SERVICE_REGISTRY]
    caller_address = private_key_to_address(private_key)

    # Find deposit contract address
    caller_address = private_key_to_address(private_key)
    deposit_contract_address = find_withdrawable_deposit(
        web3=web3,
        service_address=caller_address,
        service_registry_contract=service_registry_contract,
        start_block=start_block,
    )
    deposit_contract = web3.eth.contract(
        abi=CONTRACT_MANAGER.get_contract_abi(CONTRACT_DEPOSIT),
        address=deposit_contract_address)

    # Check usage of correct key
    withdrawer = deposit_contract.functions.withdrawer().call()
    if to_canonical_address(withdrawer) != caller_address:
        log.error(
            "You must use the key used to deposit when withdrawing",
            expected=withdrawer,
            actual=to_checksum_address(caller_address),
        )
        sys.exit(1)

    # Can we withdraw already?
    release_at = deposit_contract.functions.release_at().call()
    deprecated = service_registry_contract.functions.deprecated().call()
    if web3.eth.getBlock(
            "latest")["timestamp"] < release_at and not deprecated:
        log.error(
            "Too early to withdraw",
            released_at_utc=datetime.utcfromtimestamp(release_at).isoformat(),
        )
        sys.exit(1)

    receiver = to or private_key_to_address(private_key)
    checked_transact(
        web3=web3,
        sender_address=caller_address,
        function_call=deposit_contract.functions.withdraw(receiver),
        task_name="withdraw",
        wait_confirmation_interval=False,
    )
Exemple #7
0
def deposit_to_registry(
    web3: Web3,
    service_registry_contract: Contract,
    user_deposit_contract: Contract,
    service_address: Address,
) -> None:
    log.info("Address not registered in ServiceRegistry")
    deposit_token_address = user_deposit_contract.functions.token().call()
    deposit_token_contract = web3.eth.contract(
        address=deposit_token_address, abi=CONTRACT_MANAGER.get_contract_abi(CONTRACT_CUSTOM_TOKEN)
    )

    # Get required deposit
    required_deposit = service_registry_contract.functions.currentPrice().call()

    # Check current token balance
    account_balance = deposit_token_contract.functions.balanceOf(service_address).call()
    log.info("Current account balance", balance=account_balance, required_deposit=required_deposit)

    # mint tokens if necessary
    if account_balance < required_deposit:
        checked_transact(
            web3=web3,
            service_address=service_address,
            function_call=deposit_token_contract.functions.mint(required_deposit),
            task_name="Minting new Test RDN tokens",
        )

        account_balance = deposit_token_contract.functions.balanceOf(service_address).call()
        log.info(
            "Updated account balance", balance=account_balance, desired_deposit=required_deposit
        )

    # Approve token transfer
    checked_transact(
        web3=web3,
        service_address=service_address,
        function_call=deposit_token_contract.functions.approve(
            service_registry_contract.address, required_deposit
        ),
        task_name="Allowing token transfor for deposit",
    )

    # Deposit tokens
    checked_transact(
        web3=web3,
        service_address=service_address,
        function_call=service_registry_contract.functions.deposit(required_deposit),
        task_name="Depositing to service registry",
    )
Exemple #8
0
def create_event_topic_to_abi_dict() -> Dict[bytes, ABIEvent]:
    contract_names = [
        CONTRACT_TOKEN_NETWORK_REGISTRY,
        CONTRACT_TOKEN_NETWORK,
        CONTRACT_MONITORING_SERVICE,
    ]

    event_abis = {}
    for contract_name in contract_names:
        events = filter_by_type(
            "event", CONTRACT_MANAGER.get_contract_abi(contract_name))

        for event_abi in events:
            event_topic = event_abi_to_log_topic(event_abi)  # type: ignore
            event_abis[event_topic] = event_abi

    return event_abis  # type: ignore
def register_account(
    private_key: str,
    web3: Web3,
    contracts: Dict[str, Contract],
    start_block: BlockNumber,
    service_url: Optional[str],
    accept_disclaimer: bool,
    accept_all: bool,
    extend: bool = False,
) -> None:
    click.secho(DISCLAIMER, fg="yellow")
    if not accept_disclaimer and not accept_all:
        click.confirm(CONFIRMATION_OF_UNDERSTANDING, abort=True)

    def maybe_prompt(query: str) -> None:
        if not accept_all:
            click.confirm(query, abort=True)

    chain_id = web3.eth.chainId
    log.info("Using RPC endpoint", rpc_url=get_web3_provider_info(web3))
    hex_addresses = {
        name: to_checksum_address(contract.address) for name, contract in contracts.items()
    }
    log.info("Contract information", addresses=hex_addresses, start_block=start_block)

    # Add middleware to sign transactions by default
    web3.middleware_onion.add(construct_sign_and_send_raw_middleware(private_key))

    service_address = private_key_to_address(private_key)
    log.info("Running service registration script", account_address=service_address)
    click.secho(
        f"\nThis will run the registration with the address {to_checksum_address(service_address)}"
        f"\n\tSee {etherscan_url_for_address(chain_id, service_address)}"
    )
    maybe_prompt("I have checked that the address is correct and want to continue")

    # Create contract proxies
    service_registry_contract = contracts[CONTRACT_SERVICE_REGISTRY]
    service_registry_address = to_canonical_address(service_registry_contract.address)
    deposit_token_address = service_registry_contract.functions.token().call()
    deposit_token_contract = web3.eth.contract(
        address=deposit_token_address,
        abi=CONTRACT_MANAGER.get_contract_abi(CONTRACT_CUSTOM_TOKEN),
    )

    click.secho(
        "\nThe address of the service registry contract used is "
        f"{to_checksum_address(service_registry_contract.address)}"
        f"\n\tSee {etherscan_url_for_address(chain_id, service_registry_address)}"
    )
    maybe_prompt("I have checked that the address is correct and want to continue")

    # Check current token balance
    fmt_amount = get_token_formatter(deposit_token_contract)
    account_balance = deposit_token_contract.functions.balanceOf(service_address).call()
    log.info("Current account balance", balance=account_balance)
    click.secho(
        "\nThe address of the token used is "
        f"{to_checksum_address(deposit_token_address)}"
        f"\n\tSee {etherscan_url_for_address(chain_id, deposit_token_address)}"
        f"\nThe account balance of that token is {fmt_amount(account_balance)}."
    )
    maybe_prompt("I have checked that the address and my balance are correct and want to continue")

    # check if already registered
    currently_registered = service_registry_contract.functions.hasValidRegistration(
        service_address
    ).call()
    current_url = service_registry_contract.functions.urls(service_address).call()
    log.info(
        "Current ServiceRegistry information for service address",
        service_address=service_address,
        currently_registered=currently_registered,
        current_url=current_url,
    )

    # Register if not yet done or extension is requested
    if extend and currently_registered:
        log.info("Registration found. Preparing to extend registration.")
        send_registration_transaction(
            web3=web3,
            service_registry_contract=service_registry_contract,
            deposit_token_contract=deposit_token_contract,
            maybe_prompt=maybe_prompt,
            account_balance=account_balance,
            service_address=service_address,
            fmt_amount=fmt_amount,
        )
    elif not currently_registered:
        log.info("Address not registered in ServiceRegistry")
        send_registration_transaction(
            web3=web3,
            service_registry_contract=service_registry_contract,
            deposit_token_contract=deposit_token_contract,
            maybe_prompt=maybe_prompt,
            account_balance=account_balance,
            service_address=service_address,
            fmt_amount=fmt_amount,
        )
    else:
        log.info(
            "Already registered. If you want to extend your registration, "
            "use the 'extend' command."
        )

    if service_url and service_url != current_url:
        click.secho(f'\nNew Url to be registered "{service_url}"')
        hostname = service_url.split("//")[1]
        reachable = not subprocess.run(
            ["ping", "-c", "1", hostname], capture_output=True, check=False
        ).returncode
        if not reachable:
            click.secho(f"`ping {hostname}` fails. Are you sure the URL is correct?", fg="yellow")
        maybe_prompt("I have checked the URL and it is correct")

        checked_transact(
            web3=web3,
            sender_address=service_address,
            function_call=service_registry_contract.functions.setURL(service_url),
            task_name="Registering new URL",
        )

    current_url = service_registry_contract.functions.urls(service_address).call()

    click.secho("\nThank you for registering your services!", fg="green")
    log.info("Updated infos", current_url=current_url)