diff --git a/lbry/extras/daemon/daemon.py b/lbry/extras/daemon/daemon.py index ea7a4da6f..47c62afde 100644 --- a/lbry/extras/daemon/daemon.py +++ b/lbry/extras/daemon/daemon.py @@ -28,6 +28,7 @@ from lbry.wallet import ( from lbry.wallet.dewies import dewies_to_lbc, lbc_to_dewies, dict_values_to_lbc from lbry.wallet.constants import TXO_TYPES, CLAIM_TYPE_NAMES from lbry.wallet.bip32 import PrivateKey +from lbry.crypto.base58 import Base58 from lbry import utils from lbry.conf import Config, Setting, NOT_SET @@ -1872,6 +1873,48 @@ class Daemon(metaclass=JSONRPCServerType): outputs=outputs, broadcast=broadcast ) + @requires("wallet") + async def jsonrpc_account_deposit( + self, txid, nout, redeem_script, private_key, + to_account=None, wallet_id=None, preview=False, blocking=False + ): + """ + Spend a time locked transaction into your account. + + Usage: + account_deposit + [ | --to_account=] + [--wallet_id=] [--preview] [--blocking] + + Options: + --txid= : (str) id of the transaction + --nout= : (int) output number in the transaction + --redeem_script= : (str) redeem script for output + --private_key= : (str) private key to sign transaction + --to_account= : (str) deposit to this account + --wallet_id= : (str) limit operation to specific wallet. + --preview : (bool) do not broadcast the transaction + --blocking : (bool) wait until tx has synced + + Returns: {Transaction} + """ + wallet = self.wallet_manager.get_wallet_or_default(wallet_id) + account = wallet.get_account_or_default(to_account) + other_tx = await self.wallet_manager.get_transaction(txid) + tx = await Transaction.spend_time_lock( + other_tx.outputs[nout], unhexlify(redeem_script), account + ) + pk = PrivateKey.from_bytes( + account.ledger, Base58.decode_check(private_key)[1:-1] + ) + tx.sign([account], {pk.address: pk}) + if not preview: + await self.broadcast_or_release(tx, blocking) + self.component_manager.loop.create_task(self.analytics_manager.send_credits_sent()) + else: + await self.ledger.release_tx(tx) + return tx + @requires(WALLET_COMPONENT) def jsonrpc_account_send(self, amount, addresses, account_id=None, wallet_id=None, preview=False, blocking=False): """ diff --git a/lbry/wallet/bip32.py b/lbry/wallet/bip32.py index f9d96164e..1c2181b02 100644 --- a/lbry/wallet/bip32.py +++ b/lbry/wallet/bip32.py @@ -215,6 +215,10 @@ class PrivateKey(_KeyBase): private_key = cPrivateKey.from_int(key_int) return cls(ledger, private_key, bytes((0,)*32), 0, 0) + @classmethod + def from_bytes(cls, ledger, key_bytes) -> 'PrivateKey': + return cls(ledger, cPrivateKey(key_bytes), bytes((0,)*32), 0, 0) + @cachedproperty def private_key_bytes(self): """ Return the serialized private key (no leading zero byte). """ diff --git a/lbry/wallet/transaction.py b/lbry/wallet/transaction.py index 59f31aec1..f504c3eac 100644 --- a/lbry/wallet/transaction.py +++ b/lbry/wallet/transaction.py @@ -860,7 +860,7 @@ class Transaction: def signature_hash_type(hash_type): return hash_type - async def sign(self, funding_accounts: Iterable['Account']): + async def sign(self, funding_accounts: Iterable['Account'], extra_keys: dict = None): self._reset() ledger, wallet = self.ensure_all_have_same_ledger_and_wallet(funding_accounts) for i, txi in enumerate(self._inputs): @@ -870,6 +870,8 @@ class Transaction: if txo_script.is_pay_pubkey_hash: address = ledger.hash160_to_address(txo_script.values['pubkey_hash']) private_key = await ledger.get_private_key_for_address(wallet, address) + if private_key is None and extra_keys: + private_key = extra_keys.get(address) assert private_key is not None, 'Cannot find private key for signing output.' tx = self._serialize_for_signature(i) txi.script.values['signature'] = \ @@ -944,14 +946,9 @@ class Transaction: return cls.create([], [payment, data], funding_accounts, change_account) @classmethod - def spend_time_lock( - cls, time_locked_txo: Output, amount: int, holding_address: str, script_source: str, - funding_accounts: List['Account'], change_account: 'Account' - ): - ledger, _ = cls.ensure_all_have_same_ledger_and_wallet(funding_accounts, change_account) - txi = Input.spend_time_lock(time_locked_txo, unhexlify(script_source)) - txo = Output.pay_pubkey_hash(amount, ledger.address_to_hash160(holding_address)) - return cls.create([txi], [txo], funding_accounts, change_account) + def spend_time_lock(cls, time_locked_txo: Output, script: bytes, account: 'Account'): + txi = Input.spend_time_lock(time_locked_txo, script) + return cls.create([txi], [], [account], account, sign=False) @property def my_inputs(self): diff --git a/tests/integration/blockchain/test_account_commands.py b/tests/integration/blockchain/test_account_commands.py index 782384367..ec5295118 100644 --- a/tests/integration/blockchain/test_account_commands.py +++ b/tests/integration/blockchain/test_account_commands.py @@ -1,9 +1,9 @@ from binascii import unhexlify from lbry.testcase import CommandTestCase -from lbry.wallet.dewies import dewies_to_lbc, lbc_to_dewies +from lbry.wallet.dewies import dewies_to_lbc from lbry.wallet.account import DeterministicChannelKeyManager -from lbry.wallet.transaction import Transaction, Input, Output +from lbry.crypto.base58 import Base58 def extract(d, keys): @@ -291,16 +291,17 @@ class AccountManagement(CommandTestCase): self.assertTrue(channel3c.has_private_key) async def test_time_locked_transactions(self): - from lbry.wallet.script import InputScript address = await self.account.receiving.get_or_create_usable_address() + private_key = await self.ledger.get_private_key_for_address(self.wallet, address) redeem = await self.blockchain.add_time_locked_address(210, address) + await self.assertBalance(self.account, '10.0') tx = await self.daemon.jsonrpc_account_send('4.0', redeem['address']) await self.confirm_tx(tx.id) - await self.blockchain.generate(10) - txi = Input.spend(tx.outputs[0]) - txi.script.source = unhexlify(redeem['redeemScript']) - txo = Output.pay_pubkey_hash(lbc_to_dewies('3.9'), self.ledger.address_to_hash160(address)) - new_tx = await Transaction.create([txi], [txo], self.wallet.accounts, self.wallet.default_account) - src = new_tx.raw - print(new_tx.raw) - + await self.generate(510) + await self.assertBalance(self.account, '5.999877') + tx = await self.daemon.jsonrpc_account_deposit( + tx.id, 0, redeem['redeemScript'], + Base58.encode_check(self.ledger.private_key_to_wif(private_key.private_key_bytes)) + ) + await self.confirm_tx(tx.id) + await self.assertBalance(self.account, '9.999877')