2019-08-09 06:52:34 +02:00
|
|
|
import binascii
|
2019-12-30 23:26:37 +01:00
|
|
|
import logging
|
2019-08-09 06:52:34 +02:00
|
|
|
import hashlib
|
|
|
|
import json
|
2020-04-07 22:16:34 +02:00
|
|
|
# todo: remove sqlite3 as a dependency
|
2019-08-09 06:52:34 +02:00
|
|
|
import sqlite3
|
|
|
|
import asyncio
|
|
|
|
import aiohttp
|
|
|
|
|
2020-04-07 22:16:34 +02:00
|
|
|
from src.server.validation import is_signature_valid, get_encoded_signature
|
2019-12-30 23:26:37 +01:00
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
2019-08-09 06:52:34 +02:00
|
|
|
|
|
|
|
|
|
|
|
async def request_lbrynet(url, method, **params):
|
|
|
|
body = {'method': method, 'params': {**params}}
|
|
|
|
async with aiohttp.request('POST', url, json=body) as req:
|
|
|
|
try:
|
|
|
|
resp = await req.json()
|
|
|
|
finally:
|
|
|
|
if 'result' in resp:
|
|
|
|
return resp['result']
|
|
|
|
|
|
|
|
|
|
|
|
def get_comments_with_signatures(_conn: sqlite3.Connection) -> list:
|
|
|
|
with _conn:
|
|
|
|
curs = _conn.execute("SELECT * FROM COMMENTS_ON_CLAIMS WHERE signature IS NOT NULL")
|
|
|
|
return [dict(r) for r in curs.fetchall()]
|
|
|
|
|
|
|
|
|
|
|
|
def is_valid_signature(pubkey, channel_id, signature, signing_ts, data: str) -> bool:
|
|
|
|
try:
|
|
|
|
if pubkey:
|
|
|
|
claim_hash = binascii.unhexlify(channel_id.encode())[::-1]
|
|
|
|
injest = b''.join((signing_ts.encode(), claim_hash, data.encode()))
|
|
|
|
return is_signature_valid(
|
|
|
|
encoded_signature=get_encoded_signature(signature),
|
|
|
|
signature_digest=hashlib.sha256(injest).digest(),
|
|
|
|
public_key_bytes=binascii.unhexlify(pubkey.encode())
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
raise Exception("Pubkey is null")
|
|
|
|
except Exception as e:
|
|
|
|
print(e)
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
async def get_channel_pubkeys(comments: list):
|
|
|
|
urls = {c['channel_url'] for c in comments}
|
|
|
|
claims = await request_lbrynet('http://localhost:5279', 'resolve', urls=list(urls))
|
|
|
|
cids = {c['channel_id']: None for c in comments}
|
|
|
|
error_claims = []
|
|
|
|
for url, claim in claims.items():
|
|
|
|
if 'error' not in claim:
|
|
|
|
cids.update({
|
|
|
|
claim['claim_id']: claim['value']['public_key']
|
|
|
|
})
|
|
|
|
else:
|
|
|
|
error_claims.append({url: claim})
|
|
|
|
return cids, error_claims
|
|
|
|
|
|
|
|
|
|
|
|
def count_valid_signatures(cmts: list, chan_pubkeys: dict):
|
|
|
|
invalid_comments = []
|
|
|
|
for c in cmts:
|
|
|
|
pubkey = chan_pubkeys.get(c['channel_id'])
|
|
|
|
if not is_valid_signature(pubkey, c['channel_id'], c['signature'], c['signing_ts'], c['comment']):
|
|
|
|
invalid_comments.append(c)
|
|
|
|
return len(cmts) - len(invalid_comments), invalid_comments
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
conn = sqlite3.connect('database/default.db')
|
|
|
|
conn.row_factory = sqlite3.Row
|
|
|
|
comments = get_comments_with_signatures(conn)
|
|
|
|
loop = asyncio.get_event_loop()
|
|
|
|
chan_keys, errored = loop.run_until_complete(get_channel_pubkeys(comments))
|
|
|
|
valid_sigs, invalid_coms = count_valid_signatures(comments, chan_keys)
|
|
|
|
print(f'Total Signatures: {len(comments)}\nValid Signatures: {valid_sigs}')
|
|
|
|
print(f'Invalid Signatures: {len(comments) - valid_sigs}')
|
|
|
|
print(f'Percent Valid: {round(valid_sigs/len(comments)*100, 3)}%')
|
|
|
|
print(f'# Unresolving claims: {len(errored)}')
|
|
|
|
print(f'Num invalid comments: {len(invalid_coms)}')
|
|
|
|
print(json.dumps(errored, indent=2))
|
|
|
|
json.dump(invalid_coms, 'invalid_coms.json', indent=2)
|
|
|
|
|