예제 #1
0
    def open_channel(self, receiver_address: str, deposit: int):
        """
        Attempts to open a new channel to the receiver with the given deposit. Blocks until the
        creation transaction is found in a pending block or timeout is reached. The new channel
        state is returned.
        """
        assert isinstance(receiver_address, str)
        assert isinstance(deposit, int)
        assert deposit > 0

        balance = self.context.web3.eth.getBalance(self.context.address)
        #  token_balance = self.context.token.call().balanceOf(self.context.address)
        if balance < deposit:
            log.error(
                'Insufficient balance available for the specified deposit ({}/{})'
                .format(balance, deposit))
            return None

        current_block = self.context.web3.eth.blockNumber
        log.info(
            'Creating channel to {} with an initial deposit of {} @{}'.format(
                receiver_address, deposit, current_block))

        tx = create_signed_contract_transaction(self.context.private_key,
                                                self.context.channel_manager,
                                                'createChannel',
                                                [receiver_address], deposit)
        ret = self.context.web3.eth.sendRawTransaction(tx)
        log.warning(str(ret))

        log.debug('Waiting for channel creation event on the blockchain...')
        filters = {
            '_sender_address': self.context.address,
            '_receiver_address': receiver_address
        }
        event = get_event_blocking(self.context.channel_manager,
                                   'ChannelCreated',
                                   from_block=current_block + 1,
                                   to_block='latest',
                                   argument_filters=filters)

        if event:
            log.debug('Event received. Channel created in block {}.'.format(
                event['blockNumber']))
            assert is_same_address(event['args']['_sender_address'],
                                   self.context.address)
            assert is_same_address(event['args']['_receiver_address'],
                                   receiver_address)
            channel = Channel(self.context,
                              self.context.address,
                              receiver_address,
                              event['blockNumber'],
                              event['args']['_deposit'],
                              on_settle=lambda c: self.channels.remove(c))
            self.channels.append(channel)
        else:
            log.error('Error: No event received.')
            channel = None

        return channel
예제 #2
0
def sweep_account(private_key: str, faucet_address: str,
                  token_contract: Contract, web3: Web3, wait_for_transaction):
    address = privkey_to_addr(private_key)
    log.info('Sweeping account {}'.format(address))
    token_balance = token_contract.call().balanceOf(address)
    if token_balance > 0:
        tx = create_signed_contract_transaction(
            private_key, token_contract, 'transfer',
            [faucet_address, token_balance])
        try:
            tx_hash = web3.eth.sendRawTransaction(tx)
        except ValueError as e:
            if e.args[0]['message'].startswith('Insufficient funds.'):
                pass
            else:
                raise
        else:
            wait_for_transaction(tx_hash)
            assert token_contract.call().balanceOf(address) == 0

    balance = web3.eth.getBalance(address)
    if balance < POT_GAS_LIMIT * GAS_PRICE:
        return
    tx = create_signed_transaction(private_key,
                                   web3,
                                   to=faucet_address,
                                   value=balance - POT_GAS_LIMIT * GAS_PRICE,
                                   gas_limit=POT_GAS_LIMIT)
    tx_hash = web3.eth.sendRawTransaction(tx)
    wait_for_transaction(tx_hash)
    assert web3.eth.getBalance(address) == 0, (
        'Sweeping of account {} (private key {}) failed.'.format(
            address, private_key))
예제 #3
0
def sweep_account(private_key: str, token_contract: Contract, web3: Web3,
                  wait_for_transaction):
    address = privkey_to_addr(private_key)
    token_balance = token_contract.call().balanceOf(address)
    if token_balance > 0:
        tx = create_signed_contract_transaction(
            private_key, token_contract, 'transfer',
            [FAUCET_ADDRESS, token_balance])
        try:
            tx_hash = web3.eth.sendRawTransaction(tx)
        except ValueError as e:
            if e.args[0]['message'].startswith('Insufficient funds.'):
                pass
            else:
                raise
        else:
            wait_for_transaction(tx_hash)

    balance = web3.eth.getBalance(address, 'pending')
    if balance < 21000 * GAS_PRICE:
        return
    tx = create_signed_transaction(private_key,
                                   web3,
                                   to=FAUCET_ADDRESS,
                                   value=balance - 21000 * GAS_PRICE,
                                   gas_limit=21000)
    tx_hash = web3.eth.sendRawTransaction(tx)
    wait_for_transaction(tx_hash)
    assert web3.eth.getBalance(address, 'pending') == 0, (
        'Sweeping of account {} (private key {}) failed.'.format(
            address, private_key))
예제 #4
0
    def close_channel(self, sender, open_block_number):
        """Close and settle a channel."""
        if not (sender, open_block_number) in self.channels:
            self.log.warning(
                "attempt to close a non-registered channel (sender=%s open_block=%s"
                % (sender, open_block_number))
            return
        c = self.channels[sender, open_block_number]
        if c.last_signature is None:
            raise NoBalanceProofReceived(
                'Cannot close a channel without a balance proof.')
        # send closing tx
        raw_tx = create_signed_contract_transaction(
            self.private_key, self.channel_manager_contract,
            'uncooperativeClose', [
                self.state.receiver, open_block_number, c.balance,
                decode_hex(c.last_signature)
            ])

        # update local state
        c.is_closed = True
        c.mtime = time.time()
        self.state.set_channel(c)

        try:
            txid = self.blockchain.web3.eth.sendRawTransaction(raw_tx)
            self.log.info(
                'sent channel close(sender %s, block number %s, tx %s)',
                sender, open_block_number, txid)
        except InsufficientBalance:
            c.state = ChannelState.CLOSE_PENDING
            self.state.set_channel(c)
            raise
예제 #5
0
def fund_account(
        address: str,
        eth_allowance: int,
        token_allowance: int,
        token_contract: Contract,
        web3: Web3,
        wait_for_transaction,
        faucet_private_key: str,
):
    log.info('Funding account {}'.format(address))
    tx = create_signed_transaction(
        faucet_private_key,
        web3,
        to=address,
        value=eth_allowance
    )
    tx_hash = web3.eth.sendRawTransaction(tx)
    wait_for_transaction(tx_hash)

    if token_allowance > 0:
        tx = create_signed_contract_transaction(
            faucet_private_key,
            token_contract,
            'transfer',
            [
                address,
                token_allowance
            ]
        )
        tx_hash = web3.eth.sendRawTransaction(tx)
        wait_for_transaction(tx_hash)
예제 #6
0
    def event_channel_close_requested(self, sender: str,
                                      open_block_number: int, balance,
                                      timeout):
        sender = to_checksum_address(sender)

        self.log.info(
            'channel close requested (\n\tsender %s, \n\tblock number %s, \n\tbalance %s, \n\ttimeout %s)',
            sender, open_block_number, balance, timeout)

        job = self.jobs[self.state.receiver, sender, open_block_number]

        if not job.respond:
            self.log.info(
                "Didn't finish fair exchange to so don't respond to close request\n"
            )
            return

        if job.interfered:
            self.log.info(
                'Already responded to a close (\n\tsender %s \n\topen_block_number %s, \n\tbalance %s)\n',
                sender, open_block_number, balance)
            return

        assert job.last_hash == job.all_hashes[-1]
        assert job.last_signature == job.all_signatures[-1]
        assert job.last_customer_sig == job.all_customer_signatures[-1]
        assert job.last_round_number == job.all_round_numbers[-1]

        if self.try_to_make_some_money and len(job.all_hashes) > 1:
            evidence_hash = job.all_hashes[-2]
            evidence_sig = job.all_signatures[-2]
            evidence_customer_sig = job.all_customer_signatures[-2]
            evidence_round_number = job.all_round_numbers[-2]
        else:
            evidence_hash = job.last_hash
            evidence_sig = job.last_signature
            evidence_customer_sig = job.last_customer_sig
            evidence_round_number = job.last_round_number

        print('\nevidence')
        debug_print([
            evidence_hash,
            decode_hex(evidence_sig),
            decode_hex(evidence_customer_sig), evidence_round_number
        ])

        raw_tx = create_signed_contract_transaction(
            self.private_key, self.channel_manager_contract, 'monitorEvidence',
            [
                self.state.receiver, open_block_number, evidence_hash,
                decode_hex(evidence_sig),
                decode_hex(evidence_customer_sig), evidence_round_number
            ])

        txid = self.blockchain.web3.eth.sendRawTransaction(raw_tx)
        self.log.info(
            'sent monitor intereference (\n\tsender %s, \n\tblock number %d, \n\ttxid %s)\n',
            sender, open_block_number, encode_hex(txid))
예제 #7
0
    def close_cooperatively(self, closing_sig: bytes):
        """
        Attempts to close the channel immediately by providing a hash of the channel's balance
        proof signed by the receiver. This signature must correspond to the balance proof stored in
        the passed channel state.
        """
        if self.state == Channel.State.closed:
            log.error('Channel must not be closed already to be closed cooperatively.')
            return None
        log.info('Attempting to cooperatively close channel to {} created at block #{}.'.format(
            self.receiver, self.block
        ))
        current_block = self.core.web3.eth.blockNumber
        receiver_recovered = verify_closing_sig(
            self.sender,
            self.block,
            self.balance,
            closing_sig,
            self.core.channel_manager.address
        )
        if not is_same_address(receiver_recovered, self.receiver):
            log.error('Invalid closing signature.')
            return None

        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.channel_manager,
            'cooperativeClose',
            [
                self.receiver,
                self.block,
                self.balance,
                self.balance_sig,
                closing_sig
            ]
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for settle confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelSettled',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully closed channel in block {}.'.format(event['blockNumber']))
            self.state = Channel.State.closed
            return event
        else:
            log.error('No event received.')
            return None
예제 #8
0
    def drain_rdn(self, to: Address) -> TXHashHex:
        assert self.web3
        assert self.token
        assert self.unlocked, 'Account is locked.'

        rei = self.token.call().balanceOf(self.address)
        tx = create_signed_contract_transaction(self.private_key, self.token,
                                                'transfer', [to, rei])
        return self.web3.eth.sendRawTransaction(tx)
예제 #9
0
    def close_cooperatively(self, closing_sig: bytes):
        """
        Attempts to close the channel immediately by providing a hash of the channel's balance
        proof signed by the receiver. This signature must correspond to the balance proof stored in
        the passed channel state.
        """
        if self.state == Channel.State.closed:
            log.error('Channel must not be closed already to be closed cooperatively.')
            return None
        log.info('Attempting to cooperatively close channel to {} created at block #{}.'.format(
            self.receiver, self.block
        ))
        current_block = self.core.web3.eth.blockNumber
        receiver_recovered = verify_closing_sig(
            self.sender,
            self.block,
            self.balance,
            closing_sig,
            self.core.channel_manager.address
        )
        if not is_same_address(receiver_recovered, self.receiver):
            log.error('Invalid closing signature.')
            return None

        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.channel_manager,
            'cooperativeClose',
            [
                self.receiver,
                self.block,
                self.balance,
                self.balance_sig,
                closing_sig
            ]
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for settle confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelSettled',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully closed channel in block {}.'.format(event['blockNumber']))
            self.state = Channel.State.closed
            return event
        else:
            log.error('No event received.')
            return None
예제 #10
0
    def settle(self):
        """
        Attempts to settle a channel that has passed its settlement period. If a channel cannot be
        settled yet, the call is ignored with a warning. Blocks until a confirmation event is
        received or timeout.
        """
        if self.state != Channel.State.settling:
            log.error('Channel must be in the settlement period to settle.')
            return None
        log.info('Attempting to settle channel to {} created at block #{}.'.format(
            self.receiver, self.block
        ))

        _, _, settle_block, _, _ = self.core.channel_manager.call().getChannelInfo(
            self.sender, self.receiver, self.block
        )

        current_block = self.core.web3.eth.blockNumber
        wait_remaining = settle_block - current_block
        if wait_remaining > 0:
            log.warning('{} more blocks until this channel can be settled. Aborting.'.format(
                wait_remaining
            ))
            return None

        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.channel_manager,
            'settle',
            [
                self.receiver,
                self.block
            ]
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for settle confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelSettled',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully settled channel in block {}.'.format(event['blockNumber']))
            self.state = Channel.State.closed
            self.on_settle(self)
            return event
        else:
            log.error('No event received.')
            return None
예제 #11
0
    def settle(self):
        """
        Attempts to settle a channel that has passed its settlement period. If a channel cannot be
        settled yet, the call is ignored with a warning. Blocks until a confirmation event is
        received or timeout.
        """
        if self.state != Channel.State.settling:
            log.error('Channel must be in the settlement period to settle.')
            return None
        log.info('Attempting to settle channel to {} created at block #{}.'.format(
            self.receiver, self.block
        ))

        _, _, settle_block, _, _ = self.core.channel_manager.call().getChannelInfo(
            self.sender, self.receiver, self.block
        )

        current_block = self.core.web3.eth.blockNumber
        wait_remaining = settle_block - current_block
        if wait_remaining > 0:
            log.warning('{} more blocks until this channel can be settled. Aborting.'.format(
                wait_remaining
            ))
            return None

        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.channel_manager,
            'settle',
            [
                self.receiver,
                self.block
            ]
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for settle confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelSettled',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully settled channel in block {}.'.format(event['blockNumber']))
            self.state = Channel.State.closed
            self.on_settle(self)
            return event
        else:
            log.error('No event received.')
            return None
예제 #12
0
    def topup(self, deposit):
        """
        Attempts to increase the deposit in an existing channel. Block until confirmation.
        """
        if self.state != Channel.State.open:
            log.error('Channel must be open to be topped up.')
            return

        _balance = self.core.web3.eth.getBalance(self.core.address)
        if _balance < deposit:
            log.error(
                'Insufficient tokens available for the specified topup ({}/{})'
                .format(_balance, deposit)
            )
            return None

        log.info('Topping up channel to {} created at block #{} by {} tokens.'.format(
            self.receiver, self.block, deposit
        ))
        current_block = self.core.web3.eth.blockNumber

        data = (decode_hex(self.sender) +
                decode_hex(self.receiver) +
                self.block.to_bytes(4, byteorder='big'))
        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.channel_manager,
            'topUp',
            [
                self.receiver,
                self.block
            ],
            deposit
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for topup confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelToppedUp',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully topped up channel in block {}.'.format(event['blockNumber']))
            self.deposit += deposit
            return event
        else:
            log.error('No event received.')
            return None
예제 #13
0
    def topup(self, deposit):
        """
        Attempts to increase the deposit in an existing channel. Block until confirmation.
        """
        if self.state != Channel.State.open:
            log.error('Channel must be open to be topped up.')
            return

        token_balance = self.core.token.call().balanceOf(self.core.address)
        if token_balance < deposit:
            log.error(
                'Insufficient tokens available for the specified topup ({}/{})'
                .format(token_balance, deposit)
            )
            return None

        log.info('Topping up channel to {} created at block #{} by {} tokens.'.format(
            self.receiver, self.block, deposit
        ))
        current_block = self.core.web3.eth.blockNumber

        data = (decode_hex(self.sender) +
                decode_hex(self.receiver) +
                self.block.to_bytes(4, byteorder='big'))
        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.token,
            'transfer',
            [
                self.core.channel_manager.address,
                deposit,
                data
            ]
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for topup confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelToppedUp',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully topped up channel in block {}.'.format(event['blockNumber']))
            self.deposit += deposit
            return event
        else:
            log.error('No event received.')
            return None
예제 #14
0
    def close_channel(self, sender: str, open_block_number: int):
        """Close and settle a channel.
        Params:
            sender (str):               sender address
            open_block_number (int):    block the channel was open in
        """
        assert is_checksum_address(sender)
        if not (sender, open_block_number) in self.channels:
            self.log.warning(
                "attempt to close a non-registered channel (sender=%s open_block=%s" %
                (sender, open_block_number)
            )
            return
        c = self.channels[sender, open_block_number]
        if c.last_signature is None:
            raise NoBalanceProofReceived('Cannot close a channel without a balance proof.')
        # send closing tx
        closing_sig = sign_close(
            self.private_key,
            sender,
            open_block_number,
            c.balance,
            self.channel_manager_contract.address
        )

        raw_tx = create_signed_contract_transaction(
            self.private_key,
            self.channel_manager_contract,
            'cooperativeClose',
            [
                self.state.receiver,
                open_block_number,
                c.balance,
                decode_hex(c.last_signature),
                closing_sig
            ]
        )

        # update local state
        c.is_closed = True
        c.mtime = time.time()
        self.state.set_channel(c)

        try:
            txid = self.blockchain.web3.eth.sendRawTransaction(raw_tx)
            self.log.info('sent channel close(sender %s, block number %s, tx %s)',
                          sender, open_block_number, txid)
        except InsufficientBalance:
            c.state = ChannelState.CLOSE_PENDING
            self.state.set_channel(c)
            raise
예제 #15
0
    def event_customer_dispute(self, customer: str, sender: str,
                               open_block_number: int):
        customer = to_checksum_address(customer)
        sender = to_checksum_address(sender)

        try:
            job = self.jobs[customer, sender, open_block_number]
        except KeyError:
            self.log.info('Customer disputed a channel not being watched.')
            return

        if self.redeem_payment:
            p = job.conditional_payment

            #debug_print([p.payout, job.last_image, job.last_preimage, p.pay_sig, customer])

            print(bcolors.OKBLUE +
                  '\nMonitor: Redeeming payment on-chain instead...\n' +
                  bcolors.ENDC)

            self.log.info('Redeeming payment on-chain (customer %s)', customer)
            #            self.log.info('calling setstate to redeem payment \n\tpayout %d, \n\tlast_image %s, \n\tlast preimage %d, \n\tpay_sig %s, \n\tcustomer %s)',
            #                p.payout,
            #                encode_hex(job.last_image),
            #                job.last_preimage,
            #                encode_hex(p.pay_sig),
            #                customer
            #            )
            raw_tx = create_signed_contract_transaction(
                self.private_key, self.channel_monitor_contract, 'setstate', [
                    sender, open_block_number, p.payout,
                    p.conditional_transfer, job.last_image, job.last_preimage,
                    p.pay_sig, customer
                ])

            txid = self.blockchain.web3.eth.sendRawTransaction(raw_tx)

            self.log.debug('setstate transaction (txid %s)\n',
                           encode_hex(txid))

        print(
            bcolors.OKBLUE +
            "\nMonitor: Customer raised dispute in contract. No action required."
            + bcolors.ENDC)

        if self.redeem_payment and self.cheat_with_receipt:
            job.respond = False
        elif self.redeem_payment:
            job.respond = True
        else:
            job.respond = False
예제 #16
0
def get_tokens():
    web3 = Web3(HTTPProvider(config['web3path']))
    private_key = get_private_key('./dragonstone-rinkeby-02-03')
    log.info('This private key %s', private_key)
    log.info('This address %s', privkey_to_addr(private_key))
    token_address = config['token']
    token_abi = constants.CONTRACT_METADATA[constants.TOKEN_ABI_NAME]['abi']
    token_contract = web3.eth.contract(abi=token_abi, address=token_address)

    raw_tx = create_signed_contract_transaction(private_key, token_contract,
                                                'mint', [], 100000000000000000)

    web3.eth.sendRawTransaction(raw_tx)
    log.info('bought tokens from custom token')
예제 #17
0
    def close(self, balance=None):
        """
        Attempts to request close on a channel. An explicit balance can be given to override the
        locally stored balance signature. Blocks until a confirmation event is received or timeout.
        """
        if self.state != Channel.State.open:
            log.error('Channel must be open to request a close.')
            return
        log.info('Requesting close of channel to {} created at block #{}.'.format(
            self.receiver, self.block
        ))
        current_block = self.core.web3.eth.blockNumber

        if balance is not None:
            self.update_balance(balance)

        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.channel_manager,
            'uncooperativeClose',
            [
                self.receiver,
                self.block,
                self.balance
            ]
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for close confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelCloseRequested',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully sent channel close request in block {}.'.format(
                event['blockNumber']
            ))
            self.state = Channel.State.settling
            return event
        else:
            log.error('No event received.')
            return None
예제 #18
0
    def close(self, balance=None):
        """
        Attempts to request close on a channel. An explicit balance can be given to override the
        locally stored balance signature. Blocks until a confirmation event is received or timeout.
        """
        if self.state != Channel.State.open:
            log.error('Channel must be open to request a close.')
            return
        log.info('Requesting close of channel to {} created at block #{}.'.format(
            self.receiver, self.block
        ))
        current_block = self.core.web3.eth.blockNumber

        if balance is not None:
            self.update_balance(balance)

        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.channel_manager,
            'uncooperativeClose',
            [
                self.receiver,
                self.block,
                self.balance
            ]
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for close confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelCloseRequested',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully sent channel close request in block {}.'.format(
                event['blockNumber']
            ))
            self.state = Channel.State.settling
            return event
        else:
            log.error('No event received.')
            return None
예제 #19
0
def fund_account(address: str, eth_allowance: int, token_allowance: int,
                 token_contract: Contract, web3: Web3, wait_for_transaction):
    tx = create_signed_transaction(FAUCET_PRIVKEY,
                                   web3,
                                   to=address,
                                   value=eth_allowance,
                                   gas_limit=21000)
    tx_hash = web3.eth.sendRawTransaction(tx)
    wait_for_transaction(tx_hash)

    if token_allowance > 0:
        tx = create_signed_contract_transaction(FAUCET_PRIVKEY, token_contract,
                                                'transfer',
                                                [address, token_allowance])
        tx_hash = web3.eth.sendRawTransaction(tx)
        wait_for_transaction(tx_hash)
예제 #20
0
def sweep_account(
        private_key: str,
        faucet_address: str,
        token_contract: Contract,
        web3: Web3,
        wait_for_transaction
):
    address = privkey_to_addr(private_key)
    log.info('Sweeping account {}'.format(address))
    token_balance = token_contract.call().balanceOf(address)
    if token_balance > 0:
        tx = create_signed_contract_transaction(
            private_key,
            token_contract,
            'transfer',
            [
                faucet_address,
                token_balance
            ]
        )
        try:
            tx_hash = web3.eth.sendRawTransaction(tx)
        except ValueError as e:
            if e.args[0]['message'].startswith('Insufficient funds.'):
                pass
            else:
                raise
        else:
            wait_for_transaction(tx_hash)
            assert token_contract.call().balanceOf(address) == 0

    balance = web3.eth.getBalance(address)
    if balance < NETWORK_CFG.POT_GAS_LIMIT * NETWORK_CFG.GAS_PRICE:
        return
    tx = create_signed_transaction(
        private_key,
        web3,
        to=faucet_address,
        value=balance - NETWORK_CFG.POT_GAS_LIMIT * NETWORK_CFG.GAS_PRICE,
        gas_limit=NETWORK_CFG.POT_GAS_LIMIT
    )
    tx_hash = web3.eth.sendRawTransaction(tx)
    wait_for_transaction(tx_hash)
    assert web3.eth.getBalance(address) == 0, (
        'Sweeping of account {} (private key {}) failed.'.format(address, private_key)
    )
예제 #21
0
def close_open_channels(
        private_key: str,
        state: ChannelManagerState,
        channel_manager_contract: Contract,
        repetitions=None,
        wait=lambda: gevent.sleep(1)
):
    web3 = channel_manager_contract.web3
    channels_with_balance_proof = [
        c for c in state.channels.values() if c.last_signature is not None
    ]
    n_channels = len(state.channels)
    n_no_balance_proof = len(state.channels) - len(channels_with_balance_proof)
    n_txs_sent = 0
    pending_txs = {}

    if repetitions:
        iterator = range(repetitions)
    else:
        iterator = count()
    for _ in iterator:
        n_non_existant = 0
        n_invalid_balance_proof = 0
        for channel in channels_with_balance_proof:
            # lookup channel on block chain
            channel_id = (channel.sender, channel.receiver, channel.open_block_number)
            try:
                channel_info = channel_manager_contract.call().getChannelInfo(*channel_id)
            except (BadFunctionCallOutput, TransactionFailed):
                n_non_existant += 1
                continue
            _, deposit, settle_block_number, closing_balance, transferred_tokens = channel_info

            is_valid = channel.balance <= deposit
            n_invalid_balance_proof += int(not is_valid)
            close_sent = (channel.sender, channel.open_block_number) in pending_txs

            # send close if open or settling with wrong balance, unless already done
            if not close_sent and is_valid:
                closing_sig = sign_close(
                    private_key,
                    channel.sender,
                    channel.open_block_number,
                    channel.balance,
                    channel_manager_contract.address
                )

                raw_tx = create_signed_contract_transaction(
                    private_key,
                    channel_manager_contract,
                    'cooperativeClose',
                    [
                        channel.receiver,
                        channel.open_block_number,
                        channel.balance,
                        decode_hex(channel.last_signature),
                        closing_sig
                    ]
                )
                tx_hash = web3.eth.sendRawTransaction(raw_tx)
                log.info('sending close tx (hash: {})'.format(tx_hash))
                pending_txs[channel.sender, channel.open_block_number] = tx_hash
                n_txs_sent += 1

        # print status
        msg_status = 'block: {}, pending txs: {}, total txs sent: {}'
        msg_progress = (
            'initial channels: {}, settled: {}, pending txs: {}, no BP: {}, invalid BP: {}'
        )
        log.info(msg_status.format(web3.eth.blockNumber, len(pending_txs), n_txs_sent))
        log.info(msg_progress.format(
            n_channels,
            n_non_existant,
            len(pending_txs),
            n_no_balance_proof,
            n_invalid_balance_proof
        ))

        # wait for next block
        block_before = web3.eth.blockNumber
        while web3.eth.blockNumber == block_before:
            wait()

        # update pending txs
        confirmed = []
        for channel_id, tx_hash in pending_txs.items():
            receipt = web3.eth.getTransactionReceipt(tx_hash)
            if receipt is None:
                continue
            tx = web3.eth.getTransaction(tx_hash)
            if receipt.gasUsed == tx.gas:
                raise ValueError('Transaction failed, out of gas (hash: {})'.format(tx_hash))
            confirmed.append(channel_id)
        for channel_id in confirmed:
            pending_txs.pop(channel_id)
예제 #22
0
def close_open_channels(
        private_key: str,
        state: ChannelManagerState,
        channel_manager_contract: Contract,
        gas_price: int = None,
        wait=lambda: gevent.sleep(1)
):
    """Closes all open channels that belong to a receiver.

    Args:
        private_key (str): receiver's private key
        state (ChannelManagerState): channel manager state
        channel_manager_contract (str): address of the channel manager contract
        gas_price (int, optional): gas price you want to use
            (a network default will be used if not set)
        wait (callable): pause between checks for a succesfull transaction
    """
    web3 = channel_manager_contract.web3
    pending_txs = {}

    for channel in state.channels.values():
        if not channel.last_signature:
            continue

        channel_id = (channel.sender, channel.receiver, channel.open_block_number)
        try:
            channel_info = channel_manager_contract.call().getChannelInfo(*channel_id)
        except (BadFunctionCallOutput, TransactionFailed):
            continue
        _, deposit, settle_block_number, closing_balance, transferred_tokens = channel_info
        available_tokens = channel.balance - transferred_tokens

        if not channel.balance <= deposit:
            log.info(
                'Invalid channel: balance %d > deposit %d',
                channel.balance,
                deposit
            )
            continue
        closing_sig = utils.sign_close(
            private_key,
            channel.sender,
            channel.open_block_number,
            channel.balance,
            channel_manager_contract.address
        )

        raw_tx = utils.create_signed_contract_transaction(
            private_key,
            channel_manager_contract,
            'cooperativeClose',
            [
                channel.receiver,
                channel.open_block_number,
                channel.balance,
                decode_hex(channel.last_signature),
                closing_sig
            ],
            gas_price=gas_price,
        )
        tx_hash = web3.eth.sendRawTransaction(raw_tx)
        log.info(
            'Sending cooperative close tx (hash: %s): %d from %r',
            encode_hex(tx_hash),
            available_tokens,
            channel_id
        )
        pending_txs[channel_id] = (tx_hash, available_tokens)

    success = 0
    total_tokens = 0
    total_gas = 0
    gas_price = 0
    for channel_id, close_info in pending_txs.items():
        tx_hash, available_tokens = close_info
        receipt = None
        # wait for tx to be mined
        while True:
            receipt = web3.eth.getTransactionReceipt(tx_hash)
            if not receipt or not receipt.blockNumber:
                wait()
            else:
                break
        tx = web3.eth.getTransaction(tx_hash)
        total_gas += receipt.gasUsed
        gas_price = tx.gasPrice
        if receipt.gasUsed == tx.gas or getattr(receipt, 'status', None) == 0:
            log.error(
                'Transaction failed (hash: %s, tokens: %d, channel: %r)',
                encode_hex(tx_hash),
                available_tokens,
                channel_id
            )
        else:
            log.info(
                'Transaction success (hash: %s, tokens: %d, channel: %r)',
                encode_hex(tx_hash),
                available_tokens,
                channel_id
            )
            success += 1
            total_tokens += available_tokens
    log.info(
        'FINISHED Close all channels: total tokens recovered: %d, '
        'transactions succeeded: %d, total gas cost: %s ETH',
        total_tokens,
        success,
        web3.fromWei(total_gas * gas_price, 'ether'),
    )
예제 #23
0
    def open_channel(self, receiver_address: str, deposit: int):
        """
        Attempts to open a new channel to the receiver with the given deposit. Blocks until the
        creation transaction is found in a pending block or timeout is reached. The new channel
        state is returned.
        """
        assert isinstance(receiver_address, str)
        assert isinstance(deposit, int)
        assert deposit > 0

        token_balance = self.context.token.call().balanceOf(self.context.address)
        if token_balance < deposit:
            log.error(
                'Insufficient tokens available for the specified deposit ({}/{})'
                .format(token_balance, deposit)
            )
            return None

        current_block = self.context.web3.eth.blockNumber
        log.info('Creating channel to {} with an initial deposit of {} @{}'.format(
            receiver_address, deposit, current_block
        ))

        data = decode_hex(self.context.address) + decode_hex(receiver_address)
        tx = create_signed_contract_transaction(
            self.context.private_key,
            self.context.token,
            'transfer',
            [
                self.context.channel_manager.address,
                deposit,
                data
            ]
        )
        self.context.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for channel creation event on the blockchain...')
        filters = {
            '_sender_address': self.context.address,
            '_receiver_address': receiver_address
        }
        event = get_event_blocking(
            self.context.channel_manager,
            'ChannelCreated',
            from_block=current_block + 1,
            to_block='latest',
            argument_filters=filters
        )

        if event:
            log.debug('Event received. Channel created in block {}.'.format(event['blockNumber']))
            assert is_same_address(event['args']['_sender_address'], self.context.address)
            assert is_same_address(event['args']['_receiver_address'], receiver_address)
            channel = Channel(
                self.context,
                self.context.address,
                receiver_address,
                event['blockNumber'],
                event['args']['_deposit'],
                on_settle=lambda c: self.channels.remove(c)
            )
            self.channels.append(channel)
        else:
            log.error('Error: No event received.')
            channel = None

        return channel
예제 #24
0
def withdraw_from_channels(
        private_key: str,
        state: ChannelManagerState,
        channel_manager_contract: Contract,
        minimum: int = 1,
        gas_price: int = None,
        wait=lambda: gevent.sleep(1)
):
    web3 = channel_manager_contract.web3
    pending_txs = {}

    for channel in state.channels.values():
        if not channel.last_signature:
            continue

        channel_id = (channel.sender, channel.receiver, channel.open_block_number)
        try:
            channel_info = channel_manager_contract.call().getChannelInfo(*channel_id)
        except (BadFunctionCallOutput, TransactionFailed):
            continue
        _, deposit, settle_block_number, closing_balance, transferred_tokens = channel_info
        available_tokens = channel.balance - transferred_tokens

        if not channel.balance <= deposit or available_tokens < minimum:
            log.info(
                'Not enough available balance: %d - %d = %d < %d',
                channel.balance,
                transferred_tokens,
                available_tokens,
                minimum
            )
            continue
        raw_tx = utils.create_signed_contract_transaction(
            private_key,
            channel_manager_contract,
            'withdraw',
            [
                channel.open_block_number,
                channel.balance,
                decode_hex(channel.last_signature)
            ],
            gas_price=gas_price,
        )
        tx_hash = web3.eth.sendRawTransaction(raw_tx)
        log.info(
            'Sending withdraw tx (hash: %s): %d from %r',
            encode_hex(tx_hash),
            available_tokens,
            channel_id
        )
        pending_txs[channel_id] = (tx_hash, available_tokens)

    success = 0
    total_tokens = 0
    total_gas = 0
    gas_price = 0
    for channel_id, withdraw_info in pending_txs.items():
        tx_hash, available_tokens = withdraw_info
        receipt = None
        # wait for tx to be mined
        while True:
            receipt = web3.eth.getTransactionReceipt(tx_hash)
            if not receipt or not receipt.blockNumber:
                wait()
            else:
                break
        tx = web3.eth.getTransaction(tx_hash)
        total_gas += receipt.gasUsed
        gas_price = tx.gasPrice
        if receipt.gasUsed == tx.gas or getattr(receipt, 'status', None) == 0:
            log.error(
                'Transaction failed (hash: %s, tokens: %d, channel: %r)',
                encode_hex(tx_hash),
                available_tokens,
                channel_id
            )
        else:
            log.info(
                'Transaction success (hash: %s, tokens: %d, channel: %r)',
                encode_hex(tx_hash),
                available_tokens,
                channel_id
            )
            success += 1
            total_tokens += available_tokens
    log.info(
        'FINISHED Withdraw: total tokens recovered: %d, '
        'transactions succeeded: %d, total gas cost: %s ETH',
        total_tokens,
        success,
        web3.fromWei(total_gas * gas_price, 'ether'),
    )
예제 #25
0
def close_open_channels(private_key: str,
                        state: ChannelManagerState,
                        channel_manager_contract: Contract,
                        gas_price: int = None,
                        wait=lambda: gevent.sleep(1)):
    web3 = channel_manager_contract.web3
    pending_txs = {}

    for channel in state.channels.values():
        if not channel.last_signature:
            continue

        channel_id = (channel.sender, channel.receiver,
                      channel.open_block_number)
        try:
            channel_info = channel_manager_contract.call().getChannelInfo(
                *channel_id)
        except (BadFunctionCallOutput, TransactionFailed):
            continue
        _, deposit, settle_block_number, closing_balance, transferred_tokens = channel_info
        available_tokens = channel.balance - transferred_tokens

        if not channel.balance <= deposit:
            log.info('Invalid channel: balance %d > deposit %d',
                     channel.balance, deposit)
            continue
        closing_sig = utils.sign_close(private_key, channel.sender,
                                       channel.open_block_number,
                                       channel.balance,
                                       channel_manager_contract.address)

        raw_tx = utils.create_signed_contract_transaction(
            private_key,
            channel_manager_contract,
            'cooperativeClose',
            [
                channel.receiver, channel.open_block_number, channel.balance,
                decode_hex(channel.last_signature), closing_sig
            ],
            gas_price=gas_price,
        )
        tx_hash = web3.eth.sendRawTransaction(raw_tx)
        log.info('Sending cooperative close tx (hash: %s): %d from %r',
                 encode_hex(tx_hash), available_tokens, channel_id)
        pending_txs[channel_id] = (tx_hash, available_tokens)

    success = 0
    total_tokens = 0
    total_gas = 0
    gas_price = 0
    for channel_id, close_info in pending_txs.items():
        tx_hash, available_tokens = close_info
        receipt = None
        # wait for tx to be mined
        while True:
            receipt = web3.eth.getTransactionReceipt(tx_hash)
            if not receipt or not receipt.blockNumber:
                wait()
            else:
                break
        tx = web3.eth.getTransaction(tx_hash)
        total_gas += receipt.gasUsed
        gas_price = tx.gasPrice
        if receipt.gasUsed == tx.gas or getattr(receipt, 'status', None) == 0:
            log.error('Transaction failed (hash: %s, tokens: %d, channel: %r)',
                      encode_hex(tx_hash), available_tokens, channel_id)
        else:
            log.info('Transaction success (hash: %s, tokens: %d, channel: %r)',
                     encode_hex(tx_hash), available_tokens, channel_id)
            success += 1
            total_tokens += available_tokens
    log.info(
        'FINISHED Close all channels: total tokens recovered: %d, '
        'transactions succeeded: %d, total gas cost: %s ETH',
        total_tokens,
        success,
        web3.fromWei(total_gas * gas_price, 'ether'),
    )
예제 #26
0
    def open_channel(self, receiver_address: str,
                     deposit: int) -> Optional[Channel]:
        """Open a channel with a receiver and deposit

        Attempts to open a new channel to the receiver with the given deposit. Blocks until the
        creation transaction is found in a pending block or timeout is reached.

        Args:
            receiver_address: the partner with whom the channel should be opened
            deposit:  the initial deposit for the channel (Should be > 0)

        Returns:
            The opened channel, if successful. Otherwise `None`

        """

        assert isinstance(receiver_address, str)
        assert isinstance(deposit, int)
        assert deposit > 0

        token_balance = self.context.token.call().balanceOf(
            self.context.address)
        if token_balance < deposit:
            log.error(
                'Insufficient tokens available for the specified deposit ({}/{})'
                .format(token_balance, deposit))
            return None

        current_block = self.context.web3.eth.blockNumber
        log.info(
            'Creating channel to {} with an initial deposit of {} @{}'.format(
                receiver_address, deposit, current_block))

        data = decode_hex(self.context.address) + decode_hex(receiver_address)
        tx = create_signed_contract_transaction(
            self.context.private_key, self.context.token, 'transfer',
            [self.context.channel_manager.address, deposit, data])
        self.context.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for channel creation event on the blockchain...')
        filters = {
            '_sender_address': self.context.address,
            '_receiver_address': receiver_address
        }
        event = get_event_blocking(self.context.channel_manager,
                                   'ChannelCreated',
                                   from_block=current_block + 1,
                                   to_block='latest',
                                   argument_filters=filters)

        if event:
            log.debug('Event received. Channel created in block {}.'.format(
                event['blockNumber']))
            assert is_same_address(event['args']['_sender_address'],
                                   self.context.address)
            assert is_same_address(event['args']['_receiver_address'],
                                   receiver_address)
            channel = Channel(self.context,
                              self.context.address,
                              receiver_address,
                              event['blockNumber'],
                              event['args']['_deposit'],
                              on_settle=lambda c: self.channels.remove(c))
            self.channels.append(channel)
        else:
            log.error('Error: No event received.')
            channel = None

        return channel