Пример #1
0
    async def test_session_bloat_from_socket_timeout(self):
        await self.account.ensure_address_gap()

        address1, address2 = await self.account.receiving.get_addresses(
            limit=2, only_usable=True)
        sendtxid1 = await self.blockchain.send_to_address(address1, 5)
        sendtxid2 = await self.blockchain.send_to_address(address2, 5)

        await self.blockchain.generate(1)
        await asyncio.wait([
            self.on_transaction_id(sendtxid1),
            self.on_transaction_id(sendtxid2)
        ])

        self.assertEqual(d2l(await self.account.get_balance()), '10.0')

        channel = Claim()
        channel_txo = Output.pay_claim_name_pubkey_hash(
            l2d('1.0'), '@bar', channel,
            self.account.ledger.address_to_hash160(address1))
        channel_txo.generate_channel_private_key()
        channel_txo.script.generate()
        channel_tx = await Transaction.create([], [channel_txo],
                                              [self.account], self.account)

        stream = Claim()
        stream.stream.description = "0" * 8000
        stream_txo = Output.pay_claim_name_pubkey_hash(
            l2d('1.0'), 'foo', stream,
            self.account.ledger.address_to_hash160(address1))
        stream_tx = await Transaction.create([], [stream_txo], [self.account],
                                             self.account)
        stream_txo.sign(channel_txo)
        await stream_tx.sign([self.account])
        self.paused_session.clear()
        self.resumed_session.clear()

        await self.broadcast(channel_tx)
        await self.broadcast(stream_tx)
        await asyncio.wait_for(self.paused_session.wait(), 2)
        self.assertEqual(1, len(self.session_manager.sessions))

        real_sock = self.client_session.transport._extra.pop('socket')
        mock_sock = Mock(spec=socket.socket)

        for attr in dir(real_sock):
            if not attr.startswith('__'):
                setattr(mock_sock, attr, getattr(real_sock, attr))

        def recv(*a, **kw):
            raise TimeoutError("[Errno 110] Connection timed out")

        mock_sock.recv = recv
        self.client_session.transport._sock = mock_sock
        self.client_session.transport._extra['socket'] = mock_sock
        self.assertFalse(self.resumed_session.is_set())
        self.assertFalse(self.session_manager.session_event.is_set())
        await self.session_manager.session_event.wait()
        self.assertEqual(0, len(self.session_manager.sessions))
Пример #2
0
 def get_channel_update(self, channel, amount, key=b'a'):
     self._set_channel_key(channel, key)
     return self._make_tx(
         Output.pay_update_claim_pubkey_hash(amount, channel.claim_name,
                                             channel.claim_id,
                                             channel.claim, b'abc'),
         Input.spend(channel))
Пример #3
0
 def get_support(self, tx, amount):
     claim = Transaction(tx[0].raw).outputs[0]
     return self._make_tx(
         Output.pay_support_pubkey_hash(
             amount, claim.claim_name, claim.claim_id, b'abc'
          )
     )
Пример #4
0
    async def test_sign(self):
        account = self.ledger.account_class.from_dict(
            self.ledger, Wallet(), {
                "seed":
                    "carbon smart garage balance margin twelve chest sword toas"
                    "t envelope bottom stomach absent"
            }
        )

        await account.ensure_address_gap()
        address1, address2 = await account.receiving.get_addresses(limit=2)
        pubkey_hash1 = self.ledger.address_to_hash160(address1)
        pubkey_hash2 = self.ledger.address_to_hash160(address2)

        tx = Transaction() \
            .add_inputs([Input.spend(get_output(int(2*COIN), pubkey_hash1))]) \
            .add_outputs([Output.pay_pubkey_hash(int(1.9*COIN), pubkey_hash2)])

        await tx.sign([account])

        self.assertEqual(
            hexlify(tx.inputs[0].script.values['signature']),
            b'304402200dafa26ad7cf38c5a971c8a25ce7d85a076235f146126762296b1223c42ae21e022020ef9eeb8'
            b'398327891008c5c0be4357683f12cb22346691ff23914f457bf679601'
        )
Пример #5
0
 def get_channel(self, title, amount, name='@foo', key=b'a'):
     claim = Claim()
     claim.channel.title = title
     channel = Output.pay_claim_name_pubkey_hash(amount, name, claim,
                                                 b'abc')
     self._set_channel_key(channel, key)
     return self._make_tx(channel)
Пример #6
0
 def get_stream(self, title, amount, name='foo', channel=None):
     claim = Claim()
     claim.stream.title = title
     result = self._make_tx(Output.pay_claim_name_pubkey_hash(amount, name, claim, b'abc'))
     if channel:
         result[0].outputs[0].sign(channel)
         result[0]._reset()
     return result
Пример #7
0
 def get_stream_update(self, tx, amount):
     claim = Transaction(tx[0].serialize()).outputs[0]
     return self._make_tx(
         Output.pay_update_claim_pubkey_hash(
             amount, claim.claim_name, claim.claim_id, claim.claim, b'abc'
         ),
         Input.spend(claim)
     )
Пример #8
0
 def get_repost(self, claim_id, amount, channel):
     claim = Claim()
     claim.repost.reference.claim_id = claim_id
     result = self._make_tx(
         Output.pay_claim_name_pubkey_hash(amount, 'repost', claim, b'abc'))
     result[0].outputs[0].sign(channel)
     result[0]._reset()
     return result
Пример #9
0
def verify(channel, data, signature, channel_hash=None):
    pieces = [
        signature['signing_ts'].encode(), channel_hash or channel.claim_hash,
        data
    ]
    return Output.is_signature_valid(
        get_encoded_signature(signature['signature']),
        sha256(b''.join(pieces)), channel.claim.channel.public_key_bytes)
Пример #10
0
 def get_stream_update(self, tx, amount, channel=None):
     stream = Transaction(tx[0].raw).outputs[0]
     result = self._make_tx(
         Output.pay_update_claim_pubkey_hash(amount, stream.claim_name,
                                             stream.claim_id, stream.claim,
                                             b'abc'), Input.spend(stream))
     if channel:
         result[0].outputs[0].sign(channel)
         result[0]._reset()
     return result
Пример #11
0
    async def test_balance(self):
        address = await self.account.receiving.get_or_create_usable_address()
        hash160 = self.ledger.address_to_hash160(address)

        tx = Transaction(is_verified=True)\
            .add_outputs([Output.pay_pubkey_hash(100, hash160)])
        await self.ledger.db.insert_transaction(tx)
        await self.ledger.db.save_transaction_io(tx, address, hash160,
                                                 '{}:{}:'.format(tx.id, 1))
        self.assertEqual(await self.account.get_balance(), 100)

        tx = Transaction(is_verified=True)\
            .add_outputs([Output.pay_claim_name_pubkey_hash(100, 'foo', b'', hash160)])
        await self.ledger.db.insert_transaction(tx)
        await self.ledger.db.save_transaction_io(tx, address, hash160,
                                                 '{}:{}:'.format(tx.id, 1))
        self.assertEqual(await self.account.get_balance(),
                         100)  # claim names don't count towards balance
        self.assertEqual(await self.account.get_balance(include_claims=True),
                         200)
Пример #12
0
def is_comment_signed_by_channel(comment: dict, channel: Output):
    try:
        pieces = [
            comment['signing_ts'].encode(), channel.claim_hash,
            comment['comment'].encode()
        ]
        return Output.is_signature_valid(
            get_encoded_signature(comment['signature']),
            sha256(b''.join(pieces)), channel.claim.channel.public_key_bytes)
    except KeyError:
        pass
    return False
Пример #13
0
 async def _test_transaction(self, send_amount, address, inputs, change):
     tx = await Transaction.create([], [
         Output.pay_pubkey_hash(send_amount,
                                self.ledger.address_to_hash160(address))
     ], [self.account], self.account)
     await self.ledger.broadcast(tx)
     input_amounts = [txi.amount for txi in tx.inputs]
     self.assertListEqual(inputs, input_amounts)
     self.assertEqual(len(inputs), len(tx.inputs))
     self.assertEqual(2, len(tx.outputs))
     self.assertEqual(send_amount, tx.outputs[0].amount)
     self.assertEqual(change, tx.outputs[1].amount)
     return tx
Пример #14
0
    async def test_sending_and_receiving(self):
        account1, account2 = self.account, self.wallet.generate_account(self.ledger)
        await self.ledger.subscribe_account(account2)

        await self.assertBalance(account1, '0.0')
        await self.assertBalance(account2, '0.0')

        addresses = await account1.receiving.get_addresses()
        txids = await asyncio.gather(*(
            self.blockchain.send_to_address(address, 1.1) for address in addresses[:5]
        ))
        await asyncio.wait([self.on_transaction_id(txid) for txid in txids])  # mempool
        await self.blockchain.generate(1)
        await asyncio.wait([self.on_transaction_id(txid) for txid in txids])  # confirmed
        await self.assertBalance(account1, '5.5')
        await self.assertBalance(account2, '0.0')

        address2 = await account2.receiving.get_or_create_usable_address()
        tx = await Transaction.create(
            [],
            [Output.pay_pubkey_hash(
                coins_to_satoshis('2.0'), self.ledger.address_to_hash160(address2)
            )],
            [account1], account1
        )
        await self.broadcast(tx)
        await self.ledger.wait(tx)  # mempool
        await self.blockchain.generate(1)
        await self.ledger.wait(tx)  # confirmed

        await self.assertBalance(account1, '3.499802')
        await self.assertBalance(account2, '2.0')

        utxos = await self.account.get_utxos()
        tx = await Transaction.create(
            [Input.spend(utxos[0])],
            [],
            [account1], account1
        )
        await self.broadcast(tx)
        await self.ledger.wait(tx)  # mempool
        await self.blockchain.generate(1)
        await self.ledger.wait(tx)  # confirmed

        tx = (await account1.get_transactions(include_is_my_input=True, include_is_my_output=True))[1]
        self.assertEqual(satoshis_to_coins(tx.inputs[0].amount), '1.1')
        self.assertEqual(satoshis_to_coins(tx.inputs[1].amount), '1.1')
        self.assertEqual(satoshis_to_coins(tx.outputs[0].amount), '2.0')
        self.assertEqual(tx.outputs[0].get_address(self.ledger), address2)
        self.assertTrue(tx.outputs[0].is_internal_transfer)
        self.assertTrue(tx.outputs[1].is_internal_transfer)
Пример #15
0
def is_comment_signed_by_channel(comment: dict, channel: Output, sign_comment_id=False):
    if isinstance(channel, Output):
        try:
            signing_field = comment['comment_id'] if sign_comment_id else comment['comment']
            pieces = [
                comment['signing_ts'].encode(),
                cid2hash(comment['channel_id']),
                signing_field.encode()
            ]
            return Output.is_signature_valid(
                get_encoded_signature(comment['signature']),
                sha256(b''.join(pieces)),
                channel.claim.channel.public_key_bytes
            )
        except KeyError:
            pass
    return False
Пример #16
0
def check(db_path, claim_id):
    db = sqlite3.connect(db_path)
    db.row_factory = sqlite3.Row
    claim = db.execute('select * from claim where claim_id=?', (claim_id,)).fetchone()
    if not claim:
        print('Could not find claim.')
        return
    channel = db.execute('select * from claim where claim_hash=?', (claim['channel_hash'],)).fetchone()
    if not channel:
        print('Could not find channel for this claim.')
    print(f"Claim: {claim['claim_name']}")
    print(f"Channel: {channel['claim_name']}")
    print(f"Signature: {hexlify(claim['signature']).decode()}")
    print(f"Digest: {hexlify(claim['signature_digest']).decode()}")
    print(f"Pubkey: {hexlify(channel['public_key_bytes']).decode()}")
    print("Valid: {}".format(Output.is_signature_valid(
        claim['signature'], claim['signature_digest'], channel['public_key_bytes']
    )))
Пример #17
0
    async def pay(self):
        while self.running:
            await asyncio.sleep(self.payment_period)
            features = await self.ledger.network.retriable_call(
                self.ledger.network.get_server_features)
            address = features['payment_address']
            amount = str(features['daily_fee'])
            if not address or not amount:
                continue

            if not self.ledger.is_valid_address(address):
                self._on_payment_controller.add_error(
                    ServerPaymentInvalidAddressError(address))
                continue

            if self.wallet.is_locked:
                self._on_payment_controller.add_error(
                    ServerPaymentWalletLockedError())
                continue

            amount = lbc_to_dewies(
                features['daily_fee']
            )  # check that this is in lbc and not dewies
            limit = lbc_to_dewies(self.max_fee)
            if amount > limit:
                self._on_payment_controller.add_error(
                    ServerPaymentFeeAboveMaxAllowedError(
                        features['daily_fee'], self.max_fee))
                continue

            tx = await Transaction.create(
                [], [
                    Output.pay_pubkey_hash(
                        amount, self.ledger.address_to_hash160(address))
                ], self.wallet.get_accounts_or_all(None),
                self.wallet.get_account_or_default(None))

            await self.ledger.broadcast(tx)
            if self.analytics_manager:
                await self.analytics_manager.send_credits_sent()
            self._on_payment_controller.add(tx)
Пример #18
0
    async def test_get_utxo(self):
        address = yield self.account.receiving.get_or_create_usable_address()
        hash160 = self.ledger.address_to_hash160(address)

        tx = Transaction(is_verified=True)\
            .add_outputs([Output.pay_pubkey_hash(100, hash160)])
        await self.ledger.db.save_transaction_io('insert', tx, address,
                                                 hash160, f'{tx.id}:1:')

        utxos = await self.account.get_utxos()
        self.assertEqual(len(utxos), 1)

        tx = Transaction(is_verified=True)\
            .add_inputs([Input.spend(utxos[0])])
        await self.ledger.db.save_transaction_io('insert', tx, address,
                                                 hash160, f'{tx.id}:1:')
        self.assertEqual(await self.account.get_balance(include_claims=True),
                         0)

        utxos = await self.account.get_utxos()
        self.assertEqual(len(utxos), 0)
Пример #19
0
 async def create_purchase_transaction(
         self, accounts: List[Account], txo: Output, exchange: ExchangeRateManager, override_max_key_fee=False):
     fee = txo.claim.stream.fee
     fee_amount = exchange.to_dewies(fee.currency, fee.amount)
     if not override_max_key_fee and self.config.max_key_fee:
         max_fee = self.config.max_key_fee
         max_fee_amount = exchange.to_dewies(max_fee['currency'], Decimal(max_fee['amount']))
         if max_fee_amount and fee_amount > max_fee_amount:
             error_fee = f"{dewies_to_lbc(fee_amount)} LBC"
             if fee.currency != 'LBC':
                 error_fee += f" ({fee.amount} {fee.currency})"
             error_max_fee = f"{dewies_to_lbc(max_fee_amount)} LBC"
             if max_fee['currency'] != 'LBC':
                 error_max_fee += f" ({max_fee['amount']} {max_fee['currency']})"
             raise KeyFeeAboveMaxAllowedError(
                 f"Purchase price of {error_fee} exceeds maximum "
                 f"configured price of {error_max_fee}."
             )
     fee_address = fee.address or txo.get_address(self.ledger)
     return await Transaction.purchase(
         txo.claim_id, fee_amount, fee_address, accounts, accounts[0]
     )
Пример #20
0
def get_transaction(txo=None):
    return Transaction() \
        .add_inputs([get_input()]) \
        .add_outputs([txo or Output.pay_pubkey_hash(CENT, NULL_HASH32)])
Пример #21
0
def get_channel(claim_name='@foo'):
    channel_txo = Output.pay_claim_name_pubkey_hash(CENT, claim_name, Claim(), b'abc')
    channel_txo.generate_channel_private_key()
    get_tx().add_outputs([channel_txo])
    return channel_txo
Пример #22
0
def get_output(amount=CENT, pubkey_hash=NULL_HASH32):
    return Transaction() \
        .add_outputs([Output.pay_pubkey_hash(amount, pubkey_hash)]) \
        .outputs[0]
Пример #23
0
def get_stream(claim_name='foo'):
    stream_txo = Output.pay_claim_name_pubkey_hash(CENT, claim_name, Claim(), b'abc')
    get_tx().add_outputs([stream_txo])
    return stream_txo
Пример #24
0
    async def test_sqlite_coin_chooser(self):
        wallet_manager = WalletManager([self.wallet],
                                       {self.ledger.get_id(): self.ledger})
        await self.generate(300)

        await self.assertBalance(self.account, '0.0')
        address = await self.account.receiving.get_or_create_usable_address()
        other_account = self.wallet.generate_account(self.ledger)
        other_address = await other_account.receiving.get_or_create_usable_address(
        )
        self.ledger.coin_selection_strategy = 'sqlite'
        await self.ledger.subscribe_account(other_account)

        accepted = asyncio.ensure_future(self.on_address_update(address))
        _ = await self.send_to_address_and_wait(address, 1.0)
        await accepted

        accepted = asyncio.ensure_future(self.on_address_update(address))
        _ = await self.send_to_address_and_wait(address, 1.0)
        await accepted

        accepted = asyncio.ensure_future(self.on_address_update(address))
        _ = await self.send_to_address_and_wait(address, 3.0)
        await accepted

        accepted = asyncio.ensure_future(self.on_address_update(address))
        _ = await self.send_to_address_and_wait(address, 5.0)
        await accepted

        accepted = asyncio.ensure_future(self.on_address_update(address))
        _ = await self.send_to_address_and_wait(address, 10.0)
        await accepted

        await self.assertBalance(self.account, '20.0')
        await self.assertSpendable(
            [99992600, 99992600, 299992600, 499992600, 999992600])

        # send 1.5 lbc

        first_tx = await Transaction.create([], [
            Output.pay_pubkey_hash(
                150000000, self.ledger.address_to_hash160(other_address))
        ], [self.account], self.account)

        self.assertEqual(2, len(first_tx.inputs))
        self.assertEqual(2, len(first_tx.outputs))
        self.assertEqual(100000000, first_tx.inputs[0].amount)
        self.assertEqual(100000000, first_tx.inputs[1].amount)
        self.assertEqual(150000000, first_tx.outputs[0].amount)
        self.assertEqual(49980200, first_tx.outputs[1].amount)

        await self.assertBalance(self.account, '18.0')
        await self.assertSpendable([299992600, 499992600, 999992600])

        await wallet_manager.broadcast_or_release(first_tx, blocking=True)
        await self.assertSpendable([49972800, 299992600, 499992600, 999992600])
        # 0.499, 3.0, 5.0, 10.0
        await self.assertBalance(self.account, '18.499802')

        # send 1.5lbc again

        second_tx = await self._test_transaction(150000000, other_address,
                                                 [49980200, 300000000],
                                                 199960400)
        await self.assertSpendable([499992600, 999992600])

        # replicate cancelling the api call after the tx broadcast while ledger.wait'ing it
        e = asyncio.Event()

        real_broadcast = self.ledger.broadcast

        async def broadcast(tx):
            try:
                return await real_broadcast(tx)
            except lbry.wallet.rpc.jsonrpc.RPCError as err:
                # this is expected in tests where we try to double spend.
                if 'the transaction was rejected by network rules.' in str(
                        err):
                    pass
                else:
                    raise err
            finally:
                e.set()

        self.ledger.broadcast = broadcast

        broadcast_task = asyncio.create_task(
            wallet_manager.broadcast_or_release(second_tx, blocking=True))
        # wait for the broadcast to finish
        await e.wait()
        # cancel the api call
        broadcast_task.cancel()
        with self.assertRaises(asyncio.CancelledError):
            await broadcast_task

        # test if sending another 1.5 lbc will try to double spend the inputs from the cancelled tx
        tx1 = await self._test_transaction(150000000, other_address,
                                           [500000000], 349987600)
        await self.ledger.wait(tx1, timeout=1)
        # wait for the cancelled transaction too, so that it's in the database
        # needed to keep everything deterministic
        await self.ledger.wait(second_tx, timeout=1)
        await self.assertSpendable([199953000, 349980200, 999992600])

        # spend deep into the mempool and see what else breaks
        tx2 = await self._test_transaction(150000000, other_address,
                                           [199960400], 49948000)
        await self.assertSpendable([349980200, 999992600])
        await self.ledger.wait(tx2, timeout=1)
        await self.assertSpendable([49940600, 349980200, 999992600])

        tx3 = await self._test_transaction(150000000, other_address,
                                           [49948000, 349987600], 249915800)
        await self.assertSpendable([999992600])
        await self.ledger.wait(tx3, timeout=1)
        await self.assertSpendable([249908400, 999992600])

        tx4 = await self._test_transaction(150000000, other_address,
                                           [249915800], 99903400)
        await self.assertSpendable([999992600])
        await self.ledger.wait(tx4, timeout=1)
        await self.assertBalance(self.account, '10.999034')
        await self.assertSpendable([99896000, 999992600])

        # spend more
        tx5 = await self._test_transaction(100000000, other_address,
                                           [99903400, 1000000000], 999883600)
        await self.assertSpendable([])
        await self.ledger.wait(tx5, timeout=1)
        await self.assertSpendable([999876200])
        await self.assertBalance(self.account, '9.998836')
Пример #25
0
    async def test_variety_of_transactions_and_longish_history(self):
        await self.generate(300)
        await self.assertBalance(self.account, '0.0')
        addresses = await self.account.receiving.get_addresses()

        # send 10 coins to first 10 receiving addresses and then 10 transactions worth 10 coins each
        # to the 10th receiving address for a total of 30 UTXOs on the entire account
        for i in range(10):
            notification = asyncio.ensure_future(
                self.on_address_update(addresses[i]))
            _ = await self.send_to_address_and_wait(addresses[i], 10)
            await notification
            notification = asyncio.ensure_future(
                self.on_address_update(addresses[9]))
            _ = await self.send_to_address_and_wait(addresses[9], 10)
            await notification

        # use batching to reduce issues with send_to_address on cli
        await self.assertBalance(self.account, '200.0')
        self.assertEqual(20, await self.account.get_utxo_count())

        # address gap should have increase by 10 to cover the first 10 addresses we've used up
        addresses = await self.account.receiving.get_addresses()
        self.assertEqual(30, len(addresses))

        # there used to be a sync bug which failed to save TXIs between
        # daemon restarts, clearing cache replicates that behavior
        self.ledger._tx_cache.clear()

        # spend from each of the first 10 addresses to the subsequent 10 addresses
        txs = []
        for address in addresses[10:20]:
            txs.append(await Transaction.create([], [
                Output.pay_pubkey_hash(coins_to_satoshis('1.0'),
                                       self.ledger.address_to_hash160(address))
            ], [self.account], self.account))
        await asyncio.wait([self.broadcast(tx) for tx in txs])
        await asyncio.wait([self.ledger.wait(tx) for tx in txs])

        # verify that a previous bug which failed to save TXIs doesn't come back
        # this check must happen before generating a new block
        self.assertTrue(
            all([
                tx.inputs[0].txo_ref.txo is not None
                for tx in await self.ledger.db.get_transactions(
                    txid__in=[tx.id for tx in txs])
            ]))

        await self.generate(1)
        await asyncio.wait([self.ledger.wait(tx) for tx in txs])
        await self.assertBalance(self.account, '199.99876')

        # 10 of the UTXOs have been split into a 1 coin UTXO and a 9 UTXO change
        self.assertEqual(30, await self.account.get_utxo_count())

        # spend all 30 UTXOs into a a 199 coin UTXO and change
        tx = await Transaction.create([], [
            Output.pay_pubkey_hash(
                coins_to_satoshis('199.0'),
                self.ledger.address_to_hash160(addresses[-1]))
        ], [self.account], self.account)
        await self.broadcast(tx)
        await self.ledger.wait(tx)
        await self.generate(1)
        await self.ledger.wait(tx)

        self.assertEqual(2, await
                         self.account.get_utxo_count())  # 199 + change
        await self.assertBalance(self.account, '199.99649')
Пример #26
0
def get_claim_transaction(claim_name, claim=b''):
    return get_transaction(
        Output.pay_claim_name_pubkey_hash(CENT, claim_name, claim, NULL_HASH32)
    )
Пример #27
0
    async def test_creating_updating_and_abandoning_claim_with_channel(self):

        await self.account.ensure_address_gap()

        address1, address2 = await self.account.receiving.get_addresses(
            limit=2, only_usable=True)
        sendtxid1 = await self.blockchain.send_to_address(address1, 5)
        sendtxid2 = await self.blockchain.send_to_address(address2, 5)
        await self.blockchain.generate(1)
        await asyncio.wait([
            self.on_transaction_id(sendtxid1),
            self.on_transaction_id(sendtxid2)
        ])

        self.assertEqual(d2l(await self.account.get_balance()), '10.0')

        channel = Claim()
        channel_txo = Output.pay_claim_name_pubkey_hash(
            l2d('1.0'), '@bar', channel,
            self.account.ledger.address_to_hash160(address1))
        channel_txo.generate_channel_private_key()
        channel_txo.script.generate()
        channel_tx = await Transaction.create([], [channel_txo],
                                              [self.account], self.account)

        stream = Claim()
        stream.stream.source.media_type = "video/mp4"
        stream_txo = Output.pay_claim_name_pubkey_hash(
            l2d('1.0'), 'foo', stream,
            self.account.ledger.address_to_hash160(address1))
        stream_tx = await Transaction.create([], [stream_txo], [self.account],
                                             self.account)
        stream_txo.sign(channel_txo)
        await stream_tx.sign([self.account])

        await self.broadcast(channel_tx)
        await self.broadcast(stream_tx)
        await asyncio.wait([  # mempool
            self.ledger.wait(channel_tx),
            self.ledger.wait(stream_tx)
        ])
        await self.blockchain.generate(1)
        await asyncio.wait([  # confirmed
            self.ledger.wait(channel_tx),
            self.ledger.wait(stream_tx)
        ])

        self.assertEqual(d2l(await self.account.get_balance()), '7.985786')
        self.assertEqual(
            d2l(await self.account.get_balance(include_claims=True)),
            '9.985786')

        response = await self.ledger.resolve([], ['lbry://@bar/foo'])
        self.assertEqual(response['lbry://@bar/foo'].claim.claim_type,
                         'stream')

        abandon_tx = await Transaction.create(
            [Input.spend(stream_tx.outputs[0])], [], [self.account],
            self.account)
        await self.broadcast(abandon_tx)
        await self.ledger.wait(abandon_tx)
        await self.blockchain.generate(1)
        await self.ledger.wait(abandon_tx)

        response = await self.ledger.resolve([], ['lbry://@bar/foo'])
        self.assertIn('error', response['lbry://@bar/foo'])

        # checks for expected format in inexistent URIs
        response = await self.ledger.resolve([], ['lbry://404', 'lbry://@404'])
        self.assertEqual('lbry://404 did not resolve to a claim',
                         response['lbry://404']['error'])
        self.assertEqual('lbry://@404 did not resolve to a claim',
                         response['lbry://@404']['error'])
Пример #28
0
 def get_abandon(self, tx):
     claim = Transaction(tx[0].raw).outputs[0]
     return self._make_tx(Output.pay_pubkey_hash(claim.amount, b'abc'),
                          Input.spend(claim))
Пример #29
0
    def validate_channel_signatures(self, height, new_claims, updated_claims, spent_claims, affected_channels, timer):
        if not new_claims and not updated_claims and not spent_claims:
            return

        sub_timer = timer.add_timer('segregate channels and signables')
        sub_timer.start()
        channels, new_channel_keys, signables = {}, {}, {}
        for txo in chain(new_claims, updated_claims):
            try:
                claim = txo.claim
            except:
                continue
            if claim.is_channel:
                channels[txo.claim_hash] = txo
                new_channel_keys[txo.claim_hash] = claim.channel.public_key_bytes
            else:
                signables[txo.claim_hash] = txo
        sub_timer.stop()

        sub_timer = timer.add_timer('make list of channels we need to lookup')
        sub_timer.start()
        missing_channel_keys = set()
        for txo in signables.values():
            claim = txo.claim
            if claim.is_signed and claim.signing_channel_hash not in new_channel_keys:
                missing_channel_keys.add(claim.signing_channel_hash)
        sub_timer.stop()

        sub_timer = timer.add_timer('lookup missing channels')
        sub_timer.start()
        all_channel_keys = {}
        if new_channel_keys or missing_channel_keys or affected_channels:
            all_channel_keys = dict(self.execute(*query(
                "SELECT claim_hash, public_key_bytes FROM claim",
                claim_hash__in=set(new_channel_keys) | missing_channel_keys | affected_channels
            )))
        sub_timer.stop()

        sub_timer = timer.add_timer('prepare for updating claims')
        sub_timer.start()
        changed_channel_keys = {}
        for claim_hash, new_key in new_channel_keys.items():
            if claim_hash not in all_channel_keys or all_channel_keys[claim_hash] != new_key:
                all_channel_keys[claim_hash] = new_key
                changed_channel_keys[claim_hash] = new_key

        claim_updates = []

        for claim_hash, txo in signables.items():
            claim = txo.claim
            update = {
                'claim_hash': claim_hash,
                'channel_hash': None,
                'signature': None,
                'signature_digest': None,
                'signature_valid': None
            }
            if claim.is_signed:
                update.update({
                    'channel_hash': claim.signing_channel_hash,
                    'signature': txo.get_encoded_signature(),
                    'signature_digest': txo.get_signature_digest(self.ledger),
                    'signature_valid': 0
                })
            claim_updates.append(update)
        sub_timer.stop()

        sub_timer = timer.add_timer('find claims affected by a change in channel key')
        sub_timer.start()
        if changed_channel_keys:
            sql = f"""
            SELECT * FROM claim WHERE
                channel_hash IN ({','.join('?' for _ in changed_channel_keys)}) AND
                signature IS NOT NULL
            """
            for affected_claim in self.execute(sql, changed_channel_keys.keys()):
                if affected_claim.claim_hash not in signables:
                    claim_updates.append({
                        'claim_hash': affected_claim.claim_hash,
                        'channel_hash': affected_claim.channel_hash,
                        'signature': affected_claim.signature,
                        'signature_digest': affected_claim.signature_digest,
                        'signature_valid': 0
                    })
        sub_timer.stop()

        sub_timer = timer.add_timer('verify signatures')
        sub_timer.start()
        for update in claim_updates:
            channel_pub_key = all_channel_keys.get(update['channel_hash'])
            if channel_pub_key and update['signature']:
                update['signature_valid'] = Output.is_signature_valid(
                    bytes(update['signature']), bytes(update['signature_digest']), channel_pub_key
                )
        sub_timer.stop()

        sub_timer = timer.add_timer('update claims')
        sub_timer.start()
        if claim_updates:
            self.executemany(f"""
                UPDATE claim SET 
                    channel_hash=:channel_hash, signature=:signature, signature_digest=:signature_digest,
                    signature_valid=:signature_valid,
                    channel_join=CASE
                        WHEN signature_valid=1 AND :signature_valid=1 AND channel_hash=:channel_hash THEN channel_join
                        WHEN :signature_valid=1 THEN {height}
                    END,
                    canonical_url=CASE
                        WHEN signature_valid=1 AND :signature_valid=1 AND channel_hash=:channel_hash THEN canonical_url
                        WHEN :signature_valid=1 THEN
                            (SELECT short_url FROM claim WHERE claim_hash=:channel_hash)||'/'||
                            claim_name||COALESCE(
                                (SELECT shortest_id(other_claim.claim_id, claim.claim_id) FROM claim AS other_claim
                                 WHERE other_claim.signature_valid = 1 AND
                                       other_claim.channel_hash = :channel_hash AND
                                       other_claim.normalized = claim.normalized),
                                '#'||substr(claim_id, 1, 1)
                            )
                    END
                WHERE claim_hash=:claim_hash;
                """, claim_updates)
        sub_timer.stop()

        sub_timer = timer.add_timer('update claims affected by spent channels')
        sub_timer.start()
        if spent_claims:
            self.execute(
                f"""
                UPDATE claim SET
                    signature_valid=CASE WHEN signature IS NOT NULL THEN 0 END,
                    channel_join=NULL, canonical_url=NULL
                WHERE channel_hash IN ({','.join('?' for _ in spent_claims)})
                """, spent_claims
            )
        sub_timer.stop()

        sub_timer = timer.add_timer('update channels')
        sub_timer.start()
        if channels:
            self.executemany(
                """
                UPDATE claim SET
                    public_key_bytes=:public_key_bytes,
                    public_key_hash=:public_key_hash
                WHERE claim_hash=:claim_hash""", [{
                    'claim_hash': claim_hash,
                    'public_key_bytes': txo.claim.channel.public_key_bytes,
                    'public_key_hash': self.ledger.address_to_hash160(
                            self.ledger.public_key_to_address(txo.claim.channel.public_key_bytes)
                    )
                } for claim_hash, txo in channels.items()]
            )
        sub_timer.stop()

        sub_timer = timer.add_timer('update claims_in_channel counts')
        sub_timer.start()
        if all_channel_keys:
            self.executemany(f"""
                UPDATE claim SET
                    claims_in_channel=(
                        SELECT COUNT(*) FROM claim AS claim_in_channel
                        WHERE claim_in_channel.signature_valid=1 AND
                              claim_in_channel.channel_hash=claim.claim_hash
                    )
                WHERE claim_hash = ?
            """, [(channel_hash,) for channel_hash in all_channel_keys.keys()])
        sub_timer.stop()

        sub_timer = timer.add_timer('update blocked claims list')
        sub_timer.start()
        if (self.blocking_channel_hashes.intersection(all_channel_keys) or
                self.filtering_channel_hashes.intersection(all_channel_keys)):
            self.update_blocked_and_filtered_claims()
        sub_timer.stop()
Пример #30
0
    async def test_creating_updating_and_abandoning_claim_with_channel(self):

        await self.account.ensure_address_gap()

        address1, address2 = await self.account.receiving.get_addresses(limit=2, only_usable=True)
        notifications = asyncio.create_task(asyncio.wait(
            [asyncio.ensure_future(self.on_address_update(address1)),
             asyncio.ensure_future(self.on_address_update(address2))]
        ))
        await self.send_to_address_and_wait(address1, 5)
        await self.send_to_address_and_wait(address2, 5, 1)
        await notifications

        self.assertEqual(d2l(await self.account.get_balance()), '10.0')

        channel = Claim()
        channel_txo = Output.pay_claim_name_pubkey_hash(
            l2d('1.0'), '@bar', channel, self.account.ledger.address_to_hash160(address1)
        )
        channel_txo.set_channel_private_key(
            await self.account.generate_channel_private_key()
        )
        channel_txo.script.generate()
        channel_tx = await Transaction.create([], [channel_txo], [self.account], self.account)

        stream = Claim()
        stream.stream.source.media_type = "video/mp4"
        stream_txo = Output.pay_claim_name_pubkey_hash(
            l2d('1.0'), 'foo', stream, self.account.ledger.address_to_hash160(address1)
        )
        stream_tx = await Transaction.create([], [stream_txo], [self.account], self.account)
        stream_txo.sign(channel_txo)
        await stream_tx.sign([self.account])

        notifications = asyncio.create_task(asyncio.wait(
            [asyncio.ensure_future(self.ledger.wait(channel_tx)), asyncio.ensure_future(self.ledger.wait(stream_tx))]
        ))

        await self.broadcast(channel_tx)
        await self.broadcast(stream_tx)
        await notifications
        notifications = asyncio.create_task(asyncio.wait(
            [asyncio.ensure_future(self.ledger.wait(channel_tx)), asyncio.ensure_future(self.ledger.wait(stream_tx))]
        ))
        await self.generate(1)
        await notifications
        self.assertEqual(d2l(await self.account.get_balance()), '7.985786')
        self.assertEqual(d2l(await self.account.get_balance(include_claims=True)), '9.985786')

        response = await self.ledger.resolve([], ['lbry://@bar/foo'])
        self.assertEqual(response['lbry://@bar/foo'].claim.claim_type, 'stream')

        abandon_tx = await Transaction.create([Input.spend(stream_tx.outputs[0])], [], [self.account], self.account)
        notify = asyncio.create_task(self.ledger.wait(abandon_tx))
        await self.broadcast(abandon_tx)
        await notify
        notify = asyncio.create_task(self.ledger.wait(abandon_tx))
        await self.generate(1)
        await notify

        response = await self.ledger.resolve([], ['lbry://@bar/foo'])
        self.assertIn('error', response['lbry://@bar/foo'])

        # checks for expected format in inexistent URIs
        response = await self.ledger.resolve([], ['lbry://404', 'lbry://@404', 'lbry://@404/404'])
        self.assertEqual('Could not find claim at "lbry://404".', response['lbry://404']['error']['text'])
        self.assertEqual('Could not find channel in "lbry://@404".', response['lbry://@404']['error']['text'])
        self.assertEqual('Could not find channel in "lbry://@404/404".', response['lbry://@404/404']['error']['text'])