forked from LBRYCommunity/lbry-sdk
jsonrpc_channel_new uses new wallet
This commit is contained in:
parent
cc970c499b
commit
d5beaa0937
5 changed files with 114 additions and 71 deletions
|
@ -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)
|
||||||
|
|
56
lbrynet/tests/integration/wallet/test_commands.py
Normal file
56
lbrynet/tests/integration/wallet/test_commands.py
Normal 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'])
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue