Пример #1
0
 def sign_close(self, sender: str, open_block_number: int, balance: int):
     """Sign an agreement for a channel closing.
     Returns:
         channel close signature (str): a signature that can be used client-side to close
         the channel by directly calling contract's close method on-chain.
     """
     assert is_checksum_address(sender)
     if (sender, open_block_number) not in self.channels:
         raise NoOpenChannel('Channel does not exist or has been closed'
                             '(sender=%s, open_block_number=%d)' % (sender, open_block_number))
     c = self.channels[sender, open_block_number]
     if c.is_closed:
         raise NoOpenChannel('Channel closing has been requested already.')
     assert balance is not None
     if c.last_signature is None:
         raise NoBalanceProofReceived('Payment has not been registered.')
     if balance != c.balance:
         raise InvalidBalanceProof('Requested closing balance does not match latest one.')
     c.is_closed = True
     c.mtime = time.time()
     receiver_sig = sign_close(
         self.private_key,
         sender,
         open_block_number,
         c.balance,
         self.channel_manager_contract.address
     )
     self.state.set_channel(c)
     self.log.info('signed cooperative closing message (sender %s, block number %s)',
                   sender, open_block_number)
     return receiver_sig
Пример #2
0
def close_channel_cooperatively(channel: Channel,
                                privkey_receiver: str,
                                balance: int = None):
    if balance is not None:
        channel.update_balance(balance)
    closing_sig = sign_close(privkey_receiver, channel.balance_sig)
    assert channel.close_cooperatively(closing_sig)
Пример #3
0
 def sign_close(self, sender, open_block_number, balance):
     """Sign an agreement for a channel closing."""
     assert is_checksum_address(sender)
     if (sender, open_block_number) not in self.channels:
         raise NoOpenChannel('Channel does not exist or has been closed'
                             '(sender=%s, open_block_number=%d)' %
                             (sender, open_block_number))
     c = self.channels[sender, open_block_number]
     if c.is_closed:
         raise NoOpenChannel('Channel closing has been requested already.')
     assert balance is not None
     if c.last_signature is None:
         raise NoBalanceProofReceived('Payment has not been registered.')
     if balance != c.balance:
         raise InvalidBalanceProof(
             'Requested closing balance does not match latest one.')
     c.is_closed = True
     c.mtime = time.time()
     receiver_sig = sign_close(self.private_key, sender, open_block_number,
                               c.balance,
                               self.channel_manager_contract.address)
     self.state.set_channel(c)
     self.log.info(
         'signed cooperative closing message (sender %s, block number %s)',
         sender, open_block_number)
     return receiver_sig
Пример #4
0
 def sign_close(self, sender: str, open_block_number: int, balance: int):
     """Sign an agreement for a channel closing.
     Returns:
         channel close signature (str): a signature that can be used client-side to close
         the channel by directly calling contract's close method on-chain.
     """
     assert is_checksum_address(sender)
     if (sender, open_block_number) not in self.channels:
         raise NoOpenChannel('Channel does not exist or has been closed'
                             '(sender=%s, open_block_number=%d)' %
                             (sender, open_block_number))
     c = self.channels[sender, open_block_number]
     #  if c.is_closed:
     #      raise NoOpenChannel('Channel closing has been requested already.')
     assert balance is not None
     #  if balance != 0 and c.last_signature is None:
     #      raise NoBalanceProofReceived('Payment has not been registered.')
     if balance != c.balance:
         raise InvalidBalanceProof(
             'Requested closing balance does not match latest one.')
     #  c.is_closed = True
     c.mtime = time.time()
     receiver_sig = sign_close(self.private_key, sender, open_block_number,
                               c.balance,
                               self.channel_manager_contract.address)
     self.state.set_channel(c)
     self.log.info(
         'signed cooperative closing message (sender %s, block number %s)',
         sender, open_block_number)
     return receiver_sig
Пример #5
0
def test_sign_close_contract(channel_manager_contract: Contract):
    sig = sign_close(
        RECEIVER_PRIVATE_KEY, SENDER_ADDR, 315832, 13, channel_manager_contract.address
    )
    receiver_recovered = channel_manager_contract.call().extractClosingSignature(
        SENDER_ADDR, 315832, 13, sig
    )
    assert is_same_address(receiver_recovered, RECEIVER_ADDR)
Пример #6
0
def close_channel_cooperatively(channel: Channel,
                                privkey_receiver: str,
                                contract_address: str,
                                balance: int = None):
    if balance is not None:
        channel.update_balance(balance)
    closing_sig = sign_close(privkey_receiver, channel.sender, channel.block,
                             channel.balance, contract_address)
    assert channel.close_cooperatively(closing_sig)
Пример #7
0
def test_cooperative_close(client: Client, receiver_privkey, receiver_address):
    c = client.get_suitable_channel(receiver_address, 3)
    c.create_transfer(3)

    assert c.deposit >= 3
    assert c.balance == 3

    sig = sign_close(receiver_privkey, c.balance_sig)
    assert c.close_cooperatively(sig)
    assert c.state == Channel.State.closed
Пример #8
0
def test_cooperative_close(client: Client, receiver_privkey, receiver_address):
    c = client.get_suitable_channel(receiver_address, 3)
    assert c is not None
    c.create_transfer(3)

    assert c.deposit >= 3
    assert c.balance == 3

    sig = sign_close(receiver_privkey, c.sender, c.block, c.balance,
                     client.context.channel_manager.address)
    assert c.close_cooperatively(sig)
    assert c.state == Channel.State.closed
Пример #9
0
def close_channel_cooperatively(
        channel: Channel, privkey_receiver: str, contract_address: str, balance: int=None
):
    if balance is not None:
        channel.update_balance(balance)
    closing_sig = sign_close(
        privkey_receiver,
        channel.sender,
        channel.block,
        channel.balance,
        contract_address
    )
    assert channel.close_cooperatively(closing_sig)
Пример #10
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
Пример #11
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
Пример #12
0
 def delete(self, sender_address):
     parser = reqparse.RequestParser()
     parser.add_argument('open_block', type=int, help='block the channel was opened')
     parser.add_argument('signature', help='last balance proof signature')
     args = parser.parse_args()
     if args.signature is None:
         return "Bad signature format", 400
     if args.block is None:
         return "No opening block specified", 400
     if sender_address and is_address(sender_address):
         sender_address = to_checksum_address(sender_address)
     channel = self.channel_manager.channels[sender_address, args.block]
     if channel.last_signature != args.signature:
         return "Invalid or outdated balance signature", 400
     ret = sign_close(self.channel_manager.private_key, args.signature)
     return ret, 200
Пример #13
0
 def delete(self, sender_address):
     parser = reqparse.RequestParser()
     parser.add_argument('open_block',
                         type=int,
                         help='block the channel was opened')
     parser.add_argument('signature', help='last balance proof signature')
     args = parser.parse_args()
     if args.signature is None:
         return "Bad signature format", 400
     if args.block is None:
         return "No opening block specified", 400
     channel = self.channel_manager.channels[sender_address, args.block]
     if channel.last_signature != args.signature:
         return "Invalid or outdated balance signature", 400
     ret = sign_close(self.channel_manager.private_key, args.signature)
     return ret, 200
Пример #14
0
def test_cooperative_close(client: Client, receiver_privkey, receiver_address):
    c = client.get_suitable_channel(receiver_address, 3)
    assert c is not None
    c.create_transfer(3)

    assert c.deposit >= 3
    assert c.balance == 3

    sig = sign_close(
        receiver_privkey,
        c.sender,
        c.block,
        c.balance,
        client.context.channel_manager.address
    )
    assert c.close_cooperatively(sig)
    assert c.state == Channel.State.closed
Пример #15
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)
Пример #16
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'),
    )
Пример #17
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'),
    )
Пример #18
0
def test_verify_closing_sign(channel_manager_address: str):
    sig = sign_close(RECEIVER_PRIVATE_KEY, SENDER_ADDR, 315832, 13,
                     channel_manager_address)
    receiver_recovered = verify_closing_sig(SENDER_ADDR, 315832, 13, sig,
                                            channel_manager_address)
    assert is_same_address(receiver_recovered, RECEIVER_ADDR)
Пример #19
0
def test_sign_close_contract(channel_manager_contract: Contract):
    sig = sign_close(RECEIVER_PRIVATE_KEY, SENDER_ADDR, 315832, 13,
                     channel_manager_contract.address)
    receiver_recovered = channel_manager_contract.call(
    ).extractClosingSignature(SENDER_ADDR, 315832, 13, sig)
    assert is_same_address(receiver_recovered, RECEIVER_ADDR)
Пример #20
0
def test_verify_closing_sign(channel_manager_address: str):
    sig = sign_close(
        RECEIVER_PRIVATE_KEY, SENDER_ADDR, 315832, 13, channel_manager_address
    )
    receiver_recovered = verify_closing_sig(SENDER_ADDR, 315832, 13, sig, channel_manager_address)
    assert is_same_address(receiver_recovered, RECEIVER_ADDR)