from binascii import hexlify, unhexlify from twisted.trial import unittest from lbrynet.wallet.constants import CENT from lbrynet.wallet.transaction import Transaction, Input, Output from lbrynet.wallet.manager import WalletManager from lbrynet.wallet import set_wallet_manager from lbrynet.wallet.bip32 import PrivateKey from lbrynet.wallet.mnemonic import Mnemonic NULL_HASH = '\x00'*32 FEE_PER_BYTE = 50 FEE_PER_CHAR = 200000 class TestSizeAndFeeEstimation(unittest.TestCase): def setUp(self): set_wallet_manager(WalletManager({ 'fee_per_byte': FEE_PER_BYTE, 'fee_per_name_char': FEE_PER_CHAR })) @staticmethod def get_output(): return Output.pay_pubkey_hash(Transaction(), 1, CENT, NULL_HASH) @classmethod def get_input(cls): return cls.get_output().spend(fake=True) @classmethod def get_transaction(cls): tx = Transaction() Output.pay_pubkey_hash(tx, 1, CENT, NULL_HASH) tx.add_inputs([cls.get_input()]) return tx @classmethod def get_claim_transaction(cls, claim_name, claim=''): tx = Transaction() Output.pay_claim_name_pubkey_hash(tx, 1, CENT, claim_name, claim, NULL_HASH) tx.add_inputs([cls.get_input()]) return tx def test_output_size_and_fee(self): txo = self.get_output() self.assertEqual(txo.size, 46) self.assertEqual(txo.fee, 46 * FEE_PER_BYTE) def test_input_size_and_fee(self): txi = self.get_input() self.assertEqual(txi.size, 148) self.assertEqual(txi.fee, 148 * FEE_PER_BYTE) def test_transaction_size_and_fee(self): tx = self.get_transaction() base_size = tx.size - 1 - tx.inputs[0].size self.assertEqual(tx.size, 204) self.assertEqual(tx.base_size, base_size) self.assertEqual(tx.base_fee, FEE_PER_BYTE * base_size) def test_claim_name_transaction_size_and_fee(self): # fee based on claim name is the larger fee claim_name = 'verylongname' tx = self.get_claim_transaction(claim_name, '0'*4000) base_size = tx.size - 1 - tx.inputs[0].size self.assertEqual(tx.size, 4225) self.assertEqual(tx.base_size, base_size) self.assertEqual(tx.base_fee, len(claim_name) * FEE_PER_CHAR) # fee based on total bytes is the larger fee claim_name = 'a' tx = self.get_claim_transaction(claim_name, '0'*4000) base_size = tx.size - 1 - tx.inputs[0].size self.assertEqual(tx.size, 4214) self.assertEqual(tx.base_size, base_size) self.assertEqual(tx.base_fee, FEE_PER_BYTE * base_size) class TestTransactionSerialization(unittest.TestCase): 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.assertEqual(coinbase.output_tx_hash, NULL_HASH) self.assertEqual(coinbase.output_index, 0xFFFFFFFF) self.assertEqual(coinbase.sequence, 0xFFFFFFFF) self.assertTrue(coinbase.is_coinbase) self.assertEqual(coinbase.script, None) self.assertEqual( hexlify(coinbase.coinbase), b'04ffff001d010417696e736572742074696d657374616d7020737472696e67' ) out = tx.outputs[0] self.assertEqual(out.amount, 40000000000000000) self.assertEqual(out.index, 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_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.assertEqual(coinbase.output_tx_hash, NULL_HASH) self.assertEqual(coinbase.output_index, 0xFFFFFFFF) self.assertEqual(coinbase.sequence, 0) self.assertTrue(coinbase.is_coinbase) self.assertEqual(coinbase.script, None) self.assertEqual( hexlify(coinbase.coinbase), b'034d520504f89ac55a086032d217bf0700000d2f6e6f64655374726174756d2f' ) out = tx.outputs[0] self.assertEqual(out.amount, 36600100000) self.assertEqual(out.index, 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_claim_transaction(self): raw = unhexlify( "01000000012433e1b327603843b083344dbae5306ff7927f87ebbc5ae9eb50856c5b53fd1d000000006a4" "7304402201a91e1023d11c383a11e26bf8f9034087b15d8ada78fa565e0610455ffc8505e0220038a63a6" "ecb399723d4f1f78a20ddec0a78bf8fb6c75e63e166ef780f3944fbf0121021810150a2e4b088ec51b20c" "be1b335962b634545860733367824d5dc3eda767dffffffff028096980000000000fdff00b50463617473" "4cdc080110011a7808011230080410011a084d616361726f6e6922002a003214416c6c207269676874732" "072657365727665642e38004a0052005a001a42080110011a30add80aaf02559ba09853636a0658c42b72" "7cb5bb4ba8acedb4b7fe656065a47a31878dbf9912135ddb9e13806cc1479d220a696d6167652f6a70656" "72a5c080110031a404180cc0fa4d3839ee29cca866baed25fafb43fca1eb3b608ee889d351d3573d042c7" "b83e2e643db0d8e062a04e6e9ae6b90540a2f95fe28638d0f18af4361a1c2214f73de93f4299fb32c32f9" "49e02198a8e91101abd6d7576a914be16e4b0f9bd8f6d47d02b3a887049c36d3b84cb88ac0cd2520b0000" "00001976a914f521178feb733a719964e1da4a9efb09dcc39cfa88ac00000000" ) tx = Transaction(raw) self.assertEqual(hexlify(tx.id), b'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] # type: Input self.assertEqual( hexlify(txin.output_tx_hash[::-1]), b'1dfd535b6c8550ebe95abceb877f92f76f30e5ba4d3483b043386027b3e13324' ) self.assertEqual(txin.output_index, 0) self.assertEqual(txin.sequence, 0xFFFFFFFF) self.assertFalse(txin.is_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] # type: Output self.assertEqual(out0.amount, 10000000) self.assertEqual(out0.index, 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] # type: Output self.assertEqual(out1.amount, 189977100) self.assertEqual(out1.index, 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) class TestTransactionSigning(unittest.TestCase): def setUp(self): self.private_key = PrivateKey.from_seed(Mnemonic.mnemonic_to_seed( 'program leader library giant team normal suspect crater pair miracle sweet until absent' )) def test_sign(self): tx = Transaction() Output.pay_pubkey_hash(Transaction(), 0, CENT, NULL_HASH).spend(fake=True) tx.add_inputs([self.get_input()]) Output.pay_pubkey_hash(tx, 0, CENT, NULL_HASH) tx = self.get_tx()