def test_coinbase_transaction(self): raw = unhexlify( "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff200" "34d520504f89ac55a086032d217bf0700000d2f6e6f64655374726174756d2f0000000001a03489850800" "00001976a914cfab870d6deea54ca94a41912a75484649e52f2088ac00000000") tx = Transaction(raw) self.assertEqual(tx.version, 1) self.assertEqual(tx.locktime, 0) self.assertEqual(len(tx.inputs), 1) self.assertEqual(len(tx.outputs), 1) coinbase = tx.inputs[0] self.assertTrue(coinbase.txo_ref.is_null) self.assertEqual(coinbase.txo_ref.position, 0xFFFFFFFF) self.assertEqual(coinbase.sequence, 0) self.assertIsNotNone(coinbase.coinbase) self.assertIsNone(coinbase.script) self.assertEqual( hexlify(coinbase.coinbase), b'034d520504f89ac55a086032d217bf0700000d2f6e6f64655374726174756d2f' ) out = tx.outputs[0] self.assertEqual(out.amount, 36600100000) self.assertEqual(out.position, 0) self.assertTrue(out.script.is_pay_pubkey_hash) self.assertFalse(out.script.is_pay_script_hash) self.assertFalse(out.script.is_claim_involved) tx._reset() self.assertEqual(tx.raw, raw)
def test_genesis_transaction(self): raw = unhexlify( "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1f0" "4ffff001d010417696e736572742074696d657374616d7020737472696e67ffffffff01000004bfc91b8e" "001976a914345991dbf57bfb014b87006acdfafbfc5fe8292f88ac00000000") tx = Transaction(raw) self.assertEqual(tx.version, 1) self.assertEqual(tx.locktime, 0) self.assertEqual(len(tx.inputs), 1) self.assertEqual(len(tx.outputs), 1) coinbase = tx.inputs[0] self.assertTrue(coinbase.txo_ref.is_null) self.assertEqual(coinbase.txo_ref.position, 0xFFFFFFFF) self.assertEqual(coinbase.sequence, 0xFFFFFFFF) self.assertIsNotNone(coinbase.coinbase) self.assertIsNone(coinbase.script) self.assertEqual( hexlify(coinbase.coinbase), b'04ffff001d010417696e736572742074696d657374616d7020737472696e67') out = tx.outputs[0] self.assertEqual(out.amount, 40000000000000000) self.assertEqual(out.position, 0) self.assertTrue(out.script.is_pay_pubkey_hash) self.assertFalse(out.script.is_pay_script_hash) self.assertFalse(out.script.is_claim_involved) tx._reset() self.assertEqual(tx.raw, raw)
async def test_creating_updating_and_abandoning_claim_with_channel(self): await d2f(self.account.ensure_address_gap()) address1, address2 = await d2f( self.account.receiving.get_addresses(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( round(await d2f(self.account.get_balance(0)) / COIN, 1), 10.0) cert, key = generate_certificate() cert_tx = await d2f( Transaction.claim('@bar', cert, 1 * COIN, address1, [self.account], self.account)) claim = ClaimDict.load_dict(example_claim_dict) claim = claim.sign(key, address1, cert_tx.outputs[0].claim_id) claim_tx = await d2f( Transaction.claim('foo', claim, 1 * COIN, address1, [self.account], self.account)) await self.broadcast(cert_tx) await self.broadcast(claim_tx) await asyncio.wait([ # mempool self.on_transaction_id(claim_tx.id), self.on_transaction_id(cert_tx.id), ]) await self.blockchain.generate(1) await asyncio.wait([ # confirmed self.on_transaction_id(claim_tx.id), self.on_transaction_id(cert_tx.id), ]) self.assertEqual( round(await d2f(self.account.get_balance(0)) / COIN, 1), 8.0) self.assertEqual( round(await d2f(self.account.get_balance(0, True)) / COIN, 1), 10.0) response = await d2f(self.ledger.resolve(0, 10, 'lbry://@bar/foo')) self.assertIn('lbry://@bar/foo', response) self.assertIn('claim', response['lbry://@bar/foo']) abandon_tx = await d2f( Transaction.abandon([claim_tx.outputs[0]], [self.account], self.account)) await self.broadcast(abandon_tx) await self.on_transaction(abandon_tx) await self.blockchain.generate(1) await self.on_transaction(abandon_tx) response = await d2f(self.ledger.resolve(0, 10, 'lbry://@bar/foo')) self.assertNotIn('claim', response['lbry://@bar/foo'])
async def test_creating_updating_and_abandoning_claim_with_channel(self): await d2f(self.account.ensure_address_gap()) address1, address2 = await d2f(self.account.receiving.get_addresses(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 d2f(self.account.get_balance(0))), '10.0') cert, key = generate_certificate() cert_tx = await d2f(Transaction.claim('@bar', cert, l2d('1.0'), address1, [self.account], self.account)) claim = ClaimDict.load_dict(example_claim_dict) claim = claim.sign(key, address1, cert_tx.outputs[0].claim_id) claim_tx = await d2f(Transaction.claim('foo', claim, l2d('1.0'), address1, [self.account], self.account)) await self.broadcast(cert_tx) await self.broadcast(claim_tx) await asyncio.wait([ # mempool self.on_transaction_id(claim_tx.id), self.on_transaction_id(cert_tx.id), ]) await self.blockchain.generate(1) await asyncio.wait([ # confirmed self.on_transaction_id(claim_tx.id), self.on_transaction_id(cert_tx.id), ]) self.assertEqual(d2l(await d2f(self.account.get_balance(0))), '7.985786') self.assertEqual(d2l(await d2f(self.account.get_balance(0, include_claims=True))), '9.985786') response = await d2f(self.ledger.resolve(0, 10, 'lbry://@bar/foo')) self.assertIn('lbry://@bar/foo', response) self.assertIn('claim', response['lbry://@bar/foo']) abandon_tx = await d2f(Transaction.abandon([claim_tx.outputs[0]], [self.account], self.account)) await self.broadcast(abandon_tx) await self.on_transaction(abandon_tx) await self.blockchain.generate(1) await self.on_transaction(abandon_tx) response = await d2f(self.ledger.resolve(0, 10, 'lbry://@bar/foo')) self.assertNotIn('claim', response['lbry://@bar/foo']) # checks for expected format in inexistent URIs response = await d2f(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'])
def test_signed_claim_made_by_ytsync(self): stream_tx = Transaction( unhexlify( b'0100000001eb2a756e15bde95db3d2ae4a6e9b2796a699087890644607b5b04a5f15b67062010000006a4' b'7304402206444b920bd318a07d9b982e30eb66245fdaaa6c9866e1f6e5900161d9b0ffd70022036464714' b'4f1830898a2042aa0d6cef95a243799cc6e36630a58d411e2f9111f00121029b15f9a00a7c3f21b10bd4b' b'98ab23a9e895bd9160e21f71317862bf55fbbc89effffffff0240420f0000000000fd1503b52268657265' b'2d6172652d352d726561736f6e732d692d6e657874636c6f75642d746c674dd302080110011aee0408011' b'2a604080410011a2b4865726520617265203520526561736f6e73204920e29da4efb88f204e657874636c' b'6f7564207c20544c4722920346696e64206f7574206d6f72652061626f7574204e657874636c6f75643a2' b'068747470733a2f2f6e657874636c6f75642e636f6d2f0a0a596f752063616e2066696e64206d65206f6e' b'20746865736520736f6369616c733a0a202a20466f72756d733a2068747470733a2f2f666f72756d2e686' b'5617679656c656d656e742e696f2f0a202a20506f64636173743a2068747470733a2f2f6f6666746f7069' b'63616c2e6e65740a202a2050617472656f6e3a2068747470733a2f2f70617472656f6e2e636f6d2f74686' b'56c696e757867616d65720a202a204d657263683a2068747470733a2f2f746565737072696e672e636f6d' b'2f73746f7265732f6f6666696369616c2d6c696e75782d67616d65720a202a205477697463683a2068747' b'470733a2f2f7477697463682e74762f786f6e64616b0a202a20547769747465723a2068747470733a2f2f' b'747769747465722e636f6d2f7468656c696e757867616d65720a0a2e2e2e0a68747470733a2f2f7777772' b'e796f75747562652e636f6d2f77617463683f763d4672546442434f535f66632a0f546865204c696e7578' b'2047616d6572321c436f7079726967687465642028636f6e7461637420617574686f722938004a2968747' b'470733a2f2f6265726b2e6e696e6a612f7468756d626e61696c732f4672546442434f535f666352005a00' b'1a41080110011a30040e8ac6e89c061f982528c23ad33829fd7146435bf7a4cc22f0bff70c4fe0b91fd36' b'da9a375e3e1c171db825bf5d1f32209766964656f2f6d70342a5c080110031a4062b2dd4c45e364030fbf' b'ad1a6fefff695ebf20ea33a5381b947753e2a0ca359989a5cc7d15e5392a0d354c0b68498382b2701b22c' b'03beb8dcb91089031b871e72214feb61536c007cdf4faeeaab4876cb397feaf6b516d7576a914f4f43f6f' b'7a472bbf27fa3630329f771135fc445788ac86ff0600000000001976a914cef0fe3eeaf04416f0c3ff3e7' b'8a598a081e70ee788ac00000000')) stream = stream_tx.outputs[0] channel_tx = Transaction( unhexlify( b'010000000192a1e1e3f66b8ca05a021cfa5fb6645ebc066b46639ccc9b3781fa588a88da65010000006a4' b'7304402206be09a355f6abea8a10b5512180cd258460b42d516b5149431ffa3230a02533a0220325e83c6' b'176b295d633b18aad67adb4ad766d13152536ac04583f86d14645c9901210269c63bc8bac8143ef02f972' b'4a4ab35b12bdfa65ee1ad8c0db3d6511407a4cc2effffffff0240420f000000000091b50e405468654c69' b'6e757847616d65724c6408011002225e0801100322583056301006072a8648ce3d020106052b8104000a0' b'34200043878b1edd4a1373149909ef03f4339f6da9c2bd2214c040fd2e530463ffe66098eca14fc70b50f' b'f3aefd106049a815f595ed5a13eda7419ad78d9ed7ae473f176d7576a914994dad5f21c384ff526749b87' b'6d9d017d257b69888ac00dd6d00000000001976a914979202508a44f0e8290cea80787c76f98728845388' b'ac00000000')) channel = channel_tx.outputs[0] ledger = MainNetLedger({ 'db': MainNetLedger.database_class(':memory:'), 'headers': MainNetLedger.headers_class(':memory:') }) self.assertTrue(stream.is_signed_by(channel, ledger))
def validate_claim_signature_and_get_channel_name(claim_result, certificate_claim, ledger, claim_tx=None, cert_tx=None): if cert_tx and certificate_claim and claim_tx and claim_result: tx = Transaction(unhexlify(claim_tx)) cert_tx = Transaction(unhexlify(cert_tx)) try: is_signed = tx.outputs[claim_result['nout']].is_signed_by( cert_tx.outputs[certificate_claim['nout']], ledger) except InvalidSignature: return False, None return is_signed, certificate_claim['name'] return False, None
def advance_claim_txs(self, txs, height): # TODO: generate claim undo info! undo_info = [] add_undo = undo_info.append update_inputs = set() for etx, txid in txs: update_inputs.clear() tx = Transaction(etx.serialize()) for index, output in enumerate(tx.outputs): if not output.is_claim: continue if output.script.is_claim_name: add_undo(self.advance_claim_name_transaction(output, height, txid, index)) elif output.script.is_update_claim: update_input = self.db.get_update_input(output.claim_hash, tx.inputs) if update_input: update_inputs.add(update_input) add_undo(self.advance_update_claim(output, height, txid, index)) else: info = (hash_to_hex_str(txid), output.claim_id,) self.logger.error("REJECTED: {} updating {}".format(*info)) for txin in tx.inputs: if txin not in update_inputs: abandoned_claim_id = self.db.abandon_spent(txin.txo_ref.tx_ref.hash, txin.txo_ref.position) if abandoned_claim_id: add_undo((abandoned_claim_id, self.db.get_claim_info(abandoned_claim_id))) return undo_info
def validate_claim_signature_and_get_channel_name(claim_result, certificate_claim, ledger, claim_tx=None, cert_tx=None): valid_signature = False if cert_tx and certificate_claim and claim_tx and claim_result: tx = Transaction(unhexlify(claim_tx)) cert_tx = Transaction(unhexlify(cert_tx)) try: valid_signature = tx.outputs[claim_result['nout']].is_signed_by( cert_tx.outputs[certificate_claim['nout']], ledger) except InvalidSignature: pass if not valid_signature: log.warning("lbry://%s#%s has an invalid signature", claim_result['name'], claim_result['claim_id']) return valid_signature
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)
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)
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)
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" }) yield account.ensure_address_gap() address1, address2 = yield 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)]) yield tx.sign([account]) self.assertEqual( hexlify(tx.inputs[0].script.values['signature']), b'304402200dafa26ad7cf38c5a971c8a25ce7d85a076235f146126762296b1223c42ae21e022020ef9eeb8' b'398327891008c5c0be4357683f12cb22346691ff23914f457bf679601')
def get_all_lbry_files(transaction: sqlite3.Connection) -> typing.List[typing.Dict]: files = [] signed_claims = {} for (rowid, stream_hash, file_name, download_dir, data_rate, status, saved_file, raw_content_fee, _, sd_hash, stream_key, stream_name, suggested_file_name, *claim_args) in _batched_select( transaction, "select file.rowid, file.*, stream.*, c.* " "from file inner join stream on file.stream_hash=stream.stream_hash " "inner join content_claim cc on file.stream_hash=cc.stream_hash " "inner join claim c on cc.claim_outpoint=c.claim_outpoint " "where file.stream_hash in {} " "order by c.rowid desc", [ stream_hash for (stream_hash,) in transaction.execute("select stream_hash from file")]): claim = StoredStreamClaim(stream_hash, *claim_args) if claim.channel_claim_id: if claim.channel_claim_id not in signed_claims: signed_claims[claim.channel_claim_id] = [] signed_claims[claim.channel_claim_id].append(claim) files.append( { "rowid": rowid, "stream_hash": stream_hash, "file_name": file_name, # hex "download_directory": download_dir, # hex "blob_data_rate": data_rate, "status": status, "sd_hash": sd_hash, "key": stream_key, "stream_name": stream_name, # hex "suggested_file_name": suggested_file_name, # hex "claim": claim, "saved_file": bool(saved_file), "content_fee": None if not raw_content_fee else Transaction( binascii.unhexlify(raw_content_fee) ) } ) for claim_name, claim_id in _batched_select( transaction, "select c.claim_name, c.claim_id from claim c where c.claim_id in {}", list(signed_claims.keys())): for claim in signed_claims[claim_id]: claim.channel_name = claim_name return files
def test_claim_transaction(self): raw = unhexlify( "01000000012433e1b327603843b083344dbae5306ff7927f87ebbc5ae9eb50856c5b53fd1d000000006a4" "7304402201a91e1023d11c383a11e26bf8f9034087b15d8ada78fa565e0610455ffc8505e0220038a63a6" "ecb399723d4f1f78a20ddec0a78bf8fb6c75e63e166ef780f3944fbf0121021810150a2e4b088ec51b20c" "be1b335962b634545860733367824d5dc3eda767dffffffff028096980000000000fdff00b50463617473" "4cdc080110011a7808011230080410011a084d616361726f6e6922002a003214416c6c207269676874732" "072657365727665642e38004a0052005a001a42080110011a30add80aaf02559ba09853636a0658c42b72" "7cb5bb4ba8acedb4b7fe656065a47a31878dbf9912135ddb9e13806cc1479d220a696d6167652f6a70656" "72a5c080110031a404180cc0fa4d3839ee29cca866baed25fafb43fca1eb3b608ee889d351d3573d042c7" "b83e2e643db0d8e062a04e6e9ae6b90540a2f95fe28638d0f18af4361a1c2214f73de93f4299fb32c32f9" "49e02198a8e91101abd6d7576a914be16e4b0f9bd8f6d47d02b3a887049c36d3b84cb88ac0cd2520b0000" "00001976a914f521178feb733a719964e1da4a9efb09dcc39cfa88ac00000000") tx = Transaction(raw) self.assertEqual( tx.id, '666c3d15de1d6949a4fe717126c368e274b36957dce29fd401138c1e87e92a62') self.assertEqual(tx.version, 1) self.assertEqual(tx.locktime, 0) self.assertEqual(len(tx.inputs), 1) self.assertEqual(len(tx.outputs), 2) txin = tx.inputs[0] self.assertEqual( txin.txo_ref.id, '1dfd535b6c8550ebe95abceb877f92f76f30e5ba4d3483b043386027b3e13324:0' ) self.assertEqual(txin.txo_ref.position, 0) self.assertEqual(txin.sequence, 0xFFFFFFFF) self.assertIsNone(txin.coinbase) self.assertEqual(txin.script.template.name, 'pubkey_hash') self.assertEqual( hexlify(txin.script.values['pubkey']), b'021810150a2e4b088ec51b20cbe1b335962b634545860733367824d5dc3eda767d' ) self.assertEqual( hexlify(txin.script.values['signature']), b'304402201a91e1023d11c383a11e26bf8f9034087b15d8ada78fa565e0610455ffc8505e0220038a63a6' b'ecb399723d4f1f78a20ddec0a78bf8fb6c75e63e166ef780f3944fbf01') # Claim out0 = tx.outputs[0] self.assertEqual(out0.amount, 10000000) self.assertEqual(out0.position, 0) self.assertTrue(out0.script.is_pay_pubkey_hash) self.assertTrue(out0.script.is_claim_name) self.assertTrue(out0.script.is_claim_involved) self.assertEqual(out0.script.values['claim_name'], b'cats') self.assertEqual(hexlify(out0.script.values['pubkey_hash']), b'be16e4b0f9bd8f6d47d02b3a887049c36d3b84cb') # Change out1 = tx.outputs[1] self.assertEqual(out1.amount, 189977100) self.assertEqual(out1.position, 1) self.assertTrue(out1.script.is_pay_pubkey_hash) self.assertFalse(out1.script.is_claim_involved) self.assertEqual(hexlify(out1.script.values['pubkey_hash']), b'f521178feb733a719964e1da4a9efb09dcc39cfa') tx._reset() self.assertEqual(tx.raw, raw)
def get_tx(): return Transaction().add_inputs([get_input()])
def get_output(amount=CENT, pubkey_hash=NULL_HASH32): return Transaction() \ .add_outputs([Output.pay_pubkey_hash(amount, pubkey_hash)]) \ .outputs[0]
def get_transaction(txo=None): return Transaction() \ .add_inputs([get_input()]) \ .add_outputs([txo or Output.pay_pubkey_hash(CENT, NULL_HASH32)])
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))
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))
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'))