jsonrpc_channel_new uses new wallet

This commit is contained in:
Lex Berezhny 2018-07-04 22:16:02 -04:00 committed by Jack Robison
parent cc970c499b
commit d5beaa0937
No known key found for this signature in database
GPG key ID: DF25C68FE0239BB2
5 changed files with 114 additions and 71 deletions

View file

@ -1,5 +1,3 @@
# coding=utf-8
import binascii
import logging.handlers import logging.handlers
import mimetypes import mimetypes
import os import os
@ -7,6 +5,9 @@ import requests
import urllib import urllib
import json import json
import textwrap import textwrap
import signal
import six
from binascii import hexlify, unhexlify, b2a_hex
from copy import deepcopy from copy import deepcopy
from decimal import Decimal, InvalidOperation from decimal import Decimal, InvalidOperation
from twisted.web import server from twisted.web import server
@ -14,6 +15,8 @@ from twisted.internet import defer, reactor
from twisted.internet.task import LoopingCall from twisted.internet.task import LoopingCall
from twisted.python.failure import Failure from twisted.python.failure import Failure
from torba.constants import COIN
import lbryschema import lbryschema
from lbryschema.claim import ClaimDict from lbryschema.claim import ClaimDict
from lbryschema.uri import parse_lbry_uri from lbryschema.uri import parse_lbry_uri
@ -231,6 +234,10 @@ class Daemon(AuthJSONRPCServer):
# TODO: delete this # TODO: delete this
self.streams = {} self.streams = {}
@property
def ledger(self):
return self.session.wallet.default_account.ledger
@defer.inlineCallbacks @defer.inlineCallbacks
def setup(self): def setup(self):
log.info("Starting lbrynet-daemon") log.info("Starting lbrynet-daemon")
@ -511,7 +518,7 @@ class Daemon(AuthJSONRPCServer):
@defer.inlineCallbacks @defer.inlineCallbacks
def _get_lbry_file_dict(self, lbry_file, full_status=False): def _get_lbry_file_dict(self, lbry_file, full_status=False):
key = binascii.b2a_hex(lbry_file.key) if lbry_file.key else None key = b2a_hex(lbry_file.key) if lbry_file.key else None
full_path = os.path.join(lbry_file.download_directory, lbry_file.file_name) full_path = os.path.join(lbry_file.download_directory, lbry_file.file_name)
mime_type = mimetypes.guess_type(full_path)[0] mime_type = mimetypes.guess_type(full_path)[0]
if os.path.isfile(full_path): if os.path.isfile(full_path):
@ -1532,38 +1539,21 @@ class Daemon(AuthJSONRPCServer):
'claim_id' : (str) claim ID of the resulting claim 'claim_id' : (str) claim ID of the resulting claim
} }
""" """
tx = yield self.wallet.claim_new_channel(channel_name, amount)
try: script = tx.outputs[0].script
parsed = parse_lbry_uri(channel_name) result = {
if not parsed.is_channel: "success": True,
raise Exception("Cannot make a new channel for a non channel name") "txid": tx.hex_id.decode(),
if parsed.path: "nout": 0,
raise Exception("Invalid channel uri") "tx": hexlify(tx.raw),
except (TypeError, URIParseError): "fee": str(Decimal(tx.fee) / COIN),
raise Exception("Invalid channel name") "claim_id": tx.get_claim_id(0),
if amount <= 0: "value": hexlify(script.values['claim']),
raise Exception("Invalid amount") "claim_address": self.ledger.hash160_to_address(script.values['pubkey_hash'])
}
yield self.wallet.update_balance()
if amount >= self.wallet.get_balance():
balance = yield self.wallet.get_max_usable_balance_for_claim(channel_name)
max_bid_amount = balance - MAX_UPDATE_FEE_ESTIMATE
if balance <= MAX_UPDATE_FEE_ESTIMATE:
raise InsufficientFundsError(
"Insufficient funds, please deposit additional LBC. Minimum additional LBC needed {}"
.format(MAX_UPDATE_FEE_ESTIMATE - balance))
elif amount > max_bid_amount:
raise InsufficientFundsError(
"Please wait for any pending bids to resolve or lower the bid value. "
"Currently the maximum amount you can specify for this channel is {}"
.format(max_bid_amount)
)
result = yield self.wallet.claim_new_channel(channel_name, amount)
self.analytics_manager.send_new_channel() self.analytics_manager.send_new_channel()
log.info("Claimed a new channel! Result: %s", result) log.info("Claimed a new channel! Result: %s", result)
response = yield self._render_response(result) defer.returnValue(result)
defer.returnValue(response)
@requires(WALLET_COMPONENT) @requires(WALLET_COMPONENT)
@defer.inlineCallbacks @defer.inlineCallbacks
@ -2628,7 +2618,7 @@ class Daemon(AuthJSONRPCServer):
if not utils.is_valid_blobhash(blob_hash): if not utils.is_valid_blobhash(blob_hash):
raise Exception("invalid blob hash") raise Exception("invalid blob hash")
finished_deferred = self.dht_node.iterativeFindValue(binascii.unhexlify(blob_hash)) finished_deferred = self.dht_node.iterativeFindValue(unhexlify(blob_hash))
def trap_timeout(err): def trap_timeout(err):
err.trap(defer.TimeoutError) err.trap(defer.TimeoutError)

View file

@ -0,0 +1,56 @@
import types
from orchstr8.testcase import IntegrationTestCase, d2f
from torba.constants import COIN
import lbryschema
lbryschema.BLOCKCHAIN_NAME = 'lbrycrd_regtest'
from lbrynet import conf as lbry_conf
from lbrynet.daemon.Daemon import Daemon
from lbrynet.wallet.manager import LbryWalletManager
class FakeAnalytics:
def send_new_channel(self):
pass
class CommandTestCase(IntegrationTestCase):
WALLET_MANAGER = LbryWalletManager
async def setUp(self):
await super().setUp()
lbry_conf.settings = None
lbry_conf.initialize_settings(load_conf_file=False)
lbry_conf.settings['data_dir'] = self.stack.wallet.data_path
lbry_conf.settings['lbryum_wallet_dir'] = self.stack.wallet.data_path
lbry_conf.settings['download_directory'] = self.stack.wallet.data_path
lbry_conf.settings['use_upnp'] = False
lbry_conf.settings['blockchain_name'] = 'lbrycrd_regtest'
lbry_conf.settings['lbryum_servers'] = [('localhost', 50001)]
lbry_conf.settings['known_dht_nodes'] = []
lbry_conf.settings.node_id = None
await d2f(self.account.ensure_address_gap())
address = (await d2f(self.account.receiving.get_usable_addresses(1)))[0]
sendtxid = await self.blockchain.send_to_address(address.decode(), 10)
await self.on_transaction_id(sendtxid)
await self.blockchain.generate(1)
await self.on_transaction_id(sendtxid)
self.daemon = Daemon(FakeAnalytics())
self.daemon.session = types.SimpleNamespace()
self.daemon.session.wallet = self.manager
class DaemonCommandsTests(CommandTestCase):
VERBOSE = True
async def test_new_channel(self):
result = await d2f(self.daemon.jsonrpc_channel_new('@bar', 1*COIN))
self.assertIn('txid', result)
await self.on_transaction_id(result['txid'])

View file

@ -13,5 +13,12 @@ def generate_certificate():
class Account(BaseAccount): class Account(BaseAccount):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(BaseAccount, self).__init__(*args, **kwargs) super(Account, self).__init__(*args, **kwargs)
self.certificates = {} self.certificates = {}
def add_certificate(self, claim_id, key):
assert claim_id not in self.certificates, 'Trying to add a duplicate certificate.'
self.certificates[claim_id] = key
def get_certificate(self, claim_id):
return self.certificates[claim_id]

View file

@ -6,6 +6,7 @@ from torba.baseledger import BaseLedger
from torba.baseheader import BaseHeaders, _ArithUint256 from torba.baseheader import BaseHeaders, _ArithUint256
from torba.util import int_to_hex, rev_hex, hash_encode from torba.util import int_to_hex, rev_hex, hash_encode
from .account import Account
from .network import Network from .network import Network
from .database import WalletDatabase from .database import WalletDatabase
from .transaction import Transaction from .transaction import Transaction
@ -88,6 +89,7 @@ class MainNetLedger(BaseLedger):
symbol = 'LBC' symbol = 'LBC'
network_name = 'mainnet' network_name = 'mainnet'
account_class = Account
database_class = WalletDatabase database_class = WalletDatabase
headers_class = Headers headers_class = Headers
network_class = Network network_class = Network

View file

@ -6,8 +6,12 @@ from torba.constants import COIN
from torba.coinselection import CoinSelector from torba.coinselection import CoinSelector
from torba.manager import WalletManager as BaseWalletManager from torba.manager import WalletManager as BaseWalletManager
from lbryschema.uri import parse_lbry_uri
from lbryschema.error import URIParseError
from .ledger import MainNetLedger from .ledger import MainNetLedger
from .account import generate_certificate
from .transaction import Transaction
class BackwardsCompatibleNetwork: class BackwardsCompatibleNetwork:
@ -101,44 +105,28 @@ class LbryWalletManager(BaseWalletManager):
return defer.succeed([]) return defer.succeed([])
def claim_name(self, name, amount, claim): def claim_name(self, name, amount, claim):
amount = int(amount * COIN) pass
@defer.inlineCallbacks
def claim_new_channel(self, channel_name, amount):
try:
parsed = parse_lbry_uri(channel_name)
if not parsed.is_channel:
raise Exception("Cannot make a new channel for a non channel name")
if parsed.path:
raise Exception("Invalid channel uri")
except (TypeError, URIParseError):
raise Exception("Invalid channel name")
if amount <= 0:
raise Exception("Invalid amount")
account = self.default_account account = self.default_account
coin = account.coin address = yield account.receiving.get_or_create_usable_address()
ledger = coin.ledger cert, key = generate_certificate()
tx = yield Transaction.claim(channel_name.encode(), cert, amount, address, [account], account)
estimators = [ yield account.ledger.broadcast(tx)
txo.get_estimator(coin) for txo in ledger.get_unspent_outputs() account.add_certificate(tx.get_claim_id(0), key)
] # TODO: release reserved tx outputs in case anything fails by this point
defer.returnValue(tx)
cost_of_output = coin.get_input_output_fee(
Output.pay_pubkey_hash(COIN, NULL_HASH)
)
selector = CoinSelector(estimators, amount, cost_of_output)
spendables = selector.select()
if not spendables:
raise ValueError('Not enough funds to cover this transaction.')
claim_address = account.get_least_used_receiving_address()
outputs = [
Output.pay_claim_name_pubkey_hash(
amount, name, claim, coin.address_to_hash160(claim_address)
)
]
spent_sum = sum(s.effective_amount for s in spendables)
if spent_sum > amount:
change_address = account.get_least_used_change_address()
change_hash160 = coin.address_to_hash160(change_address)
outputs.append(Output.pay_pubkey_hash(spent_sum - amount, change_hash160))
tx = Transaction() \
.add_inputs([s.txi for s in spendables]) \
.add_outputs(outputs) \
.sign(account)
return tx
class ReservedPoints: class ReservedPoints: