def pocv10_violations(hotspot, chals): """ :param hotspot: hotspot object to analyze :param chals: list of challenges :return: """ H = Hotspots() haddr = hotspot['address'] hlat, hlng = hotspot['lat'], hotspot['lng'] transmits_w = dict(total=0, bad_rssi=0, bad_snr=0) receives_w = dict(total=0, bad_rssi=0, bad_snr=0) poc_rcv = dict(total=0, bad_rssi=0, bad_snr=0) bad_neighbors = dict() for chal in chals: transmitter = None for p in chal['path']: if p['challengee'] == haddr: for w in p['witnesses']: dist = utils.haversine_km( hlat, hlng, H.get_hotspot_by_addr(w['gateway'])['lat'], H.get_hotspot_by_addr(w['gateway'])['lng']) if dist < .3: continue rssi_lim = utils.max_rssi(dist) snr_rssi_lim = utils.snr_min_rssi(w['snr']) transmits_w['total'] += 1 if w['gateway'] not in bad_neighbors: bad_neighbors[w['gateway']] = dict(rssi=0, snr=0, ttl=0) bad_neighbors[w['gateway']]['ttl'] += 1 if w['signal'] > rssi_lim: transmits_w['bad_rssi'] += 1 bad_neighbors[w['gateway']]['rssi'] += 1 if w['signal'] < snr_rssi_lim: transmits_w['bad_snr'] += 1 bad_neighbors[w['gateway']]['snr'] += 1 if p['receipt'] and transmitter: dist = utils.haversine_km( hlat, hlng, H.get_hotspot_by_addr(transmitter)['lat'], H.get_hotspot_by_addr(transmitter)['lng'] ) rssi_lim = utils.max_rssi(dist) snr_rssi_lim = utils.snr_min_rssi(p['receipt']['snr']) poc_rcv['total'] += 1 if transmitter not in bad_neighbors: bad_neighbors[transmitter] = dict(rssi=0, snr=0, ttl=0) bad_neighbors[transmitter]['ttl'] += 1 if p['receipt']['signal'] > rssi_lim: poc_rcv['bad_rssi'] += 1 bad_neighbors[transmitter]['rssi'] += 1 if p['receipt']['signal'] < snr_rssi_lim: poc_rcv['bad_snr'] += 1 bad_neighbors[transmitter]['snr'] += 1 else: for w in p['witnesses']: if w['gateway'] != haddr: continue dist = utils.haversine_km( hlat, hlng, H.get_hotspot_by_addr(p['challengee'])['lat'], H.get_hotspot_by_addr(p['challengee'])['lng'] ) if dist < .3: continue rssi_lim = utils.max_rssi(dist) snr_rssi_lim = utils.snr_min_rssi(w['snr']) receives_w['total'] += 1 if p['challengee'] not in bad_neighbors: bad_neighbors[p['challengee']] = dict(rssi=0, snr=0, ttl=0) bad_neighbors[p['challengee']]['ttl'] += 1 if w['signal'] > rssi_lim: receives_w['bad_rssi'] += 1 bad_neighbors[p['challengee']]['rssi'] += 1 if w['signal'] < snr_rssi_lim: receives_w['bad_snr'] += 1 bad_neighbors[p['challengee']]['snr'] += 1 transmitter = p['challengee'] days, remainder = divmod(chals[0]['time'] - chals[-1]['time'], 3600 * 24) hours = int(round(remainder / 3600, 0)) print( f"analyzing {len(chals)} challenges from block {chals[0]['height']}-{chals[-1]['height']} over {days} days, {hours} hrs") print(f"PoC v10 failures for {hotspot['name']}") print(F"SUMMARY") print(f"Category | Total | bad RSSI (%) | bad SNR (%) |") print(f"-----------------------------------------------------------------") print(f"Witnesses to hotspot >300m | {transmits_w['total']:5d} | {transmits_w['bad_rssi']:4d} ({transmits_w['bad_rssi']*100/max(1, transmits_w['total']):3.0f}%) | {transmits_w['bad_snr']:4d} ({transmits_w['bad_snr']*100/max(1, transmits_w['total']):3.0f}%) |") print(f"Hotspot witnessing >300m | {receives_w['total']:5d} | {receives_w['bad_rssi']:4d} ({receives_w['bad_rssi']*100/max(1, receives_w['total']):3.0f}%) | {receives_w['bad_snr']:4d} ({receives_w['bad_snr']*100/max(1, receives_w['total']):3.0f}%) |") print(f"Hotspot PoC receipts | {poc_rcv['total']:5d} | {poc_rcv['bad_rssi']:4d} ({poc_rcv['bad_rssi']*100/max(1, poc_rcv['total']):3.0f}%) | {poc_rcv['bad_snr']:4d} ({poc_rcv['bad_snr']*100/max(1, poc_rcv['total']):3.0f}%) |") print() print() print(f'BY "BAD" NEIGHBOR') print(f"Neighboring Hotspot | dist km | heading | bad RSSI (%) | bad SNR (%) |") print(f"------------------------------+---------+---------+----------------+----------------|") hlat, hlng = hotspot['lat'], hotspot['lng'] for n in bad_neighbors: if bad_neighbors[n]['rssi'] or bad_neighbors[n]['snr']: bad_h = H.get_hotspot_by_addr(n) dist_km, heading = utils.haversine_km( hlat, hlng, bad_h['lat'], bad_h['lng'], return_heading=True ) print(f"{H.get_hotspot_by_addr(n)['name']:29} | {dist_km:5.1f} | {__heading2str__(heading):7} | {bad_neighbors[n]['rssi']:3d}/{bad_neighbors[n]['ttl']:3d} ({bad_neighbors[n]['rssi']*100/bad_neighbors[n]['ttl']:3.0f}%) | {bad_neighbors[n]['snr']:3d}/{bad_neighbors[n]['ttl']:3d} ({bad_neighbors[n]['snr']*100/bad_neighbors[n]['ttl']:3.0f}%) |")
def witness_detail(hotspot, chals, smry_only=False): haddr = hotspot['address'] H = Hotspots() H.update_reference_hspot(address=haddr) hotspot = H.get_hotspot_by_addr(haddr) vars = utils.api_call(path='vars')['data'] print() print(f"Witnesses for: {hotspot['name']}") if not smry_only: print( f"{'time':14} | {'block':7} | {'transmitting hotspot':25} | dist km | valid? | snr | rssi | RUs | inval reason" ) tx_smry = dict() total_RUs = 0 for c in chals: p = c['path'][0] for w in p['witnesses']: if w['gateway'] == haddr: transmitter = H.get_hotspot_by_addr(p['challengee']) # time, transmitter, distance, val/inval, RU, reason inval time_str = dt.datetime.fromtimestamp(w['timestamp'] / 1e9).isoformat()[5:19] time_str = time_str.replace('T', ' ') transmitter_name = transmitter['name'] reward_units = 0 valid = 'INVAL' reason = '' dist_km = utils.haversine_km(transmitter['lat'], transmitter['lng'], hotspot['lat'], hotspot['lng']) max_rssi = utils.max_rssi(dist_km) min_rssi = utils.snr_min_rssi(w['snr']) if w['is_valid']: valid = 'valid' hip15_rus = 1 if len(p['witnesses']) > vars['witness_redundancy']: hip15_rus = (vars['witness_redundancy'] - (1 - pow( vars['poc_reward_decay_rate'], len(p['witnesses']) - vars['witness_redundancy'])) ) / len(p['witnesses']) reward_units = transmitter['reward_scale'] * hip15_rus else: if dist_km < 0.3: reason = 'too close' elif w['signal'] > max_rssi: reason = f'rssi too high ({w["signal"]}dbm,{w["snr"]:.1f}snr)' elif w['signal'] < min_rssi: reason = f'snr too high (snr:{w["snr"]:.1f}, rssi: {w["signal"]}<{min_rssi})' else: reason = 'unknown' total_RUs += reward_units if not smry_only: print( f"{time_str:14} | {c['height']:7} | {transmitter_name[:25]:25} | {dist_km:6.1f} | {valid:6} | {w['snr']:5.1f} | {w['signal']:4d} | {reward_units:4.2f} | {reason}" ) if transmitter['address'] not in tx_smry: tx_smry[transmitter['address']] = dict(valid_cnt=0, invalid_cnt=0, RUs=0) tx_smry[transmitter['address']]['RUs'] += reward_units tx_smry[ transmitter['address']]['valid_cnt'] += valid == 'valid' tx_smry[ transmitter['address']]['invalid_cnt'] += valid != 'valid' if smry_only: idx_sort = [] for k in tx_smry.keys(): idx_sort.append((tx_smry[k]['RUs'], k)) idx_sort.sort() idx_sort = [x[1] for x in idx_sort[::-1]] print( f"{'transmitting hotspot':25} | scale | owner | dist km | heading | valids | invlds | RUs" ) earning_by_compass = dict() for addr in idx_sort: txer = H.get_hotspot_by_addr(addr) dist, heading = utils.haversine_km(hotspot['lat'], hotspot['lng'], txer['lat'], txer['lng'], return_heading=True) compass = utils.heading_to_compass(heading) if compass not in earning_by_compass: earning_by_compass[compass] = 0 earning_by_compass[compass] += tx_smry[addr]['RUs'] heading = round(heading / 5, 0) * 5 owner = 'same' if hotspot['owner'] != txer['owner']: owner = txer['owner'][-5:] heading_str = f"{heading:3.0f} {compass:3}" print( f"{txer['name'][:25]:25} | {txer['reward_scale']:5.2f} | {owner:5} | {dist:7.1f} | {heading_str:7} | {tx_smry[addr]['valid_cnt']:6} | {tx_smry[addr]['invalid_cnt']:6} | {tx_smry[addr]['RUs']:.2f}" ) print() print(f"Earnings by compass heading") print(f"heading | RUs | bar chart") max_compass = max(list(earning_by_compass.values())) if max_compass == 0: max_compass = 1 for h in utils.compass_headings: earnings = earning_by_compass.get(h, 0) print( f" {h:4} | {earnings:6.2f} | {'X' * round(32 * earnings / max_compass) }" ) print(f"total RUs earned: {total_RUs:.4f}") if has_mpl and show_plots: ax = plt.subplot(111, projection='polar') ax.set_theta_offset(math.pi / 2) ax.set_theta_direction(-1) width = 2 * math.pi / len(utils.compass_headings) theta = [ math.radians(utils.compass_to_heading(h)) for h in utils.compass_headings ] earnings = [ math.sqrt(earning_by_compass.get(h, 0)) for h in utils.compass_headings ] # earnings = [(earning_by_compass.get(h, 0)) for h in utils.compass_headings] ax.bar(theta, earnings, width=width, bottom=0.0) ax.set_xticks(theta) ax.set_yticks([]) ax.set_xticklabels(utils.compass_headings) plt.title(f"{hotspot['name']} RUs by compass heading") plt.show()