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_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
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)
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)
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)
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)
def get_transaction(txo=None): return Transaction() \ .add_inputs([get_input()]) \ .add_outputs([txo or Output.pay_pubkey_hash(CENT, NULL_HASH32)])
def get_output(amount=CENT, pubkey_hash=NULL_HASH32): return Transaction() \ .add_outputs([Output.pay_pubkey_hash(amount, pubkey_hash)]) \ .outputs[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')
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')
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))