add code to get a name and its history using pagination, with a fallback to get_name_blockchain_record()

This commit is contained in:
Jude Nelson
2018-06-20 11:43:51 -04:00
parent 9138da8da8
commit 2e01578a8b

View File

@@ -1023,7 +1023,10 @@ def get_name_record(name, include_history=False, include_expired=False, include_
lastblock = None
try:
if include_history:
resp = proxy.get_name_blockchain_record(name)
resp = get_name_and_history(name, proxy=proxy)
if 'error' in resp:
# fall back to legacy path
resp = proxy.get_name_blockchain_record(name)
else:
resp = proxy.get_name_record(name)
@@ -2853,6 +2856,164 @@ def get_block_from_consensus(consensus_hash, hostport=None, proxy=None):
return resp['block_id']
def get_name_history_page(name, page, hostport=None, proxy=None):
"""
Get a page of the name's history
Returns {'status': True, 'history': ..., 'indexing': ..., 'lastblock': ...} on success
Returns {'error': ...} on error
"""
assert hostport or proxy, 'Need hostport or proxy'
if proxy is None:
proxy = connect_hostport(hostport)
hist_schema = {
'type': 'object',
'patternProperties': {
'^[0-9]+$': {
'type': 'array',
'items': {
'type': 'object',
'properties': OP_HISTORY_SCHEMA['properties'],
'required': [
'op',
'opcode',
'txid',
'vtxindex',
],
},
},
},
}
hist_resp_schema = {
'type': 'object',
'properties': {
'history': hist_schema,
},
'required': [ 'history' ],
}
resp_schema = json_response_schema(hist_resp_schema)
resp = {}
lastblock = None
indexin = None
try:
_resp = proxy.get_name_history_page(name, page)
resp = json_validate(resp_schema, _resp)
if json_is_error(resp):
return resp
lastblock = _resp['lastblock']
indexing = _resp['indexing']
except ValidationError as e:
resp = json_traceback(resp.get('error'))
return resp
except Exception as ee:
if BLOCKSTACK_DEBUG:
log.exception(ee)
log.error("Caught exception while connecting to Blockstack node: {}".format(ee))
resp = {'error': 'Failed to contact Blockstack node. Try again with `--debug`.'}
return resp
return {'status': True, 'history': resp['history'], 'lastblock': lastblock, 'indexing': indexing}
def name_history_merge(h1, h2):
"""
Given two name histories (grouped by block), merge them.
"""
ret = {}
blocks_1 = [int(b) for b in h1.keys()]
blocks_2 = [int(b) for b in h2.keys()]
# find overlapping blocks
overlap = list(set(blocks_1).intersection(set(blocks_2)))
if len(overlap) > 0:
for b in overlap:
h = h1[str(b)] + h2[str(b)]
h.sort(lambda v1, v2: -1 if v1['vtxindex'] < v2['vtxindex'] else 1)
uniq = []
last_vtxindex = None
for i in range(0, len(h)):
if h[i]['vtxindex'] != last_vtxindex:
uniq.append(h[i])
last_vtxindex = h[i]['vtxindex']
ret[str(b)] = uniq
all_blocks = list(set(blocks_1 + blocks_2))
for b in all_blocks:
if b in overlap:
continue
if b in blocks_1:
ret[str(b)] = h1[str(b)]
else:
ret[str(b)] = h2[str(b)]
return ret
def get_name_history(name, hostport=None, proxy=None):
"""
Get the full history of a name
Returns {'status': True, 'history': ...} on success, where history is grouped by block
Returns {'error': ...} on error
"""
assert hostport or proxy, 'Need hostport or proxy'
if proxy is None:
proxy = connect_hostport(hostport)
hist = {}
indexing = None
lastblock = None
for i in range(0, 10000): # this is obviously too big
resp = get_name_history_page(name, i, proxy=proxy)
if 'error' in resp:
return resp
indexing = resp['indexing']
lastblock = resp['lastblock']
if len(resp['history']) == 0:
# caught up
break
hist = name_history_merge(hist, resp['history'])
return {'status': True, 'history': hist, 'indexing': indexing, 'lastblock': lastblock}
def get_name_and_history(name, include_expired=False, include_grace=True, hostport=None, proxy=None):
"""
Get the current name record and its history
(this is a replacement for proxy.get_name_blockchain_record())
Return {'status': True, 'record': ...} on success, where .record.history is defined as {block_height: [{history}, {history}, ...], ...}
Return {'error': ...} on error
"""
assert hostport or proxy, 'Need hostport or proxy'
if proxy is None:
proxy = connect_hostport(hostport)
hist = get_name_history(name, proxy=proxy)
if 'error' in hist:
return hist
# just the name
rec = get_name_record(name, include_history=False, include_expired=include_expired, include_grace=include_grace, proxy=proxy)
if 'error' in rec:
return rec
rec['history'] = hist['history']
return {'status': True, 'record': rec, 'lastblock': hist['lastblock'], 'indexing': hist['indexing']}
def get_name_at(name, block_id, include_expired=False, hostport=None, proxy=None):
"""
Get the name as it was at a particular height.