Merge pull request #620 from lbryio/claim-caching
Add claim caching to wallet storage
This commit is contained in:
commit
89ecd2c571
4 changed files with 385 additions and 127 deletions
|
@ -16,6 +16,12 @@ at anytime.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
|
||||||
|
* Added optional `address` and `include_unconfirmed` params to `jsonrpc_wallet_balance` method
|
||||||
|
* Wait for subscriptions before announcing wallet has finished starting
|
||||||
|
* Cache claims in wallet storage for use looking claims up by id or outpoint
|
||||||
|
* Try to use cached claim info for `file_list`
|
||||||
|
* Convert wallet storage to inlinecallbacks
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
from twisted.internet import threads, reactor, defer, task
|
from twisted.internet import threads, reactor, defer, task
|
||||||
from twisted.python.failure import Failure
|
from twisted.python.failure import Failure
|
||||||
|
@ -23,10 +25,11 @@ from lbrynet.core.sqlite_helpers import rerun_if_locked
|
||||||
from lbrynet.interfaces import IRequestCreator, IQueryHandlerFactory, IQueryHandler, IWallet
|
from lbrynet.interfaces import IRequestCreator, IQueryHandlerFactory, IQueryHandler, IWallet
|
||||||
from lbrynet.core.client.ClientRequest import ClientRequest
|
from lbrynet.core.client.ClientRequest import ClientRequest
|
||||||
from lbrynet.core.Error import RequestCanceledError, InsufficientFundsError, UnknownNameError
|
from lbrynet.core.Error import RequestCanceledError, InsufficientFundsError, UnknownNameError
|
||||||
from lbrynet.db_migrator.migrate1to2 import UNSET_NOUT
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CLAIM_CACHE_TIME = 600
|
||||||
|
|
||||||
|
|
||||||
class ReservedPoints(object):
|
class ReservedPoints(object):
|
||||||
def __init__(self, identifier, amount):
|
def __init__(self, identifier, amount):
|
||||||
|
@ -60,6 +63,50 @@ class ClaimOutpoint(dict):
|
||||||
return not self.__eq__(compare)
|
return not self.__eq__(compare)
|
||||||
|
|
||||||
|
|
||||||
|
class CachedClaim(object):
|
||||||
|
def __init__(self, claim_id, claim, claim_sequence, address, height, amount, supports,
|
||||||
|
channal_name, signature_is_valid, cache_timestamp, name, txid, nout):
|
||||||
|
self.claim_id = claim_id
|
||||||
|
self.claim = claim
|
||||||
|
self.claim_sequence = claim_sequence
|
||||||
|
self.address = address
|
||||||
|
self.height = height
|
||||||
|
self.amount = amount
|
||||||
|
self.supports = [] if not supports else json.loads(supports)
|
||||||
|
self.effective_amount = self.amount + sum([x['amount'] for x in self.supports])
|
||||||
|
self.channel_name = channal_name
|
||||||
|
self.signature_is_valid = signature_is_valid
|
||||||
|
self.cache_timestamp = cache_timestamp
|
||||||
|
self.name = name
|
||||||
|
self.txid = txid
|
||||||
|
self.nout = nout
|
||||||
|
|
||||||
|
def response_dict(self, check_expires=True):
|
||||||
|
if check_expires and (time.time() - int(self.cache_timestamp)) > CLAIM_CACHE_TIME:
|
||||||
|
return
|
||||||
|
claim = {
|
||||||
|
"height": self.height,
|
||||||
|
"address": self.address,
|
||||||
|
"claim_id": self.claim_id,
|
||||||
|
"claim_sequence": self.claim_sequence,
|
||||||
|
"effective_amount": self.effective_amount,
|
||||||
|
"has_signature": self.claim.has_signature,
|
||||||
|
"name": self.name,
|
||||||
|
"hex": self.claim.serialized.encode('hex'),
|
||||||
|
"value": self.claim.claim_dict,
|
||||||
|
"txid": self.txid,
|
||||||
|
"amount": self.amount,
|
||||||
|
"decoded_claim": True,
|
||||||
|
"supports": self.supports,
|
||||||
|
"nout": self.nout
|
||||||
|
}
|
||||||
|
if self.channel_name is not None:
|
||||||
|
claim['channel_name'] = self.channel_name
|
||||||
|
if self.signature_is_valid is not None:
|
||||||
|
claim['signature_is_valid'] = bool(self.signature_is_valid)
|
||||||
|
return claim
|
||||||
|
|
||||||
|
|
||||||
class MetaDataStorage(object):
|
class MetaDataStorage(object):
|
||||||
def load(self):
|
def load(self):
|
||||||
return defer.succeed(True)
|
return defer.succeed(True)
|
||||||
|
@ -76,14 +123,38 @@ class MetaDataStorage(object):
|
||||||
def update_claimid(self, claim_id, name, claim_outpoint):
|
def update_claimid(self, claim_id, name, claim_outpoint):
|
||||||
return defer.succeed(True)
|
return defer.succeed(True)
|
||||||
|
|
||||||
def get_claimid_for_tx(self, name, claim_outpoint):
|
def get_claimid_for_tx(self, claim_outpoint):
|
||||||
return defer.succeed(True)
|
return defer.succeed(True)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_cached_claim(self, claim_id, check_expire=True):
|
||||||
|
cache_info = yield self._get_cached_claim(claim_id)
|
||||||
|
response = None
|
||||||
|
if cache_info:
|
||||||
|
cached_claim = CachedClaim(claim_id, *cache_info)
|
||||||
|
response = cached_claim.response_dict(check_expires=check_expire)
|
||||||
|
defer.returnValue(response)
|
||||||
|
|
||||||
|
def _get_cached_claim(self, claim_id):
|
||||||
|
return defer.succeed(None)
|
||||||
|
|
||||||
|
def save_claim_to_cache(self, claim_id, claim_sequence, claim, claim_address, height, amount,
|
||||||
|
supports, channel_name, signature_is_valid):
|
||||||
|
return defer.succeed(True)
|
||||||
|
|
||||||
|
def save_claim_to_uri_cache(self, uri, claim_id, certificate_id=None):
|
||||||
|
return defer.succeed(None)
|
||||||
|
|
||||||
|
def get_cached_claim_for_uri(self, uri, check_expire=True):
|
||||||
|
return defer.succeed(None)
|
||||||
|
|
||||||
|
|
||||||
class InMemoryStorage(MetaDataStorage):
|
class InMemoryStorage(MetaDataStorage):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.metadata = {}
|
self.metadata = {}
|
||||||
self.claimids = {}
|
self.claimids = {}
|
||||||
|
self.claim_dicts = {}
|
||||||
|
self.uri_cache = {}
|
||||||
MetaDataStorage.__init__(self)
|
MetaDataStorage.__init__(self)
|
||||||
|
|
||||||
def save_name_metadata(self, name, claim_outpoint, sd_hash):
|
def save_name_metadata(self, name, claim_outpoint, sd_hash):
|
||||||
|
@ -101,24 +172,59 @@ class InMemoryStorage(MetaDataStorage):
|
||||||
self.claimids[(name, claim_outpoint['txid'], claim_outpoint['nout'])] = claim_id
|
self.claimids[(name, claim_outpoint['txid'], claim_outpoint['nout'])] = claim_id
|
||||||
return defer.succeed(True)
|
return defer.succeed(True)
|
||||||
|
|
||||||
def get_claimid_for_tx(self, name, claim_outpoint):
|
def get_claimid_for_tx(self, claim_outpoint):
|
||||||
try:
|
result = None
|
||||||
return defer.succeed(
|
for k, claim_id in self.claimids.iteritems():
|
||||||
self.claimids[(name, claim_outpoint['txid'], claim_outpoint['nout'])])
|
if k[1] == claim_outpoint['txid'] and k[2] == claim_outpoint['nout']:
|
||||||
except KeyError:
|
result = claim_id
|
||||||
return defer.succeed(None)
|
break
|
||||||
|
|
||||||
|
return defer.succeed(result)
|
||||||
|
|
||||||
|
def _get_cached_claim(self, claim_id):
|
||||||
|
claim_cache = self.claim_dicts.get(claim_id, None)
|
||||||
|
claim_tx_cache = None
|
||||||
|
for k, v in self.claimids.iteritems():
|
||||||
|
if v == claim_id:
|
||||||
|
claim_tx_cache = k
|
||||||
|
break
|
||||||
|
|
||||||
|
if claim_cache and claim_tx_cache:
|
||||||
|
cached_claim_args = tuple(claim_cache) + tuple(claim_tx_cache)
|
||||||
|
return defer.succeed(cached_claim_args)
|
||||||
|
return defer.succeed(None)
|
||||||
|
|
||||||
|
def save_claim_to_cache(self, claim_id, claim_sequence, claim, claim_address, height, amount,
|
||||||
|
supports, channel_name, signature_is_valid):
|
||||||
|
self.claim_dicts[claim_id] = (claim, claim_sequence, claim_address, height, amount,
|
||||||
|
supports, channel_name, signature_is_valid, int(time.time()))
|
||||||
|
return defer.succeed(True)
|
||||||
|
|
||||||
|
def save_claim_to_uri_cache(self, uri, claim_id, certificate_id=None):
|
||||||
|
self.uri_cache[uri] = (claim_id, certificate_id)
|
||||||
|
return defer.succeed(None)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_cached_claim_for_uri(self, uri, check_expire=True):
|
||||||
|
result = self.uri_cache.get(uri, None)
|
||||||
|
response = None
|
||||||
|
if result:
|
||||||
|
claim_id, certificate_id = result
|
||||||
|
response = yield self.get_cached_claim(claim_id, check_expire)
|
||||||
|
if response and certificate_id:
|
||||||
|
certificate = yield self.get_cached_claim(certificate_id, check_expire)
|
||||||
|
response['certificate'] = certificate['claim']
|
||||||
|
defer.returnValue(response)
|
||||||
|
|
||||||
|
|
||||||
class SqliteStorage(MetaDataStorage):
|
class SqliteStorage(MetaDataStorage):
|
||||||
def __init__(self, db_dir):
|
def __init__(self, db_dir):
|
||||||
self.db_dir = db_dir
|
self.db_dir = db_dir
|
||||||
self.db = None
|
self.db = adbapi.ConnectionPool('sqlite3', os.path.join(self.db_dir, "blockchainname.db"),
|
||||||
|
check_same_thread=False)
|
||||||
MetaDataStorage.__init__(self)
|
MetaDataStorage.__init__(self)
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
self.db = adbapi.ConnectionPool('sqlite3', os.path.join(self.db_dir, "blockchainname.db"),
|
|
||||||
check_same_thread=False)
|
|
||||||
|
|
||||||
def create_tables(transaction):
|
def create_tables(transaction):
|
||||||
transaction.execute("create table if not exists name_metadata (" +
|
transaction.execute("create table if not exists name_metadata (" +
|
||||||
" name text, " +
|
" name text, " +
|
||||||
|
@ -130,54 +236,162 @@ class SqliteStorage(MetaDataStorage):
|
||||||
" name text, " +
|
" name text, " +
|
||||||
" txid text, " +
|
" txid text, " +
|
||||||
" n integer)")
|
" n integer)")
|
||||||
|
transaction.execute("CREATE TABLE IF NOT EXISTS claim_cache (" +
|
||||||
|
" row_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
||||||
|
" claim_id TEXT UNIQUE NOT NULL, " +
|
||||||
|
" claim_sequence INTEGER, " +
|
||||||
|
" claim_address TEXT NOT NULL, " +
|
||||||
|
" height INTEGER NOT NULL, " +
|
||||||
|
" amount INTEGER NOT NULL, " +
|
||||||
|
" supports TEXT, " +
|
||||||
|
" claim_pb TEXT, " +
|
||||||
|
" channel_name TEXT, " +
|
||||||
|
" signature_is_valid BOOL, " +
|
||||||
|
" last_modified TEXT)")
|
||||||
|
transaction.execute("CREATE TABLE IF NOT EXISTS uri_cache (" +
|
||||||
|
" row_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
||||||
|
" uri TEXT UNIQUE NOT NULL, " +
|
||||||
|
" cache_row INTEGER, " +
|
||||||
|
" certificate_row INTEGER, " +
|
||||||
|
" last_modified TEXT)")
|
||||||
|
|
||||||
return self.db.runInteraction(create_tables)
|
return self.db.runInteraction(create_tables)
|
||||||
|
|
||||||
|
@rerun_if_locked
|
||||||
|
@defer.inlineCallbacks
|
||||||
def clean_bad_records(self):
|
def clean_bad_records(self):
|
||||||
d = self.db.runQuery("delete from name_metadata where length(txid) > 64 or txid is null")
|
yield self.db.runQuery("DELETE FROM name_metadata WHERE LENGTH(txid) > 64 OR txid IS NULL")
|
||||||
return d
|
defer.returnValue(None)
|
||||||
|
|
||||||
def save_name_metadata(self, name, claim_outpoint, sd_hash):
|
|
||||||
d = self.db.runQuery(
|
|
||||||
"delete from name_metadata where name=? and txid=? and n=? and sd_hash=?",
|
|
||||||
(name, claim_outpoint['txid'], claim_outpoint['nout'], sd_hash))
|
|
||||||
d.addCallback(
|
|
||||||
lambda _: self.db.runQuery(
|
|
||||||
"delete from name_metadata where name=? and txid=? and n=? and sd_hash=?",
|
|
||||||
(name, claim_outpoint['txid'], UNSET_NOUT, sd_hash)))
|
|
||||||
d.addCallback(
|
|
||||||
lambda _: self.db.runQuery(
|
|
||||||
"insert into name_metadata values (?, ?, ?, ?)",
|
|
||||||
(name, claim_outpoint['txid'], claim_outpoint['nout'], sd_hash)))
|
|
||||||
return d
|
|
||||||
|
|
||||||
@rerun_if_locked
|
@rerun_if_locked
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def save_name_metadata(self, name, claim_outpoint, sd_hash):
|
||||||
|
# TODO: refactor the 'name_metadata' and 'claim_ids' tables to not be terrible
|
||||||
|
txid, nout = claim_outpoint['txid'], claim_outpoint['nout']
|
||||||
|
record_exists = yield self.db.runQuery("SELECT COUNT(*) FROM name_metadata "
|
||||||
|
"WHERE name=? AND txid=? AND n=?",
|
||||||
|
(name, txid, nout))
|
||||||
|
if not record_exists:
|
||||||
|
yield self.db.runOperation("INSERT INTO name_metadata VALUES (?, ?, ?, ?)",
|
||||||
|
(name, txid, nout, sd_hash))
|
||||||
|
defer.returnValue(None)
|
||||||
|
|
||||||
|
@rerun_if_locked
|
||||||
|
@defer.inlineCallbacks
|
||||||
def get_claim_metadata_for_sd_hash(self, sd_hash):
|
def get_claim_metadata_for_sd_hash(self, sd_hash):
|
||||||
d = self.db.runQuery("select name, txid, n from name_metadata where sd_hash=?", (sd_hash,))
|
result = yield self.db.runQuery("SELECT name, txid, n FROM name_metadata WHERE sd_hash=?",
|
||||||
d.addCallback(lambda r: r[0] if r else None)
|
(sd_hash, ))
|
||||||
return d
|
response = None
|
||||||
|
if result:
|
||||||
|
response = result[0]
|
||||||
|
defer.returnValue(response)
|
||||||
|
|
||||||
|
@rerun_if_locked
|
||||||
|
@defer.inlineCallbacks
|
||||||
def update_claimid(self, claim_id, name, claim_outpoint):
|
def update_claimid(self, claim_id, name, claim_outpoint):
|
||||||
d = self.db.runQuery(
|
txid, nout = claim_outpoint['txid'], claim_outpoint['nout']
|
||||||
"delete from claim_ids where claimId=? and name=? and txid=? and n=?",
|
yield self.db.runOperation("INSERT OR IGNORE INTO claim_ids VALUES (?, ?, ?, ?)",
|
||||||
(claim_id, name, claim_outpoint['txid'], claim_outpoint['nout']))
|
(claim_id, name, txid, nout))
|
||||||
d.addCallback(
|
defer.returnValue(claim_id)
|
||||||
lambda _: self.db.runQuery(
|
|
||||||
"delete from claim_ids where claimId=? and name=? and txid=? and n=?",
|
|
||||||
(claim_id, name, claim_outpoint['txid'], UNSET_NOUT)))
|
|
||||||
d.addCallback(
|
|
||||||
lambda r: self.db.runQuery(
|
|
||||||
"insert into claim_ids values (?, ?, ?, ?)",
|
|
||||||
(claim_id, name, claim_outpoint['txid'], claim_outpoint['nout'])))
|
|
||||||
d.addCallback(lambda _: claim_id)
|
|
||||||
return d
|
|
||||||
|
|
||||||
def get_claimid_for_tx(self, name, claim_outpoint):
|
@rerun_if_locked
|
||||||
d = self.db.runQuery(
|
@defer.inlineCallbacks
|
||||||
"select claimId from claim_ids where name=? and txid=? and n=?",
|
def get_claimid_for_tx(self, claim_outpoint):
|
||||||
(name, claim_outpoint['txid'], claim_outpoint['nout']))
|
result = yield self.db.runQuery("SELECT claimId FROM claim_ids "
|
||||||
d.addCallback(lambda r: r[0][0] if r else None)
|
"WHERE txid=? AND n=?",
|
||||||
return d
|
(claim_outpoint['txid'], claim_outpoint['nout']))
|
||||||
|
response = None
|
||||||
|
if result:
|
||||||
|
response = result[0][0]
|
||||||
|
defer.returnValue(response)
|
||||||
|
|
||||||
|
@rerun_if_locked
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def _get_cached_claim(self, claim_id, check_expire=True):
|
||||||
|
r = yield self.db.runQuery("SELECT * FROM claim_cache WHERE claim_id=?", (claim_id, ))
|
||||||
|
claim_tx_info = yield self.db.runQuery("SELECT name, txid, n FROM claim_ids "
|
||||||
|
"WHERE claimId=?", (claim_id, ))
|
||||||
|
response = None
|
||||||
|
if r and claim_tx_info:
|
||||||
|
_, _, seq, claim_address, height, amount, supports, raw, chan_name, valid, ts = r[0]
|
||||||
|
last_modified = int(ts)
|
||||||
|
name, txid, nout = claim_tx_info[0]
|
||||||
|
claim = ClaimDict.deserialize(raw.decode('hex'))
|
||||||
|
response = (claim, seq, claim_address, height, amount, supports,
|
||||||
|
chan_name, valid, last_modified, name, txid, nout)
|
||||||
|
defer.returnValue(response)
|
||||||
|
|
||||||
|
@rerun_if_locked
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def save_claim_to_cache(self, claim_id, claim_sequence, claim, claim_address, height, amount,
|
||||||
|
supports, channel_name, signature_is_valid):
|
||||||
|
serialized = claim.serialized.encode("hex")
|
||||||
|
supports = json.dumps([] or supports)
|
||||||
|
now = str(int(time.time()))
|
||||||
|
|
||||||
|
yield self.db.runOperation("INSERT OR REPLACE INTO claim_cache(claim_sequence, "
|
||||||
|
" claim_id, claim_address, height, "
|
||||||
|
" amount, supports, claim_pb, "
|
||||||
|
" channel_name, signature_is_valid, "
|
||||||
|
" last_modified)"
|
||||||
|
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
|
(claim_sequence, claim_id, claim_address, height, amount,
|
||||||
|
supports, serialized, channel_name, signature_is_valid, now))
|
||||||
|
defer.returnValue(None)
|
||||||
|
|
||||||
|
@rerun_if_locked
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def save_claim_to_uri_cache(self, uri, claim_id, certificate_id=None):
|
||||||
|
result = yield self.db.runQuery("SELECT row_id, last_modified FROM claim_cache "
|
||||||
|
"WHERE claim_id=?", (claim_id, ))
|
||||||
|
certificate_result = None
|
||||||
|
certificate_row = None
|
||||||
|
|
||||||
|
if certificate_id:
|
||||||
|
certificate_result = yield self.db.runQuery("SELECT row_id FROM claim_cache "
|
||||||
|
"WHERE claim_id=?", (certificate_id, ))
|
||||||
|
if certificate_id is not None and certificate_result is None:
|
||||||
|
log.warning("Certificate is not in cache")
|
||||||
|
elif certificate_result:
|
||||||
|
certificate_row = certificate_result[0][0]
|
||||||
|
|
||||||
|
if result:
|
||||||
|
cache_row, ts = result[0]
|
||||||
|
yield self.db.runOperation("INSERT OR REPLACE INTO uri_cache(uri, cache_row, "
|
||||||
|
" certificate_row, last_modified) "
|
||||||
|
"VALUES (?, ?, ?, ?)",
|
||||||
|
(uri, cache_row, certificate_row,
|
||||||
|
str(int(time.time()))))
|
||||||
|
else:
|
||||||
|
log.warning("Claim is not in cache")
|
||||||
|
defer.returnValue(None)
|
||||||
|
|
||||||
|
@rerun_if_locked
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_cached_claim_for_uri(self, uri, check_expire=True):
|
||||||
|
result = yield self.db.runQuery("SELECT "
|
||||||
|
"claim.claim_id, cert.claim_id, uri_cache.last_modified "
|
||||||
|
"FROM uri_cache "
|
||||||
|
"INNER JOIN claim_cache as claim "
|
||||||
|
"ON uri_cache.cache_row=claim.row_id "
|
||||||
|
"LEFT OUTER JOIN claim_cache as cert "
|
||||||
|
"ON uri_cache.certificate_row=cert.row_id "
|
||||||
|
"WHERE uri_cache.uri=?", (uri, ))
|
||||||
|
response = None
|
||||||
|
if result:
|
||||||
|
claim_id, certificate_id, last_modified = result[0]
|
||||||
|
last_modified = int(last_modified)
|
||||||
|
if check_expire and time.time() - last_modified > CLAIM_CACHE_TIME:
|
||||||
|
defer.returnValue(None)
|
||||||
|
claim = yield self.get_cached_claim(claim_id)
|
||||||
|
if claim:
|
||||||
|
response = {
|
||||||
|
"claim": claim
|
||||||
|
}
|
||||||
|
if response and certificate_id is not None:
|
||||||
|
certificate = yield self.get_cached_claim(certificate_id)
|
||||||
|
response['certificate'] = certificate
|
||||||
|
defer.returnValue(response)
|
||||||
|
|
||||||
|
|
||||||
class Wallet(object):
|
class Wallet(object):
|
||||||
|
@ -230,9 +444,6 @@ class Wallet(object):
|
||||||
def _update_claimid(self, claim_id, name, claim_outpoint):
|
def _update_claimid(self, claim_id, name, claim_outpoint):
|
||||||
return self._storage.update_claimid(claim_id, name, claim_outpoint)
|
return self._storage.update_claimid(claim_id, name, claim_outpoint)
|
||||||
|
|
||||||
def _get_claimid_for_tx(self, name, claim_outpoint):
|
|
||||||
return self._storage.get_claimid_for_tx(name, claim_outpoint)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def log_stop_error(err):
|
def log_stop_error(err):
|
||||||
log.error("An error occurred stopping the wallet: %s", err.getTraceback())
|
log.error("An error occurred stopping the wallet: %s", err.getTraceback())
|
||||||
|
@ -452,42 +663,30 @@ class Wallet(object):
|
||||||
######
|
######
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def get_claim(self, claim_id):
|
def get_cached_claim(self, claim_id, check_expire=True):
|
||||||
claim = yield self._get_claim_by_claimid(claim_id)
|
results = yield self._storage.get_cached_claim(claim_id, check_expire)
|
||||||
if not claim:
|
defer.returnValue(results)
|
||||||
log.warning("Claim does not exist: %s", claim_id)
|
|
||||||
defer.returnValue(None)
|
|
||||||
try:
|
|
||||||
decoded = smart_decode(claim['value'])
|
|
||||||
claim['value'] = decoded.claim_dict
|
|
||||||
claim['hex'] = decoded.serialized.encode('hex')
|
|
||||||
except DecodeError:
|
|
||||||
claim['hex'] = claim['value']
|
|
||||||
claim['value'] = None
|
|
||||||
claim['error'] = "Failed to decode"
|
|
||||||
log.warning("Failed to decode claim value for lbry://%s#%s", claim['name'],
|
|
||||||
claim['claim_id'])
|
|
||||||
defer.returnValue(claim)
|
|
||||||
|
|
||||||
def get_claimid(self, name, txid, nout):
|
@defer.inlineCallbacks
|
||||||
def _get_id_for_return(claim_id):
|
def get_claim(self, claim_id, check_expire=True):
|
||||||
if claim_id:
|
cached_claim = yield self.get_cached_claim(claim_id, check_expire)
|
||||||
return defer.succeed(claim_id)
|
if cached_claim:
|
||||||
|
result = cached_claim
|
||||||
|
else:
|
||||||
|
log.debug("Refreshing cached claim: %s", claim_id)
|
||||||
|
claim = yield self._get_claim_by_claimid(claim_id)
|
||||||
|
result = None
|
||||||
|
if claim:
|
||||||
|
result = yield self._handle_claim_result(claim)
|
||||||
else:
|
else:
|
||||||
d = self.get_claims_from_tx(txid)
|
log.warning("Claim does not exist: %s", claim_id)
|
||||||
d.addCallback(
|
defer.returnValue(result)
|
||||||
lambda claims: next(
|
|
||||||
c for c in claims if c['name'] == name and
|
|
||||||
c['nout'] == claim_outpoint['nout']))
|
|
||||||
d.addCallback(
|
|
||||||
lambda claim: self._update_claimid(
|
|
||||||
claim['claim_id'], name, ClaimOutpoint(txid, claim['nout'])))
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_claimid(self, txid, nout):
|
||||||
claim_outpoint = ClaimOutpoint(txid, nout)
|
claim_outpoint = ClaimOutpoint(txid, nout)
|
||||||
d = self._get_claimid_for_tx(name, claim_outpoint)
|
claim_id = yield self._storage.get_claimid_for_tx(claim_outpoint)
|
||||||
d.addCallback(_get_id_for_return)
|
defer.returnValue(claim_id)
|
||||||
return d
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def get_my_claim(self, name):
|
def get_my_claim(self, name):
|
||||||
|
@ -501,19 +700,19 @@ class Wallet(object):
|
||||||
defer.returnValue(my_claim)
|
defer.returnValue(my_claim)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def get_claim_info(self, name, txid=None, nout=None, claim_id=None):
|
def get_claim_info(self, name, txid=None, nout=None, claim_id=None, check_expire=True):
|
||||||
if claim_id is not None:
|
if claim_id is not None:
|
||||||
results = yield self.get_claim(claim_id)
|
results = yield self.get_claim(claim_id, check_expire)
|
||||||
if results['name'] != name:
|
if results['name'] != name:
|
||||||
raise Exception("Name does not match claim referenced by id")
|
raise Exception("Name does not match claim referenced by id")
|
||||||
elif txid is None or nout is None:
|
elif txid is None or nout is None:
|
||||||
results = yield self.get_claim_by_name(name)
|
results = yield self.get_claim_by_name(name)
|
||||||
else:
|
else:
|
||||||
results = yield self.get_claim_by_outpoint(ClaimOutpoint(txid, nout))
|
results = yield self.get_claim_by_outpoint(ClaimOutpoint(txid, nout), check_expire)
|
||||||
defer.returnValue(results)
|
defer.returnValue(results)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _handle_claim_result(self, results):
|
def _handle_claim_result(self, results, update_caches=True):
|
||||||
if not results:
|
if not results:
|
||||||
raise UnknownNameError("No results to return")
|
raise UnknownNameError("No results to return")
|
||||||
|
|
||||||
|
@ -523,16 +722,35 @@ class Wallet(object):
|
||||||
else:
|
else:
|
||||||
raise Exception(results['error'])
|
raise Exception(results['error'])
|
||||||
|
|
||||||
|
if 'certificate' in results:
|
||||||
|
try:
|
||||||
|
decoded = smart_decode(results['certificate']['value'])
|
||||||
|
claim_dict = decoded.claim_dict
|
||||||
|
outpoint = ClaimOutpoint(results['certificate']['txid'],
|
||||||
|
results['certificate']['nout'])
|
||||||
|
name = results['certificate']['name']
|
||||||
|
results['certificate']['value'] = claim_dict
|
||||||
|
results['certificate']['hex'] = decoded.serialized.encode('hex')
|
||||||
|
if update_caches:
|
||||||
|
yield self._save_name_metadata(name, outpoint, decoded.source_hash)
|
||||||
|
yield self._update_claimid(results['certificate']['claim_id'], name, outpoint)
|
||||||
|
yield self._storage.save_claim_to_cache(results['certificate']['claim_id'],
|
||||||
|
results['certificate']['claim_sequence'],
|
||||||
|
decoded, results['certificate']['address'],
|
||||||
|
results['certificate']['height'],
|
||||||
|
results['certificate']['amount'],
|
||||||
|
results['certificate']['supports'],
|
||||||
|
None,
|
||||||
|
None)
|
||||||
|
except DecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
if 'claim' in results:
|
if 'claim' in results:
|
||||||
claim = results['claim']
|
claim = results['claim']
|
||||||
if 'has_signature' in claim and claim['has_signature']:
|
if 'has_signature' in claim and claim['has_signature']:
|
||||||
if not claim['signature_is_valid']:
|
if not claim['signature_is_valid']:
|
||||||
log.warning("lbry://%s#%s has an invalid signature",
|
log.warning("lbry://%s#%s has an invalid signature",
|
||||||
claim['name'], claim['claim_id'])
|
claim['name'], claim['claim_id'])
|
||||||
decoded = ClaimDict.load_dict(claim['value'])
|
|
||||||
claim_dict = decoded.claim_dict
|
|
||||||
claim['value'] = claim_dict
|
|
||||||
defer.returnValue(claim)
|
|
||||||
try:
|
try:
|
||||||
decoded = smart_decode(claim['value'])
|
decoded = smart_decode(claim['value'])
|
||||||
claim_dict = decoded.claim_dict
|
claim_dict = decoded.claim_dict
|
||||||
|
@ -540,24 +758,28 @@ class Wallet(object):
|
||||||
name = claim['name']
|
name = claim['name']
|
||||||
claim['value'] = claim_dict
|
claim['value'] = claim_dict
|
||||||
claim['hex'] = decoded.serialized.encode('hex')
|
claim['hex'] = decoded.serialized.encode('hex')
|
||||||
yield self._save_name_metadata(name, outpoint, decoded.source_hash)
|
if update_caches:
|
||||||
yield self._update_claimid(claim['claim_id'], name, outpoint)
|
yield self._save_name_metadata(name, outpoint, decoded.source_hash)
|
||||||
|
yield self._update_claimid(claim['claim_id'], name, outpoint)
|
||||||
|
yield self._storage.save_claim_to_cache(claim['claim_id'],
|
||||||
|
claim['claim_sequence'],
|
||||||
|
decoded, claim['address'],
|
||||||
|
claim['height'],
|
||||||
|
claim['amount'], claim['supports'],
|
||||||
|
claim.get('channel_name', None),
|
||||||
|
claim.get('signature_is_valid', None))
|
||||||
except DecodeError:
|
except DecodeError:
|
||||||
claim['hex'] = claim['value']
|
claim['hex'] = claim['value']
|
||||||
claim['value'] = None
|
claim['value'] = None
|
||||||
claim['error'] = "Failed to decode value"
|
claim['error'] = "Failed to decode value"
|
||||||
|
|
||||||
results = claim
|
results['claim'] = claim
|
||||||
|
|
||||||
elif 'value' in results:
|
elif 'value' in results:
|
||||||
if 'has_signature' in results and results['has_signature']:
|
if 'has_signature' in results and results['has_signature']:
|
||||||
if not results['signature_is_valid']:
|
if not results['signature_is_valid']:
|
||||||
log.warning("lbry://%s#%s has an invalid signature",
|
log.warning("lbry://%s#%s has an invalid signature",
|
||||||
results['name'], results['claim_id'])
|
results['name'], results['claim_id'])
|
||||||
decoded = ClaimDict.load_dict(results['value'])
|
|
||||||
claim_dict = decoded.claim_dict
|
|
||||||
results['value'] = claim_dict
|
|
||||||
defer.returnValue(results)
|
|
||||||
try:
|
try:
|
||||||
decoded = ClaimDict.load_dict(results['value'])
|
decoded = ClaimDict.load_dict(results['value'])
|
||||||
claim_dict = decoded.claim_dict
|
claim_dict = decoded.claim_dict
|
||||||
|
@ -565,8 +787,18 @@ class Wallet(object):
|
||||||
claim_err = None
|
claim_err = None
|
||||||
outpoint = ClaimOutpoint(results['txid'], results['nout'])
|
outpoint = ClaimOutpoint(results['txid'], results['nout'])
|
||||||
name = results['name']
|
name = results['name']
|
||||||
yield self._save_name_metadata(name, outpoint, decoded.source_hash)
|
if update_caches:
|
||||||
yield self._update_claimid(results['claim_id'], name, outpoint)
|
yield self._save_name_metadata(name, outpoint, decoded.source_hash)
|
||||||
|
yield self._update_claimid(results['claim_id'], name, outpoint)
|
||||||
|
yield self._storage.save_claim_to_cache(results['claim_id'],
|
||||||
|
results.get('claim_sequence', None),
|
||||||
|
decoded, results['address'],
|
||||||
|
results['height'], results['amount'],
|
||||||
|
results.get('supports', '[]'),
|
||||||
|
results.get('channel_name', None),
|
||||||
|
results.get('signature_is_valid',
|
||||||
|
None))
|
||||||
|
|
||||||
except DecodeError:
|
except DecodeError:
|
||||||
claim_dict = None
|
claim_dict = None
|
||||||
claim_hex = results['value']
|
claim_hex = results['value']
|
||||||
|
@ -576,33 +808,46 @@ class Wallet(object):
|
||||||
results['hex'] = claim_hex
|
results['hex'] = claim_hex
|
||||||
results['value'] = claim_dict
|
results['value'] = claim_dict
|
||||||
|
|
||||||
log.info("get claim info lbry://%s#%s", results['name'], results['claim_id'])
|
|
||||||
defer.returnValue(results)
|
defer.returnValue(results)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def resolve_uri(self, uri):
|
def resolve_uri(self, uri, check_cache=True):
|
||||||
resolve_results = yield self._get_value_for_uri(uri)
|
cached_claim = None
|
||||||
if 'claim' in resolve_results:
|
if check_cache:
|
||||||
formatted = yield self._handle_claim_result(resolve_results)
|
cached_claim = yield self._storage.get_cached_claim_for_uri(uri, check_cache)
|
||||||
resolve_results['claim'] = formatted
|
if cached_claim:
|
||||||
result = resolve_results
|
log.debug("Using cached results for %s", uri)
|
||||||
elif 'claims_in_channel' in resolve_results:
|
resolve_results = cached_claim
|
||||||
claims_for_return = []
|
|
||||||
for claim in resolve_results['claims_in_channel']:
|
|
||||||
formatted = yield self._handle_claim_result(claim)
|
|
||||||
claims_for_return.append(formatted)
|
|
||||||
resolve_results['claims_in_channel'] = claims_for_return
|
|
||||||
result = resolve_results
|
|
||||||
elif 'error' in resolve_results:
|
|
||||||
raise Exception(resolve_results['error'])
|
|
||||||
else:
|
else:
|
||||||
result = None
|
log.info("Resolving %s", uri)
|
||||||
|
resolve_results = yield self._get_value_for_uri(uri)
|
||||||
|
|
||||||
|
claim_id = None
|
||||||
|
if resolve_results and 'claim' in resolve_results:
|
||||||
|
claim_id = resolve_results['claim']['claim_id']
|
||||||
|
certificate_id = None
|
||||||
|
if resolve_results and 'certificate' in resolve_results:
|
||||||
|
certificate_id = resolve_results['certificate']['claim_id']
|
||||||
|
|
||||||
|
result = yield self._handle_claim_result(resolve_results, cached_claim is None)
|
||||||
|
if claim_id:
|
||||||
|
yield self._storage.save_claim_to_uri_cache(uri, claim_id, certificate_id)
|
||||||
|
|
||||||
defer.returnValue(result)
|
defer.returnValue(result)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def get_claim_by_outpoint(self, claim_outpoint):
|
def get_claim_by_outpoint(self, claim_outpoint, check_expire=True):
|
||||||
claim = yield self._get_claim_by_outpoint(claim_outpoint['txid'], claim_outpoint['nout'])
|
claim_id = yield self._storage.get_claimid_for_tx(claim_outpoint)
|
||||||
result = yield self._handle_claim_result(claim)
|
txid, nout = claim_outpoint['txid'], claim_outpoint['nout']
|
||||||
|
if claim_id:
|
||||||
|
cached_claim = yield self._storage.get_cached_claim(claim_id, check_expire)
|
||||||
|
else:
|
||||||
|
cached_claim = None
|
||||||
|
if not cached_claim:
|
||||||
|
claim = yield self._get_claim_by_outpoint(txid, nout)
|
||||||
|
result = yield self._handle_claim_result(claim)
|
||||||
|
else:
|
||||||
|
result = cached_claim
|
||||||
defer.returnValue(result)
|
defer.returnValue(result)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
|
@ -1124,7 +1369,7 @@ class LBRYumWallet(Wallet):
|
||||||
def _do_send_many(self, payments_to_send):
|
def _do_send_many(self, payments_to_send):
|
||||||
def broadcast_send_many(paytomany_out):
|
def broadcast_send_many(paytomany_out):
|
||||||
if 'hex' not in paytomany_out:
|
if 'hex' not in paytomany_out:
|
||||||
raise Exception('Unepxected paytomany output:{}'.format(paytomany_out))
|
raise Exception('Unexpected paytomany output:{}'.format(paytomany_out))
|
||||||
return self._broadcast_transaction(paytomany_out['hex'])
|
return self._broadcast_transaction(paytomany_out['hex'])
|
||||||
|
|
||||||
log.debug("Doing send many. payments to send: %s", str(payments_to_send))
|
log.debug("Doing send many. payments to send: %s", str(payments_to_send))
|
||||||
|
|
|
@ -123,7 +123,7 @@ class ManagedEncryptedFileDownloader(EncryptedFileSaver):
|
||||||
self.outpoint = ClaimOutpoint(self.txid, self.nout)
|
self.outpoint = ClaimOutpoint(self.txid, self.nout)
|
||||||
else:
|
else:
|
||||||
raise NoSuchSDHash(self.sd_hash)
|
raise NoSuchSDHash(self.sd_hash)
|
||||||
self.claim_id = yield self.wallet.get_claimid(self.name, self.txid, self.nout)
|
self.claim_id = yield self.wallet.get_claimid(self.txid, self.nout)
|
||||||
defer.returnValue(None)
|
defer.returnValue(None)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
|
|
|
@ -878,7 +878,7 @@ class Daemon(AuthJSONRPCServer):
|
||||||
size = None
|
size = None
|
||||||
message = None
|
message = None
|
||||||
|
|
||||||
claim = yield self.session.wallet.get_claim(lbry_file.claim_id)
|
claim = yield self.session.wallet.get_claim(lbry_file.claim_id, check_expire=False)
|
||||||
|
|
||||||
if claim and 'value' in claim:
|
if claim and 'value' in claim:
|
||||||
metadata = claim['value']
|
metadata = claim['value']
|
||||||
|
@ -915,7 +915,6 @@ class Daemon(AuthJSONRPCServer):
|
||||||
'suggested_file_name': lbry_file.suggested_file_name,
|
'suggested_file_name': lbry_file.suggested_file_name,
|
||||||
'sd_hash': lbry_file.sd_hash,
|
'sd_hash': lbry_file.sd_hash,
|
||||||
'name': lbry_file.name,
|
'name': lbry_file.name,
|
||||||
'channel_name': channel_name,
|
|
||||||
'outpoint': outpoint,
|
'outpoint': outpoint,
|
||||||
'claim_id': lbry_file.claim_id,
|
'claim_id': lbry_file.claim_id,
|
||||||
'download_path': full_path,
|
'download_path': full_path,
|
||||||
|
@ -926,6 +925,8 @@ class Daemon(AuthJSONRPCServer):
|
||||||
'message': message,
|
'message': message,
|
||||||
'metadata': metadata
|
'metadata': metadata
|
||||||
}
|
}
|
||||||
|
if channel_name is not None:
|
||||||
|
result['channel_name'] = channel_name
|
||||||
if has_signature is not None:
|
if has_signature is not None:
|
||||||
result['has_signature'] = has_signature
|
result['has_signature'] = has_signature
|
||||||
if signature_is_valid is not None:
|
if signature_is_valid is not None:
|
||||||
|
@ -1394,7 +1395,13 @@ class Daemon(AuthJSONRPCServer):
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
claim_results = yield self.session.wallet.get_claim_info(name, txid, nout, claim_id)
|
if claim_id:
|
||||||
|
claim_results = yield self.session.wallet.get_claim(claim_id)
|
||||||
|
elif txid and nout is not None:
|
||||||
|
outpoint = ClaimOutpoint(txid, nout)
|
||||||
|
claim_results = yield self.session.wallet.get_claim_by_outpoint(outpoint)
|
||||||
|
else:
|
||||||
|
claim_results = yield self.session.wallet.get_claim_by_name(name)
|
||||||
result = format_json_out_amount_as_float(claim_results)
|
result = format_json_out_amount_as_float(claim_results)
|
||||||
except (TypeError, UnknownNameError):
|
except (TypeError, UnknownNameError):
|
||||||
result = False
|
result = False
|
||||||
|
|
Loading…
Reference in a new issue