Beispiel #1
0
 def get_channel(self, title, amount, name='@foo'):
     claim = Claim()
     claim.channel.title = title
     channel = Output.pay_claim_name_pubkey_hash(amount, name, claim,
                                                 b'abc')
     channel.generate_channel_private_key()
     return self._make_tx(channel)
Beispiel #2
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)
Beispiel #3
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))
Beispiel #4
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].tx.outputs[0].sign(channel)
         result[0].tx._reset()
     return result
    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.save_transaction_io('insert', 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.save_transaction_io('insert', 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)
Beispiel #6
0
    def test_balance(self):
        address = yield self.account.receiving.get_or_create_usable_address()
        hash160 = self.ledger.address_to_hash160(address)

        tx = Transaction().add_outputs([Output.pay_pubkey_hash(100, hash160)])
        yield self.ledger.db.save_transaction_io('insert', tx, True, address,
                                                 hash160,
                                                 '{}:{}:'.format(tx.id, 1))
        balance = yield self.account.get_balance(0)
        self.assertEqual(balance, 100)

        tx = Transaction().add_outputs(
            [Output.pay_claim_name_pubkey_hash(100, 'foo', b'', hash160)])
        yield self.ledger.db.save_transaction_io('insert', tx, True, address,
                                                 hash160,
                                                 '{}:{}:'.format(tx.id, 1))
        balance = yield self.account.get_balance(0)
        self.assertEqual(balance,
                         100)  # claim names don't count towards balance
        balance = yield self.account.get_balance(0, include_claims=True)
        self.assertEqual(balance, 200)
Beispiel #7
0
    def test_get_utxo(self):
        address = yield self.account.receiving.get_or_create_usable_address()
        hash160 = self.ledger.address_to_hash160(address)

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

        utxos = yield self.account.get_unspent_outputs()
        self.assertEqual(len(utxos), 1)

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

        utxos = yield self.account.get_unspent_outputs()
        self.assertEqual(len(utxos), 0)
Beispiel #8
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')
    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(0, 10, 'lbry://@bar/foo')
        self.assertIn('lbry://@bar/foo', response)
        self.assertIn('claim', response['lbry://@bar/foo'])

        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(0, 10, 'lbry://@bar/foo')
        self.assertNotIn('claim', response['lbry://@bar/foo'])

        # checks for expected format in inexistent URIs
        response = await self.ledger.resolve(0, 10, 'lbry://404',
                                             'lbry://@404')
        self.assertEqual('URI lbry://404 cannot be resolved',
                         response['lbry://404']['error'])
        self.assertEqual('URI lbry://@404 cannot be resolved',
                         response['lbry://@404']['error'])
Beispiel #10
0
 def get_stream():
     stream_txo = Output.pay_claim_name_pubkey_hash(CENT, 'foo', Claim(), b'abc')
     get_tx().add_outputs([stream_txo])
     return stream_txo
Beispiel #11
0
 def get_channel():
     channel_txo = Output.pay_claim_name_pubkey_hash(CENT, '@foo', Claim(), b'abc')
     channel_txo.generate_channel_private_key()
     get_tx().add_outputs([channel_txo])
     return channel_txo
Beispiel #12
0
def get_output(amount=CENT, pubkey_hash=NULL_HASH32):
    return Transaction() \
        .add_outputs([Output.pay_pubkey_hash(amount, pubkey_hash)]) \
        .outputs[0]
Beispiel #13
0
    def validate_channel_signatures(self, height, new_claims, updated_claims, spent_claims):
        if not new_claims and not updated_claims and not spent_claims:
            return

        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

        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)

        all_channel_keys = {}
        if new_channel_keys or missing_channel_keys:
            all_channel_keys = dict(self.execute(*query(
                "SELECT claim_hash, public_key_bytes FROM claim",
                claim_hash__in=[
                    sqlite3.Binary(channel_hash) for channel_hash in
                    set(new_channel_keys) | missing_channel_keys
                ]
            )))

        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': sqlite3.Binary(claim_hash),
                'channel_hash': None,
                'signature': None,
                'signature_digest': None,
                'is_channel_signature_valid': False
            }
            if claim.is_signed:
                update.update({
                    'channel_hash': sqlite3.Binary(claim.signing_channel_hash),
                    'signature': sqlite3.Binary(txo.get_encoded_signature()),
                    'signature_digest': sqlite3.Binary(txo.get_signature_digest(self.ledger))
                })
            claim_updates.append(update)

        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, [sqlite3.Binary(h) for h in changed_channel_keys]):
                if affected_claim['claim_hash'] not in signables:
                    claim_updates.append({
                        'claim_hash': sqlite3.Binary(affected_claim['claim_hash']),
                        'channel_hash': sqlite3.Binary(affected_claim['channel_hash']),
                        'signature': sqlite3.Binary(affected_claim['signature']),
                        'signature_digest': sqlite3.Binary(affected_claim['signature_digest']),
                        'is_channel_signature_valid': False
                    })

        for update in claim_updates:
            channel_pub_key = all_channel_keys.get(update['channel_hash'])
            if channel_pub_key and update['signature']:
                update['is_channel_signature_valid'] = Output.is_signature_valid(
                    bytes(update['signature']), bytes(update['signature_digest']), channel_pub_key
                )

        if claim_updates:
            self.db.executemany(f"""
                UPDATE claim SET 
                    channel_hash=:channel_hash, signature=:signature, signature_digest=:signature_digest,
                    is_channel_signature_valid=:is_channel_signature_valid,
                    channel_join=CASE
                        WHEN is_channel_signature_valid AND :is_channel_signature_valid THEN channel_join
                        WHEN :is_channel_signature_valid THEN {height}
                    END,
                    canonical_url=CASE
                        WHEN is_channel_signature_valid AND :is_channel_signature_valid THEN canonical_url
                        WHEN :is_channel_signature_valid 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.normalized = claim.normalized AND
                                       other_claim.channel_hash = :channel_hash AND
                                       other_claim.is_channel_signature_valid = 1),
                                '#'||substr(claim_id, 1, 1)
                            )
                    END
                WHERE claim_hash=:claim_hash;
                """, claim_updates)

        if spent_claims:
            self.execute(
                f"""
                UPDATE claim SET is_channel_signature_valid=0, channel_join=NULL, canonical_url=NULL
                WHERE channel_hash IN ({','.join('?' for _ in spent_claims)})
                """, [sqlite3.Binary(cid) for cid in spent_claims]
            )

        if channels:
            self.db.executemany(
                "UPDATE claim SET public_key_bytes=:public_key_bytes WHERE claim_hash=:claim_hash", [{
                    'claim_hash': sqlite3.Binary(claim_hash),
                    'public_key_bytes': sqlite3.Binary(txo.claim.channel.public_key_bytes)
                } for claim_hash, txo in channels.items()]
            )

        if all_channel_keys:
            self.db.executemany(f"""
                UPDATE claim SET
                    claims_in_channel=(
                        SELECT COUNT(*) FROM claim AS claim_in_channel
                        WHERE claim_in_channel.channel_hash=claim.claim_hash AND
                              claim_in_channel.is_channel_signature_valid
                    )
                WHERE claim_hash = ?
            """, [(sqlite3.Binary(channel_hash),) for channel_hash in all_channel_keys.keys()])
Beispiel #14
0
def get_claim_transaction(claim_name, claim=b''):
    return get_transaction(
        Output.pay_claim_name_pubkey_hash(CENT, claim_name, claim,
                                          NULL_HASH32))
Beispiel #15
0
 def get_stream(self, title, amount, name='foo'):
     claim = Claim()
     claim.stream.title = title
     return self._make_tx(
         Output.pay_claim_name_pubkey_hash(amount, name, claim, b'abc'))
Beispiel #16
0
 def get_support(self, tx, amount):
     claim = Transaction(tx[0].serialize()).outputs[0]
     return self._make_tx(
         Output.pay_support_pubkey_hash(amount, claim.claim_name,
                                        claim.claim_id, b'abc'))
Beispiel #17
0
 def get_stream_abandon(self, tx):
     claim = Transaction(tx[0].serialize()).outputs[0]
     return self._make_tx(Output.pay_pubkey_hash(claim.amount, b'abc'),
                          Input.spend(claim))
Beispiel #18
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))
Beispiel #19
0
def get_transaction(txo=None):
    return Transaction() \
        .add_inputs([get_input()]) \
        .add_outputs([txo or Output.pay_pubkey_hash(CENT, NULL_HASH32)])
Beispiel #20
0
    def validate_channel_signatures(self, height, new_claims, updated_claims,
                                    spent_claims, 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:
            all_channel_keys = dict(
                self.execute(
                    *query("SELECT claim_hash, public_key_bytes FROM claim",
                           claim_hash__in=[
                               sqlite3.Binary(channel_hash)
                               for channel_hash in set(new_channel_keys)
                               | missing_channel_keys
                           ])))
        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': sqlite3.Binary(claim_hash),
                'channel_hash': None,
                'signature': None,
                'signature_digest': None,
                'signature_valid': None
            }
            if claim.is_signed:
                update.update({
                    'channel_hash':
                    sqlite3.Binary(claim.signing_channel_hash),
                    'signature':
                    sqlite3.Binary(txo.get_encoded_signature()),
                    'signature_digest':
                    sqlite3.Binary(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, [sqlite3.Binary(h) for h in changed_channel_keys]):
                if affected_claim['claim_hash'] not in signables:
                    claim_updates.append({
                        'claim_hash':
                        sqlite3.Binary(affected_claim['claim_hash']),
                        'channel_hash':
                        sqlite3.Binary(affected_claim['channel_hash']),
                        'signature':
                        sqlite3.Binary(affected_claim['signature']),
                        'signature_digest':
                        sqlite3.Binary(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.db.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 THEN channel_join
                        WHEN :signature_valid=1 THEN {height}
                    END,
                    canonical_url=CASE
                        WHEN signature_valid=1 AND :signature_valid=1 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)})
                """, [sqlite3.Binary(cid) for cid in spent_claims])
        sub_timer.stop()

        sub_timer = timer.add_timer('update channels')
        sub_timer.start()
        if channels:
            self.db.executemany(
                """
                UPDATE claim SET
                    public_key_bytes=:public_key_bytes,
                    public_key_hash=:public_key_hash
                WHERE claim_hash=:claim_hash""", [{
                    'claim_hash':
                    sqlite3.Binary(claim_hash),
                    'public_key_bytes':
                    sqlite3.Binary(txo.claim.channel.public_key_bytes),
                    'public_key_hash':
                    sqlite3.Binary(
                        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.db.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 = ?
            """, [(sqlite3.Binary(channel_hash), )
                  for channel_hash in all_channel_keys.keys()])
        sub_timer.stop()